Commit 148a0a91 authored by Yann Diorcet's avatar Yann Diorcet
Browse files

Add XML2LPC lib/tool

parent 4c346156
......@@ -4,7 +4,7 @@
ACLOCAL_AMFLAGS = -I m4 $(ACLOCAL_MACOS_FLAGS)
SUBDIRS = build m4 pixmaps po @ORTP_DIR@ @MS2_DIR@ \
coreapi console gtk share scripts
coreapi console gtk share scripts tools
......
......@@ -140,6 +140,33 @@ AC_ARG_ENABLE(console_ui,
*) AC_MSG_ERROR(bad value ${enableval} for --enable-console_ui) ;;
esac],[console_ui=true])
dnl conditionnal build of tools.
AC_ARG_ENABLE(tools,
[AS_HELP_STRING([--enable-tools=[yes/no]], [Turn on or off compilation of console interface (default=yes)])],
[case "${enableval}" in
yes) build_tools=true ;;
no) build_tools=false ;;
*) AC_MSG_ERROR(bad value ${enableval} for --enable-tools) ;;
esac],[build_tools=check])
dnl check libxml2 (needed for tools)
if test "$build_tools" != "false" ; then
PKG_CHECK_MODULES(LIBXML2, [libxml-2.0],[],
[
if test "$build_tools" = "true" ; then
AC_MSG_ERROR([Could not found libxml2, tools cannot be compiled.])
else
build_tools=false
fi
])
fi
AM_CONDITIONAL(BUILD_TOOLS, test x$build_tools != xfalse)
if test "$build_tools" != "false" ; then
build_tools=true
AC_DEFINE(BUILD_TOOLS, 1, [Define if tools enabled] )
fi
dnl conditionnal build of gtk interface.
AC_ARG_ENABLE(gtk_ui,
[AS_HELP_STRING([--enable-gtk_ui=[yes/no]], [Turn on or off compilation of gtk interface (default=yes)])],
......@@ -418,8 +445,10 @@ AC_ARG_ENABLE(portaudio,
dnl build console if required
AM_CONDITIONAL(BUILD_CONSOLE, test x$console_ui = xtrue)
dnl special things for arm-linux cross compilation toolchain
AM_CONDITIONAL(ARMBUILD, test x$use_arm_toolchain = xyes)
dnl compilation of gtk user interface
AM_CONDITIONAL(BUILD_GTK_UI, [test x$gtk_ui = xtrue ] )
AM_CONDITIONAL(BUILD_WIN32, test x$mingw_found = xyes )
......@@ -642,9 +671,11 @@ share/fr/Makefile
share/it/Makefile
share/ja/Makefile
share/cs/Makefile
share/xml/Makefile
share/linphone.pc
share/linphone.desktop
scripts/Makefile
tools/Makefile
linphone.spec
linphone.iss
])
......@@ -657,6 +688,7 @@ printf "* Video support\t\t\t%s\n" $video
printf "* GTK interface\t\t\t%s\n" $gtk_ui
printf "* Account assistant\t\t%s\n" $build_wizard
printf "* Console interface\t\t%s\n" $console_ui
printf "* Tools\t\t\t\t%s\n" $build_tools
printf "* zRTP encryption (GPLv3)\t%s\n" $zrtp
if test "$enable_tunnel" = "true" ; then
......
......@@ -25,6 +25,8 @@
#ifndef LPCONFIG_H
#define LPCONFIG_H
#include <ortp/port.h>
/**
* The LpConfig object is used to manipulate a configuration file.
*
......
SUBDIRS=C fr it ja cs
SUBDIRS=C fr it ja cs xml
LINPHONE_SOUNDS=ringback.wav hello8000.wav hello16000.wav
LINPHONE_RINGS=rings/orig.wav \
......
EXTRA_DIST=lpconfig.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.linphone.org/xsds/lpconfig.xsd"
xmlns:tns="http://www.linphone.org/xsds/lpconfig.xsd"
elementFormDefault="qualified">
<xs:element name="config" type="tns:LPConfig"></xs:element>
<xs:complexType name="LPConfig">
<xs:sequence>
<xs:element name="section" type="tns:LPSection" minOccurs="0" maxOccurs="unbounded"></xs:element>
</xs:sequence>
</xs:complexType>
<xs:complexType name="LPSection">
<xs:sequence>
<xs:element name="entry" type="tns:LPEntry" maxOccurs="unbounded" minOccurs="0">
</xs:element>
</xs:sequence>
<xs:attribute name="name" use="required">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:minLength value="1"></xs:minLength>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
</xs:complexType>
<xs:complexType name="LPEntry">
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="overwrite" type="xs:boolean"
use="optional" default="false">
</xs:attribute>
<xs:attribute name="name" use="required">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:minLength value="1"></xs:minLength>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:schema>
## Process this file with automake to produce Makefile.in
AM_CPPFLAGS=\
-I$(top_srcdir) \
-I$(top_srcdir)/coreapi \
-I$(top_srcdir)/exosip
COMMON_CFLAGS=\
-DIN_LINPHONE \
$(LIBXML2_CFLAGS) \
$(ORTP_CFLAGS) \
$(STRICT_OPTIONS)
if BUILD_TOOLS
lib_LTLIBRARIES=libxml2lpc.la
libxml2lpc_la_SOURCES=xml2lpc.c xml2lpc.h
libxml2lpc_la_CFLAGS=$(COMMON_CFLAGS)
libxml2lpc_la_LIBADD=\
$(LIBXML2_LIBS)
bin_PROGRAMS=xml2lpc_test
xml2lpc_test_SOURCES=xml2lpc_test.c
xml2lpc_test_CFLAGS=$(COMMON_CFLAGS)
xml2lpc_test_LDADD=$(top_builddir)/coreapi/liblinphone.la libxml2lpc.la
endif
/*
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;
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;
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);
}
void xml2lpc_context_clear_logs(xml2lpc_context *ctx) {
ctx->errorBuffer[0]='\0';
ctx->warningBuffer[0]='\0';
}
void xml2lpc_log(xml2lpc_context *xmlCtx, int level, const char *fmt, ...) {
va_list args;
va_start(args, fmt);
if(xmlCtx->cbf != NULL) {
xmlCtx->cbf((xmlCtx)->ctx, level, fmt, args);
}
va_end(args);
}
void xml2lpc_genericxml_error(void *ctx, const char *fmt, ...) {
xml2lpc_context *xmlCtx = (xml2lpc_context *)ctx;
int sl = strlen(xmlCtx->errorBuffer);
va_list args;
va_start(args, fmt);
vsnprintf(xmlCtx->errorBuffer + sl, XML2LPC_BZ-sl, fmt, args);
va_end(args);
}
void xml2lpc_genericxml_warning(void *ctx, const char *fmt, ...) {
xml2lpc_context *xmlCtx = (xml2lpc_context *)ctx;
int sl = strlen(xmlCtx->warningBuffer);
va_list args;
va_start(args, fmt);
vsnprintf(xmlCtx->warningBuffer + sl, XML2LPC_BZ-sl, fmt, args);
va_end(args);
}
static void dumpNodes(int level, xmlNode * a_node, xml2lpc_context *ctx) {
xmlNode *cur_node = NULL;
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);
}
dumpNodes(level + 1, cur_node->children, ctx);
}
}
static void dumpNode(xmlNode *node, xml2lpc_context *ctx) {
xml2lpc_log(ctx, XML2LPC_DEBUG, "node type: %d, name: %s", node->type, node->name);
}
static void dumpAttr(xmlNode *node, xml2lpc_context *ctx) {
xml2lpc_log(ctx, XML2LPC_DEBUG, "attr name: %s value:%s", node->name, node->children->content);
}
static void dumpContent(xmlNode *node, xml2lpc_context *ctx) {
xml2lpc_log(ctx, XML2LPC_DEBUG, "content: %s", node->children->content);
}
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;
}
}
}
value = (const char *)element->children->content;
dumpContent((xmlNode *)element, ctx);
if(name != NULL) {
const char *str = lp_config_get_string(ctx->lpc, sectionName, name, NULL);
if(str == NULL || overwrite) {
xml2lpc_log(ctx, XML2LPC_MESSAGE, "Set %s|%s = %s",sectionName, name, value);
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));
}
return 0;
}
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);
}
}
}
} else {
xml2lpc_log(ctx, XML2LPC_WARNING, "ignored section with no \"name\" attribute, line:%d", xmlGetLineNo((xmlNode*)element));
}
return 0;
}
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);
if (cur_node->type == XML_ELEMENT_NODE &&
strcmp((const char*)cur_node->name, "section") == 0 ) {
processSection((xmlElement*)cur_node, ctx);
}
}
return 0;
}
static int processDoc(xmlNode *node, xml2lpc_context *ctx) {
dumpNode(node, ctx);
if (node->type == XML_ELEMENT_NODE &&
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;
}
static int internal_convert_xml2lpc(xmlDoc *doc, xml2lpc_context *ctx) {
xml2lpc_log(ctx, XML2LPC_DEBUG, "Parse started");
xmlNode *rootNode = xmlDocGetRootElement(doc);
//dumpNodes(0, rootNode, cbf, ctx);
int ret = processDoc(rootNode, ctx);
xml2lpc_log(ctx, XML2LPC_DEBUG, "Parse ended ret:%d", ret);
return ret;
}
int xml2lpc_validate(xml2lpc_context *xmlCtx) {
xml2lpc_context_clear_logs(xmlCtx);
xmlSchemaValidCtxtPtr validCtx;
xmlSchemaParserCtxtPtr parserCtx = xmlSchemaNewDocParserCtxt(xmlCtx->xsd);
validCtx = xmlSchemaNewValidCtxt(xmlSchemaParse(parserCtx));
xmlSchemaSetValidErrors(validCtx, xml2lpc_genericxml_error, xml2lpc_genericxml_warning, xmlCtx);
int ret = xmlSchemaValidateDoc(validCtx, xmlCtx->doc);
if(ret >0) {
xml2lpc_log(xmlCtx, XML2LPC_WARNING, "%s", xmlCtx->warningBuffer);
xml2lpc_log(xmlCtx, XML2LPC_ERROR, "%s", xmlCtx->errorBuffer);
} else {
xml2lpc_log(xmlCtx, XML2LPC_ERROR, "Internal error");
}
xmlSchemaFreeValidCtxt(validCtx);
return ret;
}
int xml2lpc_convert(xml2lpc_context *xmlCtx, LpConfig *lpc) {
xml2lpc_context_clear_logs(xmlCtx);
xmlCtx->lpc = lpc;
return internal_convert_xml2lpc(xmlCtx->doc, xmlCtx);
}
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;
}
\ No newline at end of file
/*
linphone
Copyright (C) 2012 Belledonne Communications SARL
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.
*/
#ifndef XML2LPC_H_
#define XML2LPC_H_
#include "lpconfig.h"
typedef struct _xml2lpc_context xml2lpc_context;
typedef enum _xml2lpc_log_level {
XML2LPC_DEBUG = 0,
XML2LPC_MESSAGE,
XML2LPC_WARNING,
XML2LPC_ERROR
} xml2lpc_log_level;
typedef void(*xml2lpc_function)(void *ctx, xml2lpc_log_level level, const char *fmt, va_list list);
xml2lpc_context* xml2lpc_context_new(xml2lpc_function cbf, void *ctx);
void xml2lpc_context_destroy(xml2lpc_context*);
int xml2lpc_set_xml_file(xml2lpc_context* context, const char *filename);
int xml2lpc_set_xml_fd(xml2lpc_context* context, int fd);
int xml2lpc_set_xml_string(xml2lpc_context* context, const char *content);
int xml2lpc_set_xsd_file(xml2lpc_context* context, const char *filename);
int xml2lpc_set_xsd_fd(xml2lpc_context* context, int fd);
int xml2lpc_set_xsd_string(xml2lpc_context* context, const char *content);
int xml2lpc_validate(xml2lpc_context *context);
int xml2lpc_convert(xml2lpc_context *context, LpConfig *lpc);
#endif //XML2LPC_H_
/*
linphone
Copyright (C) 2012 Belledonne Communications SARL
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 <stdio.h>
#include "xml2lpc.h"
void cb_function(void *ctx, xml2lpc_log_level level, const char *msg, va_list list) {
const char *header;
switch(level) {
case XML2LPC_DEBUG:
header = "DEBUG";
break;
case XML2LPC_MESSAGE:
header = "MESSAGE";
break;
case XML2LPC_WARNING:
header = "WARNING";
break;
case XML2LPC_ERROR:
header = "ERROR";
break;
}
fprintf(stdout, "%s - ", header);
vfprintf(stdout, msg, list);
fprintf(stdout, "\n");
}
void show_usage(int argc, char *argv[]) {
fprintf(stderr, "usage %s convert <xml_file> <lpc_file>\n"
" %s validate <xml_file> <xsd_file>\n",
argv[0], argv[0]);
}
int main(int argc, char *argv[]) {
if(argc != 4) {
show_usage(argc, argv);
return -1;
}
xml2lp