Browse code

xcap_server: new module providing xcap server functinality

- re-use the transport layer from SIP server
- XCAP documents can be sent via SIP (UDP, TCP, TLS, SCTP, HTTP and
HTTPS
- re-use xcap table from K presence server to store the documents
- implemented basic ops: PUT, GET and DELETE
- no much testing done so far
- todo: document validation and xcap diff operations

Daniel-Constantin Mierla authored on 25/05/2010 14:19:43
Showing 6 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,22 @@
0
+# $Id: Makefile $
1
+#
2
+# XCAP server for Kamailio
3
+#
4
+# 
5
+# WARNING: do not run this directly, it should be run by the master Makefile
6
+
7
+include ../../Makefile.defs
8
+auto_gen=
9
+NAME=xcap_server.so
10
+LIBS=
11
+
12
+DEFS+=-I/usr/include/libxml2 -I$(LOCALBASE)/include/libxml2 \
13
+      -I$(LOCALBASE)/include
14
+LIBS+=-L$(SYSBASE)/include/lib  -L$(LOCALBASE)/lib -lxml2
15
+
16
+DEFS+=-DOPENSER_MOD_INTERFACE
17
+
18
+SERLIBPATH=../../lib
19
+SER_LIBS+=$(SERLIBPATH)/srdb1/srdb1
20
+
21
+include ../../Makefile.modules
0 22
new file mode 100644
... ...
@@ -0,0 +1,310 @@
0
+XCAP Server Module
1
+
2
+Daniel-Constantin Mierla
3
+
4
+   <miconda@gmail.com>
5
+
6
+Edited by
7
+
8
+Daniel-Constantin Mierla
9
+
10
+   <miconda@gmail.com>
11
+
12
+   Copyright © 2010 asipto.com
13
+     __________________________________________________________________
14
+
15
+   Table of Contents
16
+
17
+   1. Admin Guide
18
+
19
+        1. Overview
20
+        2. Dependencies
21
+
22
+              2.1. Kamailio Modules
23
+              2.2. External Libraries or Applications
24
+
25
+        3. Exported Parameters
26
+
27
+              3.1. db_url (string)
28
+              3.2. xcap_table (string)
29
+              3.3. xcap_root (str)
30
+              3.4. xcap_root (str)
31
+              3.5. buf_size (int)
32
+
33
+        4. Exported Functions
34
+
35
+              4.1. xcaps_put(uri, path, doc)
36
+              4.2. xcaps_get(uri, path)
37
+              4.3. xcaps_del(uri, path)
38
+
39
+        5. Simple XCAP Server Config
40
+
41
+   List of Examples
42
+
43
+   1.1. Set the “db_url” parameter
44
+   1.2. Set the “xcap_table” parameter
45
+   1.3. Set url_match parameter
46
+   1.4. Set url_match parameter
47
+   1.5. Set the “buf_size” parameter
48
+   1.6. xcaps_put usage
49
+   1.7. xcaps_get usage
50
+   1.8. xcaps_del usage
51
+   1.9. sample xcap server
52
+
53
+Chapter 1. Admin Guide
54
+
55
+   Table of Contents
56
+
57
+   1. Overview
58
+   2. Dependencies
59
+
60
+        2.1. Kamailio Modules
61
+        2.2. External Libraries or Applications
62
+
63
+   3. Exported Parameters
64
+
65
+        3.1. db_url (string)
66
+        3.2. xcap_table (string)
67
+        3.3. xcap_root (str)
68
+        3.4. xcap_root (str)
69
+        3.5. buf_size (int)
70
+
71
+   4. Exported Functions
72
+
73
+        4.1. xcaps_put(uri, path, doc)
74
+        4.2. xcaps_get(uri, path)
75
+        4.3. xcaps_del(uri, path)
76
+
77
+   5. Simple XCAP Server Config
78
+
79
+1. Overview
80
+
81
+   This module provides an XCAP server functionaly inside Kamailio and SER
82
+   SIP servers.
83
+
84
+   Benefits brought by this integrated XCAP server:
85
+     * reuse of SIP router transport layer - XCAP documents can be sent
86
+       via SIP (UDP, TCP, TLS and SCTP) and via HTTP (TCP or TLS (HTTPS)).
87
+       For HTTP/S, you neet to load XHTTP module to handle HTTP/S
88
+       requests.
89
+     * the Presence server has access imediatelly to the latest version of
90
+       XCAP documents. No more need to trigger refresh of XCAP documents
91
+       via MI command
92
+     * can be used stand-alone, with a different Presence server. It is
93
+       not specific for Kamailio or SER. Documents can be fetched via GET
94
+     * no exotic dependencies, it is written in C. It depends on libxml2,
95
+       sl module and a database module (required to store the xcap
96
+       documents).
97
+     * you can do digest authentication using database, radius, ldap, etc.
98
+       Can reuse authorization mechanisms provided by SIP server.
99
+     * flexibility - the XCAP server is controlled from config file of SIP
100
+       server, therefore you can blend the XCAP logic with features
101
+       provided by core or other modules.
102
+
103
+2. Dependencies
104
+
105
+   2.1. Kamailio Modules
106
+   2.2. External Libraries or Applications
107
+
108
+2.1. Kamailio Modules
109
+
110
+   The following modules must be loaded before this module:
111
+     * sl - stateless reply module
112
+     * db - a database engine module
113
+
114
+2.2. External Libraries or Applications
115
+
116
+   The following libraries or applications must be installed before
117
+   running Kamailio with this module loaded:
118
+     * libxml2 - libxml2 library for runtime and libxml2-dev for
119
+       compilation.
120
+
121
+3. Exported Parameters
122
+
123
+   3.1. db_url (string)
124
+   3.2. xcap_table (string)
125
+   3.3. xcap_root (str)
126
+   3.4. xcap_root (str)
127
+   3.5. buf_size (int)
128
+
129
+3.1. db_url (string)
130
+
131
+   Database URL.
132
+
133
+   Default value is “mysql://openser:openserrw@localhost/openser”.
134
+
135
+   Example 1.1. Set the “db_url” parameter
136
+...
137
+modparam("xcap_server", "db_url", "mysql://user:passwd@host.com/dbname")
138
+...
139
+
140
+3.2. xcap_table (string)
141
+
142
+   The name of table where to store the xcap documents.
143
+
144
+   Default value is “xcap”.
145
+
146
+   Example 1.2. Set the “xcap_table” parameter
147
+...
148
+modparam("xcap_server", "xcap_table", "xcapdocs")
149
+...
150
+
151
+3.3. xcap_root (str)
152
+
153
+   XCAP root URL.
154
+
155
+   Default value is '/xcap-root/'.
156
+
157
+   Example 1.3. Set url_match parameter
158
+...
159
+modparam("xcap_server", "xcap_root", "/xcap-root/")
160
+...
161
+
162
+3.4. xcap_root (str)
163
+
164
+   XCAP root URL.
165
+
166
+   Default value is '/xcap-root/'.
167
+
168
+   Example 1.4. Set url_match parameter
169
+...
170
+modparam("xcap_server", "xcap_root", "/xcap-root/")
171
+...
172
+
173
+3.5. buf_size (int)
174
+
175
+   Size of local buffer for handling XCAP documents.
176
+
177
+   Default value is “1024”.
178
+
179
+   Example 1.5. Set the “buf_size” parameter
180
+...
181
+modparam("xcap_server", "buf_size", 2048)
182
+...
183
+
184
+4. Exported Functions
185
+
186
+   4.1. xcaps_put(uri, path, doc)
187
+   4.2. xcaps_get(uri, path)
188
+   4.3. xcaps_del(uri, path)
189
+
190
+4.1.  xcaps_put(uri, path, doc)
191
+
192
+   Handle XCAP PUT command.
193
+
194
+   Example 1.6. xcaps_put usage
195
+...
196
+event_route[xhttp:request] {
197
+        if($hu=~"^/xcap-root/")
198
+        {
199
+                # xcap ops
200
+                switch($rm) {
201
+                        case "PUT":
202
+                                xcaps_put("sip:101@$Ri", "$hu", "$rb");
203
+                                exit;
204
+                                break;
205
+                }
206
+        }
207
+}
208
+...
209
+
210
+4.2.  xcaps_get(uri, path)
211
+
212
+   Handle XCAP GET command.
213
+
214
+   Example 1.7. xcaps_get usage
215
+...
216
+event_route[xhttp:request] {
217
+        if($hu=~"^/xcap-root/")
218
+        {
219
+                # xcap ops
220
+                switch($rm) {
221
+                        case "GETT":
222
+                                xcaps_get("sip:101@$Ri", "$hu");
223
+                                exit;
224
+                                break;
225
+                }
226
+        }
227
+}
228
+...
229
+
230
+4.3.  xcaps_del(uri, path)
231
+
232
+   Handle XCAP DELETE command.
233
+
234
+   Example 1.8. xcaps_del usage
235
+...
236
+event_route[xhttp:request] {
237
+        if($hu=~"^/xcap-root/")
238
+        {
239
+                # xcap ops
240
+                switch($rm) {
241
+                        case "DELETE":
242
+                                xcaps_del("sip:101@$Ri", "$hu");
243
+                                exit;
244
+                                break;
245
+                }
246
+        }
247
+}
248
+...
249
+
250
+5. Simple XCAP Server Config
251
+
252
+   Example 1.9. sample xcap server
253
+...
254
+loadmodule "xhttp.so"
255
+loadmodule "xcap_server.so"
256
+
257
+...
258
+
259
+# ----- xcap_server params -----
260
+modparam("xcap_server", "db_url",
261
+        "mysql://openser:openserrw@localhost/openser")
262
+
263
+...
264
+
265
+event_route[xhttp:request] {
266
+#!ifdef WITH_XHTTPAUTH
267
+        if (!www_authorize("xcap", "subscriber"))
268
+        {
269
+                www_challenge("xcap", "0");
270
+                exit;
271
+        }
272
+#!endif
273
+        if($hu=~"^/xcap-root/")
274
+        {
275
+                # xcap requests
276
+                switch($rm) {
277
+                        case "PUT":
278
+                                xcaps_put("sip:101@$Ri", "$hu", "$rb");
279
+                                exit;
280
+                        break;
281
+                        case "GET":
282
+                                xcaps_get("sip:101@$Ri", "$hu");
283
+                                exit;
284
+                        break;
285
+                        case "DELETE":
286
+                                xcaps_del("sip:101@$Ri", "$hu");
287
+                                exit;
288
+                        break;
289
+                }
290
+        }
291
+
292
+        # other http requests
293
+        xhttp_reply("200", "OK", "text/html",
294
+                "<html><body>OK: $si:$sp</body></html>");
295
+        exit;
296
+}
297
+
298
+...
299
+
300
+   The URL for XCAP has to be:
301
+
302
+   http://_your_sip_server_ip_:_your_sip_server_port_/xcap-root/...
303
+
304
+   For example, if your SIP server IP is 10.1.1.10 and it is listening on
305
+   TCP port 5060 and TLS port 5061, following XCAP URLs can be used:
306
+
307
+   http://10.1.1.10:5060/xcap-root/...
308
+
309
+   https://10.1.1.10:5061/xcap-root/...
0 310
new file mode 100644
... ...
@@ -0,0 +1,4 @@
0
+docs = xcap_server.xml
1
+
2
+docbook_dir = ../../../docbook
3
+include $(docbook_dir)/Makefile.module
0 4
new file mode 100644
... ...
@@ -0,0 +1,37 @@
0
+<?xml version="1.0" encoding='ISO-8859-1'?>
1
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
2
+"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
3
+
4
+<!-- Include general documentation entities -->
5
+<!ENTITY % docentities SYSTEM "../../../docbook/entities.xml">
6
+%docentities;
7
+
8
+]>
9
+
10
+<book xmlns:xi="http://www.w3.org/2001/XInclude">
11
+    <bookinfo>
12
+	<title>XCAP Server Module</title>
13
+	<productname class="trade">sip-router.org</productname>
14
+	<authorgroup>
15
+	    <author>
16
+		<firstname>Daniel-Constantin</firstname>
17
+		<surname>Mierla</surname>
18
+		<email>miconda@gmail.com</email>
19
+	    </author>
20
+	    <editor>
21
+		<firstname>Daniel-Constantin</firstname>
22
+		<surname>Mierla</surname>
23
+		<email>miconda@gmail.com</email>
24
+	    </editor>
25
+	</authorgroup>
26
+	<copyright>
27
+	    <year>2010</year>
28
+	    <holder>asipto.com</holder>
29
+	</copyright>
30
+    </bookinfo>
31
+    <toc></toc>
32
+    
33
+    <xi:include href="xcap_server_admin.xml"/>
34
+    
35
+    
36
+</book>
0 37
new file mode 100644
... ...
@@ -0,0 +1,362 @@
0
+<?xml version="1.0" encoding='ISO-8859-1'?>
1
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
2
+"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
3
+
4
+<!-- Include general documentation entities -->
5
+<!ENTITY % docentities SYSTEM "../../../docbook/entities.xml">
6
+%docentities;
7
+
8
+]>
9
+<!-- Module User's Guide -->
10
+
11
+<chapter>
12
+	
13
+	<title>&adminguide;</title>
14
+	
15
+	<section>
16
+	<title>Overview</title>
17
+	<para>
18
+		This module provides an XCAP server functionaly inside Kamailio
19
+		and SER SIP servers.
20
+	</para>
21
+	<para>
22
+		Benefits brought by this integrated XCAP server:
23
+		<itemizedlist>
24
+		<listitem>
25
+		<para>
26
+			reuse of SIP router transport layer - XCAP documents can be sent
27
+			via SIP (UDP, TCP, TLS and SCTP) and via HTTP (TCP or TLS (HTTPS)).
28
+			For HTTP/S, you neet to load XHTTP module to handle HTTP/S
29
+			requests.
30
+		</para>
31
+		</listitem>
32
+		<listitem>
33
+		<para>
34
+			the Presence server has access imediatelly to the latest version
35
+			of XCAP documents. No more need to trigger refresh of XCAP
36
+			documents via MI command
37
+		</para>
38
+		</listitem>
39
+		<listitem>
40
+		<para>
41
+			can be used stand-alone, with a different Presence server. It is not
42
+			specific for Kamailio or SER. Documents can be fetched via GET
43
+		</para>
44
+		</listitem>
45
+		<listitem>
46
+		<para>
47
+			no exotic dependencies, it is written in C. It depends on
48
+			libxml2, sl module and a database module (required to store the
49
+			xcap documents).
50
+		</para>
51
+		</listitem>
52
+		<listitem>
53
+		<para>
54
+			you can do digest authentication using database, radius, ldap, etc.
55
+			Can reuse authorization mechanisms provided by SIP server.
56
+		</para>
57
+		</listitem>
58
+		<listitem>
59
+		<para>
60
+			flexibility - the XCAP server is controlled from config file of
61
+			SIP server, therefore you can blend the XCAP logic with features
62
+			provided by core or other modules.
63
+		</para>
64
+		</listitem>
65
+		</itemizedlist>
66
+	</para>
67
+	</section>
68
+	<section>
69
+	<title>Dependencies</title>
70
+	<section>
71
+		<title>&kamailio; Modules</title>
72
+		<para>
73
+		The following modules must be loaded before this module:
74
+			<itemizedlist>
75
+			<listitem>
76
+			<para>
77
+				<emphasis>sl</emphasis> - stateless reply module
78
+			</para>
79
+			</listitem>
80
+			<listitem>
81
+			<para>
82
+				<emphasis>db</emphasis> - a database engine module
83
+			</para>
84
+			</listitem>
85
+			</itemizedlist>
86
+		</para>
87
+	</section>
88
+	<section>
89
+		<title>External Libraries or Applications</title>
90
+		<para>
91
+		The following libraries or applications must be installed before running
92
+		&kamailio; with this module loaded:
93
+			<itemizedlist>
94
+			<listitem>
95
+			<para>
96
+				<emphasis>libxml2</emphasis> - libxml2 library for runtime and
97
+				libxml2-dev for compilation.
98
+			</para>
99
+			</listitem>
100
+			</itemizedlist>
101
+		</para>
102
+	</section>
103
+	</section>
104
+	<section>
105
+	<title>Exported Parameters</title>
106
+	<section>
107
+		<title><varname>db_url</varname> (string)</title>
108
+		<para>
109
+		Database &url;.
110
+		</para>
111
+		<para>
112
+		<emphasis>
113
+			Default value is 
114
+			<quote>mysql://openser:openserrw@localhost/openser</quote>.
115
+		</emphasis>
116
+		</para>
117
+		<example>
118
+		<title>Set the <quote>db_url</quote> parameter</title>
119
+		<programlisting format="linespecific">
120
+...
121
+modparam("xcap_server", "db_url", "mysql://user:passwd@host.com/dbname")
122
+...
123
+</programlisting>
124
+		</example>
125
+	</section>
126
+	<section>
127
+		<title><varname>xcap_table</varname> (string)</title>
128
+		<para>
129
+		The name of table where to store the xcap documents.
130
+		</para>
131
+		<para>
132
+		<emphasis>
133
+			Default value is <quote>xcap</quote>.
134
+		</emphasis>
135
+		</para>
136
+		<example>
137
+		<title>Set the <quote>xcap_table</quote> parameter</title>
138
+<programlisting format="linespecific">
139
+...
140
+modparam("xcap_server", "xcap_table", "xcapdocs")
141
+...
142
+</programlisting>
143
+		</example>
144
+	</section>
145
+	<section>
146
+		<title><varname>xcap_root</varname> (str)</title>
147
+		<para>
148
+			XCAP root URL.
149
+		</para>
150
+		<para>
151
+		<emphasis>
152
+			Default value is '/xcap-root/'.
153
+		</emphasis>
154
+		</para>
155
+		<example>
156
+		<title>Set <varname>url_match</varname> parameter</title>
157
+		<programlisting format="linespecific">
158
+...
159
+modparam("xcap_server", "xcap_root", "/xcap-root/")
160
+...
161
+</programlisting>
162
+		</example>
163
+	</section>
164
+	<section>
165
+		<title><varname>xcap_root</varname> (str)</title>
166
+		<para>
167
+			XCAP root URL.
168
+		</para>
169
+		<para>
170
+		<emphasis>
171
+			Default value is '/xcap-root/'.
172
+		</emphasis>
173
+		</para>
174
+		<example>
175
+		<title>Set <varname>url_match</varname> parameter</title>
176
+		<programlisting format="linespecific">
177
+...
178
+modparam("xcap_server", "xcap_root", "/xcap-root/")
179
+...
180
+</programlisting>
181
+		</example>
182
+	</section>
183
+	<section>
184
+		<title><varname>buf_size</varname> (int)</title>
185
+		<para>
186
+		Size of local buffer for handling XCAP documents.
187
+		</para>
188
+		<para>
189
+		<emphasis>
190
+			Default value is <quote>1024</quote>.
191
+		</emphasis>
192
+		</para>
193
+		<example>
194
+		<title>Set the <quote>buf_size</quote> parameter</title>
195
+<programlisting format="linespecific">
196
+...
197
+modparam("xcap_server", "buf_size", 2048)
198
+...
199
+</programlisting>
200
+		</example>
201
+	</section>
202
+	</section>
203
+
204
+	<section>
205
+	<title>Exported Functions</title>
206
+ 	<section>
207
+	    <title>
208
+		<function moreinfo="none">xcaps_put(uri, path, doc)</function>
209
+	    </title>
210
+	    <para>
211
+		Handle XCAP PUT command.
212
+	    </para>
213
+		<example>
214
+		<title><function>xcaps_put</function> usage</title>
215
+		<programlisting format="linespecific">
216
+...
217
+event_route[xhttp:request] {
218
+	if($hu=~"^/xcap-root/")
219
+	{
220
+		# xcap ops
221
+		switch($rm) {
222
+			case "PUT":
223
+				xcaps_put("sip:101@$Ri", "$hu", "$rb");
224
+				exit;
225
+				break;
226
+		}
227
+	}
228
+}
229
+...
230
+</programlisting>
231
+	    </example>
232
+	</section>
233
+ 	<section>
234
+	    <title>
235
+		<function moreinfo="none">xcaps_get(uri, path)</function>
236
+	    </title>
237
+	    <para>
238
+		Handle XCAP GET command.
239
+	    </para>
240
+		<example>
241
+		<title><function>xcaps_get</function> usage</title>
242
+		<programlisting format="linespecific">
243
+...
244
+event_route[xhttp:request] {
245
+	if($hu=~"^/xcap-root/")
246
+	{
247
+		# xcap ops
248
+		switch($rm) {
249
+			case "GETT":
250
+				xcaps_get("sip:101@$Ri", "$hu");
251
+				exit;
252
+				break;
253
+		}
254
+	}
255
+}
256
+...
257
+</programlisting>
258
+	    </example>
259
+	</section>
260
+ 	<section>
261
+	    <title>
262
+		<function moreinfo="none">xcaps_del(uri, path)</function>
263
+	    </title>
264
+	    <para>
265
+		Handle XCAP DELETE command.
266
+	    </para>
267
+		<example>
268
+		<title><function>xcaps_del</function> usage</title>
269
+		<programlisting format="linespecific">
270
+...
271
+event_route[xhttp:request] {
272
+	if($hu=~"^/xcap-root/")
273
+	{
274
+		# xcap ops
275
+		switch($rm) {
276
+			case "DELETE":
277
+				xcaps_del("sip:101@$Ri", "$hu");
278
+				exit;
279
+				break;
280
+		}
281
+	}
282
+}
283
+...
284
+</programlisting>
285
+	    </example>
286
+	</section>
287
+	</section>
288
+
289
+	<section>
290
+	<title>Simple XCAP Server Config</title>
291
+		<example>
292
+		<title>sample xcap server</title>
293
+		<programlisting format="linespecific">
294
+...
295
+loadmodule "xhttp.so"
296
+loadmodule "xcap_server.so"
297
+
298
+...
299
+
300
+# ----- xcap_server params -----
301
+modparam("xcap_server", "db_url",
302
+	"mysql://openser:openserrw@localhost/openser")
303
+
304
+...
305
+
306
+event_route[xhttp:request] {
307
+#!ifdef WITH_XHTTPAUTH
308
+	if (!www_authorize("xcap", "subscriber"))
309
+	{
310
+		www_challenge("xcap", "0");
311
+		exit;
312
+	}
313
+#!endif
314
+	if($hu=~"^/xcap-root/")
315
+	{
316
+		# xcap requests
317
+		switch($rm) {
318
+			case "PUT":
319
+				xcaps_put("sip:101@$Ri", "$hu", "$rb");
320
+				exit;
321
+			break;
322
+			case "GET":
323
+				xcaps_get("sip:101@$Ri", "$hu");
324
+				exit;
325
+			break;
326
+			case "DELETE":
327
+				xcaps_del("sip:101@$Ri", "$hu");
328
+				exit;
329
+			break;
330
+		}
331
+	}
332
+
333
+	# other http requests
334
+	xhttp_reply("200", "OK", "text/html",
335
+		"&lt;html&gt;&lt;body>OK: $si:$sp&lt;/body&gt;&lt;/html&gt;");
336
+	exit;
337
+}
338
+
339
+...
340
+</programlisting>
341
+	    </example>
342
+	<para>
343
+		The URL for XCAP has to be:
344
+	</para>
345
+	<para>
346
+		http://_your_sip_server_ip_:_your_sip_server_port_/xcap-root/...
347
+	</para>
348
+	<para>
349
+		For example, if your SIP server IP is 10.1.1.10 and it is listening
350
+		on TCP port 5060 and TLS port 5061, following XCAP URLs can be used:
351
+	</para>
352
+	<para>
353
+		http://10.1.1.10:5060/xcap-root/...
354
+	</para>
355
+	<para>
356
+		https://10.1.1.10:5061/xcap-root/...
357
+	</para>
358
+	</section>
359
+
360
+</chapter>
361
+
0 362
new file mode 100644
... ...
@@ -0,0 +1,860 @@
0
+/*
1
+ * $Id$
2
+ *
3
+ * xcap_server module - builtin XCAP server
4
+ *
5
+ * Copyright (C) 2010 Daniel-Constantin Mierla (asipto.com)
6
+ *
7
+ * This file is part of Kamailio, a free SIP server.
8
+ *
9
+ * Kamailio is free software; you can redistribute it and/or modify
10
+ * it under the terms of the GNU General Public License as published by
11
+ * the Free Software Foundation; either version 2 of the License, or
12
+ * (at your option) any later version
13
+ *
14
+ * Kamailio is distributed in the hope that it will be useful,
15
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
+ * GNU General Public License for more details.
18
+ *
19
+ * You should have received a copy of the GNU General Public License 
20
+ * along with this program; if not, write to the Free Software 
21
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22
+ *
23
+ */
24
+
25
+#include <stdio.h>
26
+#include <string.h>
27
+#include <stdlib.h>
28
+#include <sys/types.h>
29
+#include <unistd.h>
30
+#include <fcntl.h>
31
+#include <time.h>
32
+
33
+#include "../../lib/srdb1/db.h"
34
+#include "../../pt.h"
35
+#include "../../sr_module.h"
36
+#include "../../dprint.h"
37
+#include "../../error.h"
38
+#include "../../ut.h"
39
+#include "../../mem/mem.h"
40
+#include "../../data_lump.h"
41
+#include "../../data_lump_rpl.h"
42
+#include "../../mod_fix.h"
43
+#include "../../parser/parse_uri.h"
44
+#include "../../modules_k/xcap_client/xcap_callbacks.h"
45
+#include "../../modules_k/sl/sl_api.h"
46
+
47
+MODULE_VERSION
48
+
49
+#define XCAP_TABLE_VERSION   3
50
+
51
+
52
+static int w_xcaps_put(sip_msg_t* msg, char* puri, char* ppath,
53
+		char* pbody);
54
+static int w_xcaps_get(sip_msg_t* msg, char* puri, char* ppath);
55
+static int w_xcaps_del(sip_msg_t* msg, char* puri, char* ppath);
56
+static int fixup_xcaps_put(void** param, int param_no);
57
+
58
+static int mod_init(void);
59
+static int child_init(int rank);
60
+static void destroy(void);
61
+
62
+int xcaps_get_auid_type(str *path);
63
+int xcaps_generate_etag_hdr(str *etag);
64
+
65
+static str xcaps_db_table = str_init("xcap");
66
+static str xcaps_db_url = str_init(DEFAULT_DB_URL);
67
+static str xcaps_root = str_init("/xcap-root/");
68
+static int xcaps_init_time = 0;
69
+static int xcaps_etag_counter = 1;
70
+
71
+static str xcaps_buf = {0, 1024};
72
+#define XCAPS_ETAG_SIZE	128
73
+static char xcaps_etag_buf[XCAPS_ETAG_SIZE];
74
+
75
+static str str_source_col = str_init("source");
76
+static str str_doc_col = str_init("doc");
77
+static str str_etag_col = str_init("etag");
78
+static str str_username_col = str_init("username");
79
+static str str_domain_col = str_init("domain");
80
+static str str_doc_type_col = str_init("doc_type");
81
+static str str_doc_uri_col = str_init("doc_uri");
82
+static str str_port_col = str_init("port");
83
+
84
+
85
+/* database connection */
86
+db1_con_t *xcaps_db = NULL;
87
+db_func_t xcaps_dbf;
88
+
89
+/** SL binds */
90
+struct sl_binds slb;
91
+
92
+static param_export_t params[] = {
93
+	{ "db_url",		STR_PARAM, &xcaps_db_url.s    },
94
+	{ "xcap_table",	STR_PARAM, &xcaps_db_table.s  },
95
+	{ "xcap_root",	STR_PARAM, &xcaps_root.s  },
96
+	{ "buf_size",	INT_PARAM, &xcaps_buf.len  },
97
+	{ 0, 0, 0 }
98
+};
99
+
100
+static cmd_export_t cmds[]={
101
+	{"xcaps_put",   (cmd_function)w_xcaps_put,   3,
102
+			fixup_xcaps_put,  0, REQUEST_ROUTE},
103
+	{"xcaps_get",   (cmd_function)w_xcaps_get,   2,
104
+			fixup_xcaps_put,  0, REQUEST_ROUTE},
105
+	{"xcaps_del",   (cmd_function)w_xcaps_del,   2,
106
+			fixup_xcaps_put,  0, REQUEST_ROUTE},
107
+	{0,0,0,0,0,0}
108
+};
109
+
110
+
111
+/** module exports */
112
+struct module_exports exports= {
113
+	"xcap_server",				/* module name */
114
+	DEFAULT_DLFLAGS,			/* dlopen flags */
115
+	cmds,  						/* exported functions */
116
+	params,						/* exported parameters */
117
+	0,      					/* exported statistics */
118
+	0,							/* exported MI functions */
119
+	0,							/* exported pseudo-variables */
120
+	0,							/* extra processes */
121
+	mod_init,					/* module initialization function */
122
+	0,							/* response handling function */
123
+	destroy,                    /* destroy function */
124
+	child_init					/* per-child init function */
125
+};
126
+
127
+/**
128
+ * init module function
129
+ */
130
+static int mod_init(void)
131
+{
132
+
133
+	xcaps_db_url.len   = (xcaps_db_url.s) ? strlen(xcaps_db_url.s) : 0;
134
+	xcaps_db_table.len = (xcaps_db_table.s) ? strlen(xcaps_db_table.s) : 0;
135
+	xcaps_root.len     = (xcaps_root.s) ? strlen(xcaps_root.s) : 0;
136
+	
137
+	if(xcaps_buf.len<=0)
138
+	{
139
+		LM_ERR("invalid buffer size\n");
140
+		return -1;
141
+	}
142
+
143
+	xcaps_buf.s = (char*)pkg_malloc(xcaps_buf.len+1);
144
+	if(xcaps_buf.s==NULL)
145
+	{
146
+		LM_ERR("no pkg\n");
147
+		return -1;
148
+	}
149
+
150
+	/* binding to mysql module  */
151
+	if (db_bind_mod(&xcaps_db_url, &xcaps_dbf))
152
+	{
153
+		LM_ERR("Database module not found\n");
154
+		return -1;
155
+	}
156
+	
157
+	if (!DB_CAPABILITY(xcaps_dbf, DB_CAP_ALL)) {
158
+		LM_ERR("Database module does not implement all functions"
159
+				" needed by the module\n");
160
+		return -1;
161
+	}
162
+
163
+	xcaps_db = xcaps_dbf.init(&xcaps_db_url);
164
+	if (xcaps_db==NULL)
165
+	{
166
+		LM_ERR("connecting to database\n");
167
+		return -1;
168
+	}
169
+
170
+	if(db_check_table_version(&xcaps_dbf, xcaps_db, &xcaps_db_table,
171
+				XCAP_TABLE_VERSION) < 0) {
172
+		LM_ERR("error during table version check.\n");
173
+		return -1;
174
+	}
175
+	xcaps_dbf.close(xcaps_db);
176
+	xcaps_db = NULL;
177
+
178
+	if (load_sl_api(&slb)!=0)
179
+	{
180
+		LM_ERR("can't load SL API\n");
181
+		return -1;
182
+	}
183
+
184
+	xcaps_init_time = (int)time(NULL);
185
+	return 0;
186
+}
187
+
188
+/**
189
+ *
190
+ */
191
+static int child_init(int rank)
192
+{
193
+	if (rank==PROC_INIT || rank==PROC_MAIN || rank==PROC_TCP_MAIN)
194
+		return 0; /* do nothing for the main process */
195
+
196
+	if((xcaps_db = xcaps_dbf.init(&xcaps_db_url))==NULL)
197
+	{
198
+		LM_ERR("cannot connect to db\n");
199
+		return -1;
200
+	}
201
+	return 0;
202
+}
203
+
204
+/**
205
+ *
206
+ */
207
+static void destroy(void)
208
+{
209
+	if(xcaps_db != NULL)
210
+		xcaps_dbf.close(xcaps_db);
211
+}
212
+
213
+
214
+/**
215
+ *
216
+ */
217
+static int xcaps_send_reply(sip_msg_t *msg, int code, str *reason,
218
+		str *hdrs, str *ctype, str *body)
219
+{
220
+	str tbuf;
221
+
222
+	if(ctype->len>0)
223
+	{
224
+		/* add content-type */
225
+		tbuf.len=sizeof("Content-Type: ") - 1 + ctype->len + CRLF_LEN;
226
+		tbuf.s=pkg_malloc(sizeof(char)*(tbuf.len));
227
+
228
+		if (tbuf.len==0)
229
+		{
230
+			LM_ERR("out of pkg memory\n");
231
+			return -1;
232
+		}
233
+		memcpy(tbuf.s, "Content-Type: ", sizeof("Content-Type: ") - 1);
234
+		memcpy(tbuf.s+sizeof("Content-Type: ") - 1, ctype->s, ctype->len);
235
+		memcpy(tbuf.s+sizeof("Content-Type: ") - 1 + ctype->len,
236
+				CRLF, CRLF_LEN);
237
+		if (add_lump_rpl(msg, tbuf.s, tbuf.len, LUMP_RPL_HDR) == 0)
238
+		{
239
+			LM_ERR("failed to insert content-type lump\n");
240
+			pkg_free(tbuf.s);
241
+			return -1;
242
+		}
243
+		pkg_free(tbuf.s);
244
+	}
245
+	if(body->len>0)
246
+	{
247
+		if (add_lump_rpl(msg, body->s, body->len, LUMP_RPL_BODY) < 0)
248
+		{
249
+			LM_ERR("Error while adding reply lump\n");
250
+			return -1;
251
+		}
252
+	}
253
+	if (slb.send_reply(msg, code, reason) < 0)
254
+	{
255
+		LM_ERR("Error while sending reply\n");
256
+		return -1;
257
+	}
258
+	return 0;
259
+}
260
+
261
+
262
+/**
263
+ *
264
+ */
265
+static int xcaps_put_db(str* user, str *domain, str* path, str *etag,
266
+		int dtype, str* doc)
267
+{
268
+	db_key_t qcols[9];
269
+	db_val_t qvals[9];
270
+	int ncols = 0;
271
+
272
+	/* insert in xcap table*/
273
+	qcols[ncols] = &str_username_col;
274
+	qvals[ncols].type = DB1_STR;
275
+	qvals[ncols].nul = 0;
276
+	qvals[ncols].val.str_val = *user;
277
+	ncols++;
278
+	
279
+	qcols[ncols] = &str_domain_col;
280
+	qvals[ncols].type = DB1_STR;
281
+	qvals[ncols].nul = 0;
282
+	qvals[ncols].val.str_val = *domain;
283
+	ncols++;
284
+	
285
+	qcols[ncols] = &str_doc_type_col;
286
+	qvals[ncols].type = DB1_INT;
287
+	qvals[ncols].nul = 0;
288
+	qvals[ncols].val.int_val= dtype;
289
+	ncols++;
290
+
291
+	qcols[ncols] = &str_doc_col;
292
+	qvals[ncols].type = DB1_STR;
293
+	qvals[ncols].nul = 0;
294
+	qvals[ncols].val.str_val= *doc;
295
+	ncols++;
296
+
297
+	qcols[ncols] = &str_etag_col;
298
+	qvals[ncols].type = DB1_STRING;
299
+	qvals[ncols].nul = 0;
300
+	qvals[ncols].val.str_val= *etag;
301
+	ncols++;
302
+
303
+	qcols[ncols] = &str_source_col;
304
+	qvals[ncols].type = DB1_INT;
305
+	qvals[ncols].nul = 0;
306
+	qvals[ncols].val.int_val = 0;
307
+	ncols++;
308
+
309
+	qcols[ncols] = &str_doc_uri_col;
310
+	qvals[ncols].type = DB1_STR;
311
+	qvals[ncols].nul = 0;
312
+	qvals[ncols].val.str_val= *path;
313
+	ncols++;
314
+
315
+	qcols[ncols] = &str_port_col;
316
+	qvals[ncols].type = DB1_INT;
317
+	qvals[ncols].nul = 0;
318
+	qvals[ncols].val.int_val= 0;
319
+	ncols++;
320
+
321
+	if (xcaps_dbf.use_table(xcaps_db, &xcaps_db_table) < 0) 
322
+	{
323
+		LM_ERR("in use_table-[table]= %.*s\n", xcaps_db_table.len,
324
+				xcaps_db_table.s);
325
+		goto error;
326
+	}
327
+	
328
+	if(xcaps_dbf.insert(xcaps_db, qcols, qvals, ncols)< 0)
329
+	{
330
+		LM_ERR("in sql insert\n");
331
+		goto error;
332
+	}
333
+
334
+	return 0;
335
+error:
336
+	return -1;
337
+}
338
+
339
+static str xcaps_str_empty    = {"", 0};
340
+static str xcaps_str_ok       = {"OK", 2};
341
+static str xcaps_str_srverr   = {"Server error", 12};
342
+static str xcaps_str_notfound = {"Not found", 9};
343
+static str xcaps_str_appxml   = {"application/xml", 15};
344
+
345
+/**
346
+ *
347
+ */
348
+static int w_xcaps_put(sip_msg_t* msg, char* puri, char* ppath,
349
+		char* pbody)
350
+{
351
+	int dtype;
352
+	struct sip_uri turi;
353
+	str uri;
354
+	str path;
355
+	str body;
356
+	str etag;
357
+	str etag_hdr;
358
+	pv_elem_t *xm;
359
+
360
+	if(puri==0 || ppath==0 || pbody==0)
361
+	{
362
+		LM_ERR("invalid parameters\n");
363
+		goto error;
364
+	}
365
+
366
+	if(fixup_get_svalue(msg, (gparam_p)puri, &uri)!=0)
367
+	{
368
+		LM_ERR("unable to get uri\n");
369
+		goto error;
370
+	}
371
+	if(uri.s==NULL || uri.len == 0)
372
+	{
373
+		LM_ERR("invalid uri parameter\n");
374
+		goto error;
375
+	}
376
+
377
+	if(fixup_get_svalue(msg, (gparam_p)ppath, &path)!=0)
378
+	{
379
+		LM_ERR("unable to get path\n");
380
+		goto error;
381
+	}
382
+	if(path.s==NULL || path.len == 0)
383
+	{
384
+		LM_ERR("invalid path parameter\n");
385
+		goto error;
386
+	}
387
+
388
+	xm = (pv_elem_t*)pbody;
389
+	body.len = xcaps_buf.len - 1;
390
+	body.s   = xcaps_buf.s;
391
+	if(pv_printf(msg, xm, body.s, &body.len)<0)
392
+	{
393
+		LM_ERR("unable to get body\n");
394
+		goto error;
395
+	}
396
+	if(body.s==NULL || body.len == 0)
397
+	{
398
+		LM_ERR("invalid body parameter\n");
399
+		goto error;
400
+	}
401
+	/* TODO: do xml parsing for validation */
402
+
403
+	if(parse_uri(uri.s, uri.len, &turi)!=0)
404
+	{
405
+		LM_ERR("parsing uri parameter\n");
406
+		goto error;
407
+	}
408
+
409
+	dtype = xcaps_get_auid_type(&path);
410
+
411
+	if(dtype==-1)
412
+	{
413
+		LM_ERR("unknown documet type in [%.*s]\n",
414
+				path.len, path.s);
415
+		goto error;
416
+	}
417
+
418
+	if(xcaps_generate_etag_hdr(&etag_hdr)<0)
419
+	{
420
+		LM_ERR("could not generate etag\n");
421
+		goto error;
422
+	}
423
+	etag.s = etag_hdr.s + 10; /* 'SIP-ETag: ' */
424
+	etag.len = etag_hdr.len - 12;
425
+	if(xcaps_put_db(&turi.user, &turi.host, &path, &etag, dtype, &body)<0)
426
+	{
427
+		LM_ERR("could not store document\n");
428
+		goto error;
429
+	}
430
+	xcaps_send_reply(msg, 200, &xcaps_str_ok, &etag_hdr,
431
+				&xcaps_str_empty, &xcaps_str_empty);
432
+	return 1;
433
+
434
+error:
435
+	xcaps_send_reply(msg, 500, &xcaps_str_srverr, &xcaps_str_empty,
436
+				&xcaps_str_empty, &xcaps_str_empty);
437
+	return -1;
438
+}
439
+
440
+/**
441
+ *
442
+ */
443
+static int xcaps_get_db(str* user, str *domain, str* path,
444
+		str *etag, str *doc)
445
+{
446
+	db_key_t qcols[4];
447
+	db_val_t qvals[4];
448
+	int ncols = 0;
449
+	db_key_t rcols[4];
450
+	int nrcols = 0;
451
+	db1_res_t* db_res = NULL;
452
+	str s;
453
+
454
+	/* returned cols from xcap table*/
455
+	rcols[nrcols] = &str_etag_col;
456
+	nrcols++;
457
+	rcols[nrcols] = &str_doc_col;
458
+	nrcols++;
459
+	
460
+	/* query cols in xcap table*/
461
+	qcols[ncols] = &str_username_col;
462
+	qvals[ncols].type = DB1_STR;
463
+	qvals[ncols].nul = 0;
464
+	qvals[ncols].val.str_val = *user;
465
+	ncols++;
466
+	
467
+	qcols[ncols] = &str_domain_col;
468
+	qvals[ncols].type = DB1_STR;
469
+	qvals[ncols].nul = 0;
470
+	qvals[ncols].val.str_val = *domain;
471
+	ncols++;
472
+	
473
+	qcols[ncols] = &str_doc_uri_col;
474
+	qvals[ncols].type = DB1_STR;
475
+	qvals[ncols].nul = 0;
476
+	qvals[ncols].val.str_val= *path;
477
+	ncols++;
478
+
479
+	if (xcaps_dbf.use_table(xcaps_db, &xcaps_db_table) < 0) 
480
+	{
481
+		LM_ERR("in use_table-[table]= %.*s\n", xcaps_db_table.len,
482
+				xcaps_db_table.s);
483
+		goto error;
484
+	}
485
+	
486
+	if(xcaps_dbf.query(xcaps_db, qcols, NULL, qvals, rcols,
487
+				ncols, nrcols, NULL, &db_res)< 0)
488
+	{
489
+		LM_ERR("in sql query\n");
490
+		goto error;
491
+	}
492
+	if (RES_ROW_N(db_res) <= 0)
493
+	{
494
+		LM_DBG("no document\n");
495
+		goto notfound;
496
+	}
497
+	/* etag */
498
+	switch(RES_ROWS(db_res)[0].values[0].type)
499
+	{
500
+		case DB1_STRING:
501
+			s.s=(char*)RES_ROWS(db_res)[0].values[0].val.string_val;
502
+			s.len=strlen(s.s);
503
+		break;
504
+		case DB1_STR:
505
+			s.len=RES_ROWS(db_res)[0].values[0].val.str_val.len;
506
+			s.s=(char*)RES_ROWS(db_res)[0].values[0].val.str_val.s;
507
+		break;
508
+		case DB1_BLOB:
509
+			s.len=RES_ROWS(db_res)[0].values[0].val.blob_val.len;
510
+			s.s=(char*)RES_ROWS(db_res)[0].values[0].val.blob_val.s;
511
+		break;
512
+		default:
513
+			s.len=0;
514
+			s.s=NULL;
515
+	}
516
+	if(s.len==0)
517
+	{
518
+		LM_ERR("no etag in db record\n");
519
+		goto error;
520
+	}
521
+	etag->len = snprintf(xcaps_etag_buf, XCAPS_ETAG_SIZE,
522
+			"SIP-ETag: %.*s\r\n", s.len, s.s);
523
+	if(etag->len < 0)
524
+	{
525
+		LM_ERR("error printing etag hdr\n ");
526
+		goto error;
527
+	}
528
+	if(etag->len >= XCAPS_ETAG_SIZE)
529
+	{
530
+		LM_ERR("etag buffer overflow\n");
531
+		goto error;
532
+	}
533
+
534
+	etag->s = xcaps_etag_buf;
535
+	etag->s[etag->len] = '\0';
536
+
537
+	/* doc */
538
+	switch(RES_ROWS(db_res)[0].values[1].type)
539
+	{
540
+		case DB1_STRING:
541
+			s.s=(char*)RES_ROWS(db_res)[0].values[1].val.string_val;
542
+			s.len=strlen(s.s);
543
+		break;
544
+		case DB1_STR:
545
+			s.len=RES_ROWS(db_res)[0].values[1].val.str_val.len;
546
+			s.s=(char*)RES_ROWS(db_res)[0].values[1].val.str_val.s;
547
+		break;
548
+		case DB1_BLOB:
549
+			s.len=RES_ROWS(db_res)[0].values[1].val.blob_val.len;
550
+			s.s=(char*)RES_ROWS(db_res)[0].values[1].val.blob_val.s;
551
+		break;
552
+		default:
553
+			s.len=0;
554
+			s.s=NULL;
555
+	}
556
+	if(s.len==0)
557
+	{
558
+		LM_ERR("no xcap doc in db record\n");
559
+		goto error;
560
+	}
561
+	if(s.len>xcaps_buf.len-1)
562
+	{
563
+		LM_ERR("xcap doc buffer overflow\n");
564
+		goto error;
565
+	}
566
+	doc->len = s.len;
567
+	doc->s = xcaps_buf.s;
568
+	memcpy(doc->s, s.s, s.len);
569
+	doc->s[doc->len] = '\0';
570
+
571
+	xcaps_dbf.free_result(xcaps_db, db_res);
572
+	return 0;
573
+
574
+notfound:
575
+	xcaps_dbf.free_result(xcaps_db, db_res);
576
+	return 1;
577
+
578
+error:
579
+	if(db_res!=NULL)
580
+		xcaps_dbf.free_result(xcaps_db, db_res);
581
+	return -1;
582
+}
583
+
584
+
585
+/**
586
+ *
587
+ */
588
+static int w_xcaps_get(sip_msg_t* msg, char* puri, char* ppath)
589
+{
590
+	struct sip_uri turi;
591
+	str uri;
592
+	str path;
593
+	str etag;
594
+	str body;
595
+	int ret = 0;
596
+
597
+	if(puri==0 || ppath==0)
598
+	{
599
+		LM_ERR("invalid parameters\n");
600
+		return -1;
601
+	}
602
+
603
+	if(fixup_get_svalue(msg, (gparam_p)puri, &uri)!=0)
604
+	{
605
+		LM_ERR("unable to get uri\n");
606
+		return -1;
607
+	}
608
+	if(uri.s==NULL || uri.len == 0)
609
+	{
610
+		LM_ERR("invalid uri parameter\n");
611
+		return -1;
612
+	}
613
+
614
+	if(fixup_get_svalue(msg, (gparam_p)ppath, &path)!=0)
615
+	{
616
+		LM_ERR("unable to get path\n");
617
+		return -1;
618
+	}
619
+	if(path.s==NULL || path.len == 0)
620
+	{
621
+		LM_ERR("invalid path parameter\n");
622
+		return -1;
623
+	}
624
+
625
+	if(parse_uri(uri.s, uri.len, &turi)!=0)
626
+	{
627
+		LM_ERR("parsing uri parameter\n");
628
+		goto error;
629
+	}
630
+
631
+	if((ret=xcaps_get_db(&turi.user, &turi.host, &path, &etag, &body))<0)
632
+	{
633
+		LM_ERR("could not fetch xcap document\n");
634
+		goto error;
635
+	}
636
+	if(ret==0)
637
+	{
638
+		/* doc found */
639
+		xcaps_send_reply(msg, 200, &xcaps_str_ok, &etag,
640
+				&xcaps_str_appxml, &body);
641
+	} else {
642
+		/* doc not found */
643
+		xcaps_send_reply(msg, 404, &xcaps_str_notfound, &xcaps_str_empty,
644
+				&xcaps_str_empty, &xcaps_str_empty);
645
+	}
646
+	return 1;
647
+
648
+error:
649
+	xcaps_send_reply(msg, 500, &xcaps_str_srverr, &xcaps_str_empty,
650
+				&xcaps_str_empty, &xcaps_str_empty);
651
+	return -1;
652
+}
653
+
654
+
655
+/**
656
+ *
657
+ */
658
+static int xcaps_del_db(str* user, str *domain, str* path)
659
+{
660
+	db_key_t qcols[4];
661
+	db_val_t qvals[4];
662
+	int ncols = 0;
663
+
664
+	/* delete in xcap table*/
665
+	qcols[ncols] = &str_username_col;
666
+	qvals[ncols].type = DB1_STR;
667
+	qvals[ncols].nul = 0;
668
+	qvals[ncols].val.str_val = *user;
669
+	ncols++;
670
+	
671
+	qcols[ncols] = &str_domain_col;
672
+	qvals[ncols].type = DB1_STR;
673
+	qvals[ncols].nul = 0;
674