瀏覽代碼

corex: a new module to collect reimplemented core cfg functions

- old core functions for cofiguration file do not take variables in the
  parameters, working only with static strings or integers
- some of them will be reimplemented in corex moduel to add support for
  variables, making the parameters to be dynamic at runtime
- append_branch(...) was reimplemented at this moment, allowing URI and
  Q parameters to contain variables
- there are also brand new features - two RPC commands, one to list the
  listen sockets and the other to list the hostname aliases
- name of the module comes from CORE eXtensions
Daniel-Constantin Mierla 13 年之前
父節點
當前提交
e4ecf49add

+ 12 - 0
modules/corex/Makefile

@@ -0,0 +1,12 @@
+# $Id$
+#
+# WARNING: do not run this directly, it should be run by the master Makefile
+
+include ../../Makefile.defs
+auto_gen=
+NAME=corex.so
+LIBS=
+
+DEFS+=-DOPENSER_MOD_INTERFACE
+
+include ../../Makefile.modules

+ 130 - 0
modules/corex/README

@@ -0,0 +1,130 @@
+COREX Module
+
+Daniel-Constantin Mierla
+
+   <[email protected]>
+
+Edited by
+
+Daniel-Constantin Mierla
+
+   <[email protected]>
+
+   Copyright © 2012 asipto.com
+     __________________________________________________________________
+
+   Table of Contents
+
+   1. Admin Guide
+
+        1. Overview
+        2. Dependencies
+
+              2.1. Kamailio Modules
+              2.2. External Libraries or Applications
+
+        3. Functions
+
+              3.1. append_branch([ uri, [ q ] ])
+
+        4. RPC Commands
+
+              4.1. corex.list_sockets
+              4.2. corex.list_aliases
+
+   List of Examples
+
+   1.1. append_branch usage
+
+Chapter 1. Admin Guide
+
+   Table of Contents
+
+   1. Overview
+   2. Dependencies
+
+        2.1. Kamailio Modules
+        2.2. External Libraries or Applications
+
+   3. Functions
+
+        3.1. append_branch([ uri, [ q ] ])
+
+   4. RPC Commands
+
+        4.1. corex.list_sockets
+        4.2. corex.list_aliases
+
+1. Overview
+
+   This module provides reimplementation of very old functions that were
+   in core and supported only static string or integer parameters. The new
+   versions bring support for dynamic parameters (allowing variables
+   inside the parameters).
+
+   There are also brand new features, related to core internals, but
+   controlled from configuration file or via control interfaces.
+
+   Contributions to this module must be done under BSD license, to follow
+   the requirements of the core contributions.
+
+2. Dependencies
+
+   2.1. Kamailio Modules
+   2.2. External Libraries or Applications
+
+2.1. Kamailio Modules
+
+   The following modules must be loaded before this module:
+     * None.
+
+2.2. External Libraries or Applications
+
+   The following libraries or applications must be installed before
+   running Kamailio with this module loaded:
+     * None
+
+3. Functions
+
+   3.1. append_branch([ uri, [ q ] ])
+
+3.1. append_branch([ uri, [ q ] ])
+
+   Append a new branch to the destination set, useful to build the
+   addresses for parallel forking or redirect replies.
+
+   Both parameters are optional, If no uri parameter is provided, then the
+   address from request URI (r-uri) is taken to build the new branch.
+
+   Meaning of the parameters is as follows:
+     * uri - SIP address of the branch to be used as R-URI in the outgoing
+       request.
+     * q - the Q value to set the priority of the branch based on Contact
+       address specifications
+
+   This function can be used from REQUEST_ROUTE or FAILURE_ROUTE.
+
+   Example 1.1. append_branch usage
+...
+    append_branch();
+    append_branch("$avp(uri)", "0.5");
+...
+
+4. RPC Commands
+
+   4.1. corex.list_sockets
+   4.2. corex.list_aliases
+
+4.1. corex.list_sockets
+
+   Print the list of sockets the application is listening on.
+
+   Example:
+                sercmd corex.list_sockets
+
+4.2. corex.list_aliases
+
+   Print the list of hostname aliases used to match myself condition.
+
+   Example:
+                sercmd corex.list_aliases

+ 90 - 0
modules/corex/corex_lib.c

@@ -0,0 +1,90 @@
+/**
+ * $Id$
+ *
+ * Copyright (C) 2011 Daniel-Constantin Mierla (asipto.com)
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../../dprint.h"
+#include "../../dset.h"
+
+#include "corex_lib.h"
+
+/**
+ * append new branches with generic parameters
+ */
+int corex_append_branch(sip_msg_t *msg, gparam_t *pu, gparam_t *pq)
+{
+	str uri = {0};
+	str qv = {0};
+	int ret = 0;
+
+	qvalue_t q = Q_UNSPECIFIED;
+	flag_t branch_flags = 0;
+
+	if (pu!=NULL)
+	{
+		if(fixup_get_svalue(msg, pu, &uri)!=0)
+		{
+			LM_ERR("cannot get the URI parameter\n");
+			return -1;
+		}
+	}
+
+	if (pq!=NULL)
+	{
+		if(fixup_get_svalue(msg, pq, &qv)!=0)
+		{
+			LM_ERR("cannot get the Q parameter\n");
+			return -1;
+		}
+		if(qv.len>0 && str2q(&q, qv.s, qv.len)<0)
+		{
+			LM_ERR("cannot parse the Q parameter\n");
+			return -1;
+		}
+	}
+
+
+	getbflagsval(0, &branch_flags);
+	ret = append_branch(msg, (uri.len>0)?&uri:0, &msg->dst_uri,
+			&msg->path_vec, q, branch_flags,
+			msg->force_send_socket);
+
+
+	if(uri.len<=0)
+	{
+		/* reset all branch attributes if r-uri was shifted to branch */
+		reset_force_socket(msg);
+		setbflagsval(0, 0);
+		if(msg->dst_uri.s!=0)
+			pkg_free(msg->dst_uri.s);
+		msg->dst_uri.s = 0;
+		msg->dst_uri.len = 0;
+		if(msg->path_vec.s!=0)
+			pkg_free(msg->path_vec.s);
+		msg->path_vec.s = 0;
+		msg->path_vec.len = 0;
+	}
+
+	return ret;
+}

+ 29 - 0
modules/corex/corex_lib.h

@@ -0,0 +1,29 @@
+/**
+ * $Id$
+ *
+ * Copyright (C) 2011 Daniel-Constantin Mierla (asipto.com)
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#ifndef _COREX_LIB_H_
+#define _COREX_LIB_H_
+
+#include "../../mod_fix.h"
+
+int corex_append_branch(sip_msg_t *msg, gparam_t *pu, gparam_t *pq);
+
+#endif

+ 115 - 0
modules/corex/corex_mod.c

@@ -0,0 +1,115 @@
+/**
+ * $Id$
+ *
+ * Copyright (C) 2011 Daniel-Constantin Mierla (asipto.com)
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../../sr_module.h"
+#include "../../dprint.h"
+#include "../../ut.h"
+
+#include "corex_lib.h"
+#include "corex_rpc.h"
+
+MODULE_VERSION
+
+static int w_append_branch(sip_msg_t *msg, char *su, char *sq);
+
+static int  mod_init(void);
+static int  child_init(int);
+static void mod_destroy(void);
+
+static cmd_export_t cmds[]={
+	{"append_branch", (cmd_function)w_append_branch, 0, 0,
+			0, REQUEST_ROUTE | FAILURE_ROUTE },
+	{"append_branch", (cmd_function)w_append_branch, 1, fixup_spve_null,
+			0, REQUEST_ROUTE | FAILURE_ROUTE },
+	{"append_branch", (cmd_function)w_append_branch, 2, fixup_spve_spve,
+			0, REQUEST_ROUTE | FAILURE_ROUTE },
+
+
+	{0, 0, 0, 0, 0, 0}
+};
+
+static param_export_t params[]={
+	{0, 0, 0}
+};
+
+struct module_exports exports = {
+	"corex",
+	DEFAULT_DLFLAGS, /* dlopen flags */
+	cmds,
+	params,
+	0,
+	0,              /* exported MI functions */
+	0,              /* exported pseudo-variables */
+	0,              /* extra processes */
+	mod_init,       /* module initialization function */
+	0,              /* response function */
+	mod_destroy,    /* destroy function */
+	child_init      /* per child init function */
+};
+
+
+
+/**
+ * init module function
+ */
+static int mod_init(void)
+{
+	if(corex_init_rpc()<0)
+	{
+		LM_ERR("failed to register RPC commands\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+/**
+ * @brief Initialize async module children
+ */
+static int child_init(int rank)
+{
+	if (rank!=PROC_MAIN)
+		return 0;
+
+	return 0;
+}
+/**
+ * destroy module function
+ */
+static void mod_destroy(void)
+{
+}
+
+/**
+ * config wrapper for append branch
+ */
+static int w_append_branch(sip_msg_t *msg, char *su, char *sq)
+{
+	if(corex_append_branch(msg, (gparam_t*)su, (gparam_t*)sq) < 0)
+		return -1;
+	return 1;
+}
+

+ 168 - 0
modules/corex/corex_rpc.c

@@ -0,0 +1,168 @@
+/**
+ * $Id$
+ *
+ * Copyright (C) 2011 Daniel-Constantin Mierla (asipto.com)
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../../dprint.h"
+#include "../../ut.h"
+#include "../../socket_info.h"
+#include "../../name_alias.h"
+#include "../../rpc.h"
+#include "../../rpc_lookup.h"
+
+
+static const char* corex_rpc_list_sockets_doc[2] = {
+	"List listening sockets",
+	0
+};
+
+
+/*
+ * RPC command to list the listening sockets
+ */
+static void corex_rpc_list_sockets(rpc_t* rpc, void* ctx)
+{
+	void* th;
+	void* ih;
+
+	struct socket_info *si;
+	struct socket_info** list;
+	struct addr_info* ai;
+	unsigned short proto;
+	
+	proto=PROTO_UDP;
+	do {
+		list=get_sock_info_list(proto);
+		for(si=list?*list:0; si; si=si->next)
+		{
+			/* add structure node */
+			if (rpc->add(ctx, "{", &th) < 0)
+			{
+				rpc->fault(ctx, 500, "Internal error socket structure");
+				return;
+			}
+
+			if(rpc->struct_add(th, "ss{",
+				"PROTO", 	get_valid_proto_name(proto),
+				"NAME", 	si->name.s,
+				"ADDRLIST",  &ih)<0)
+			{
+				rpc->fault(ctx, 500, "Internal error address list structure");
+				return;
+			}
+
+			if(rpc->struct_add(ih, "s", "ADDR", si->address_str.s)<0)
+			{
+				rpc->fault(ctx, 500, "Internal error address structure");
+				return;
+			}
+	
+			if (si->addr_info_lst)
+			{
+
+				for (ai=si->addr_info_lst; ai; ai=ai->next)
+				{
+					if(rpc->struct_add(ih, "s", "ADDR", ai->address_str.s)<0)
+					{
+						rpc->fault(ctx, 500,
+								"Internal error extra address structure");
+						return;
+					}
+
+				}
+			}
+
+			if(rpc->struct_add(th, "ssss",
+					"PORT", si->port_no_str.s,
+					"MCAST", si->flags & SI_IS_MCAST ? "yes" : "no",
+					"MHOMED", si->flags & SI_IS_MHOMED? "yes" : "no",
+					"ADVERTISE", si->useinfo.name.s?si->useinfo.name.s:"-")<0)
+			{
+				rpc->fault(ctx, 500, "Internal error attrs structure");
+				return;
+			}
+		}
+	} while((proto=next_proto(proto)));
+
+	return;
+}
+
+
+static const char* corex_rpc_list_aliases_doc[2] = {
+	"List socket aliases",
+	0
+};
+
+
+/*
+ * RPC command to list the socket aliases
+ */
+static void corex_rpc_list_aliases(rpc_t* rpc, void* ctx)
+{
+	void* th;
+
+	struct host_alias* a;
+
+	for(a=aliases; a; a=a->next) 
+	{
+		/* add structure node */
+		if (rpc->add(ctx, "{", &th) < 0)
+		{
+			rpc->fault(ctx, 500, "Internal error alias structure");
+			return;
+		}
+		if(rpc->struct_add(th, "sSd",
+				"PROTO", get_valid_proto_name(a->proto),
+				"ADDR",  &a->alias,
+				"PORT",  a->port)<0)
+		{
+			rpc->fault(ctx, 500, "Internal error alias attributes");
+			return;
+		}
+	}
+
+	return;
+}
+
+
+rpc_export_t corex_rpc_cmds[] = {
+	{"corex.list_sockets", corex_rpc_list_sockets,
+		corex_rpc_list_sockets_doc, 0},
+	{"corex.list_aliases", corex_rpc_list_aliases,
+		corex_rpc_list_aliases_doc, 0},
+	{0, 0, 0, 0}
+};
+
+/**
+ * register RPC commands
+ */
+int corex_init_rpc(void)
+{
+	if (rpc_register_array(corex_rpc_cmds)!=0)
+	{
+		LM_ERR("failed to register RPC commands\n");
+		return -1;
+	}
+	return 0;
+}

+ 27 - 0
modules/corex/corex_rpc.h

@@ -0,0 +1,27 @@
+/**
+ * $Id$
+ *
+ * Copyright (C) 2011 Daniel-Constantin Mierla (asipto.com)
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#ifndef _COREX_RPC_H_
+#define _COREX_RPC_H_
+
+int corex_init_rpc(void);
+
+#endif

+ 4 - 0
modules/corex/doc/Makefile

@@ -0,0 +1,4 @@
+docs = corex.xml
+
+docbook_dir = ../../../docbook
+include $(docbook_dir)/Makefile.module

+ 37 - 0
modules/corex/doc/corex.xml

@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding='ISO-8859-1'?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
+"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
+
+<!-- Include general documentation entities -->
+<!ENTITY % docentities SYSTEM "../../../docbook/entities.xml">
+%docentities;
+
+]>
+
+<book xmlns:xi="http://www.w3.org/2001/XInclude">
+    <bookinfo>
+	<title>COREX Module</title>
+	<productname class="trade">kamailio.org</productname>
+	<authorgroup>
+	    <author>
+		<firstname>Daniel-Constantin</firstname>
+		<surname>Mierla</surname>
+		<email>[email protected]</email>
+	    </author>
+	    <editor>
+		<firstname>Daniel-Constantin</firstname>
+		<surname>Mierla</surname>
+		<email>[email protected]</email>
+	    </editor>
+	</authorgroup>
+	<copyright>
+	    <year>2012</year>
+	    <holder>asipto.com</holder>
+	</copyright>
+    </bookinfo>
+    <toc></toc>
+    
+    <xi:include href="corex_admin.xml"/>
+    
+    
+</book>

+ 142 - 0
modules/corex/doc/corex_admin.xml

@@ -0,0 +1,142 @@
+<?xml version="1.0" encoding='ISO-8859-1'?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
+"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
+
+<!-- Include general documentation entities -->
+<!ENTITY % docentities SYSTEM "../../../docbook/entities.xml">
+%docentities;
+
+]>
+<!-- Module User's Guide -->
+
+<chapter>
+	
+	<title>&adminguide;</title>
+	
+	<section>
+	<title>Overview</title>
+	<para>
+		This module provides reimplementation of very old functions that
+		were in core and supported only static string or integer parameters.
+		The new versions bring support for dynamic parameters (allowing
+		variables inside the parameters).
+	</para>
+	<para>
+		There are also brand new features, related to core internals, but
+		controlled from configuration file or via control interfaces.
+	</para>
+	<para>
+		Contributions to this module must be done under BSD license, to
+		follow the requirements of the core contributions.
+	</para>
+	</section>
+
+	<section>
+	<title>Dependencies</title>
+	<section>
+		<title>&kamailio; Modules</title>
+		<para>
+		The following modules must be loaded before this module:
+			<itemizedlist>
+			<listitem>
+			<para>
+				<emphasis>None</emphasis>.
+			</para>
+			</listitem>
+			</itemizedlist>
+		</para>
+	</section>
+	<section>
+		<title>External Libraries or Applications</title>
+		<para>
+		The following libraries or applications must be installed before running
+		&kamailio; with this module loaded:
+			<itemizedlist>
+			<listitem>
+			<para>
+				<emphasis>None</emphasis>
+			</para>
+			</listitem>
+			</itemizedlist>
+		</para>
+	</section>
+	</section>
+	<section>
+	<title>Functions</title>
+	<section>
+	    <title>
+		<function moreinfo="none">append_branch([ uri, [ q ] ])</function>
+	    </title>
+	    <para>
+			Append a new branch to the destination set, useful to build the
+			addresses for parallel forking or redirect replies.
+		</para>
+		<para>
+			Both parameters are optional, If no uri parameter is provided,
+			then the address from request URI (r-uri) is taken to build the
+			new branch.
+		</para>
+		<para>Meaning of the parameters is as follows:</para>
+		<itemizedlist>
+		<listitem>
+			<para>
+				<emphasis>uri</emphasis> - SIP address of the branch to be
+				used as R-URI in the outgoing request.
+			</para>
+		</listitem>
+		<listitem>
+			<para>
+				<emphasis>q</emphasis> - the Q value to set the priority
+				of the branch based on Contact address specifications
+			</para>
+		</listitem>
+		</itemizedlist>
+		<para>
+		This function can be used from REQUEST_ROUTE or FAILURE_ROUTE.
+		</para>
+		<example>
+		<title><function>append_branch</function> usage</title>
+		<programlisting format="linespecific">
+...
+    append_branch();
+    append_branch("$avp(uri)", "0.5");
+...
+</programlisting>
+	    </example>
+	</section>
+	</section>
+
+	<section>
+	<title>RPC Commands</title>
+	<section>
+		<title>
+		<function moreinfo="none">corex.list_sockets</function>
+		</title>
+		<para>
+			Print the list of sockets the application is listening on.
+		</para>
+		<para>
+		Example:
+		</para>
+        <programlisting  format="linespecific">
+		sercmd corex.list_sockets
+		</programlisting>
+    </section>
+	<section>
+		<title>
+		<function moreinfo="none">corex.list_aliases</function>
+		</title>
+		<para>
+			Print the list of hostname aliases used to match myself
+			condition.
+		</para>
+		<para>
+		Example:
+		</para>
+        <programlisting  format="linespecific">
+		sercmd corex.list_aliases
+		</programlisting>
+    </section>
+    </section>
+</chapter>
+