Browse code

xcap_server: parser for xcap uri

- easier to get xcap uri attributes for doc and xpath selectors

Daniel-Constantin Mierla authored on 31/08/2010 21:20:00
Showing 2 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,551 @@
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 <libxml/xpath.h>
29
+#include <libxml/xpathInternals.h>
30
+
31
+#include "../../dprint.h"
32
+#include "../../sr_module.h"
33
+#include "../../mem/mem.h"
34
+#include "../../parser/parse_param.h"
35
+#include "../../modules_k/xcap_client/xcap_callbacks.h"
36
+
37
+#include "xcap_misc.h"
38
+
39
+static param_t *_xcaps_xpath_ns_root = NULL;
40
+
41
+int xcap_parse_uri(str *huri, str *xroot, xcap_uri_t *xuri)
42
+{
43
+	str s;
44
+	char *p;
45
+	int i;
46
+
47
+	if(huri==NULL || xuri==NULL || huri->s==NULL)
48
+	{
49
+		LM_ERR("invalid parameters\n");
50
+		return -1;
51
+	}
52
+
53
+	if(huri->len>XCAP_MAX_URI_SIZE)
54
+	{
55
+		LM_ERR("http uri too long\n");
56
+		return -1;
57
+	}
58
+
59
+	memset(xuri, 0, sizeof(xcap_uri_t));
60
+
61
+	/* copy and url decode */
62
+	for(p=huri->s, i=0; p<huri->s+huri->len; p++)
63
+	{
64
+		if(*p=='%') {
65
+			if(p > huri->s+huri->len-2)
66
+			{
67
+				LM_ERR("invalid http uri - hexa value too short\n");
68
+				return -1;
69
+			}
70
+			p++;
71
+			if(*p>='0' && *p<='9')
72
+				xuri->buf[i] = (*p - '0') << 4;
73
+			else if(*p>='a'&&*p<='f')
74
+				xuri->buf[i] = (*p-'a'+10) << 4;
75
+			else if(*p>='A'&&*p<='F')
76
+				xuri->buf[i] = (*p-'A'+10) << 4;
77
+			else return -1;
78
+			p++;
79
+			if(*p>='0'&&*p<='9')
80
+				xuri->buf[i] += *p-'0';
81
+			else if(*p>='a'&&*p<='f')
82
+				xuri->buf[i] += *p-'a'+10;
83
+			else if(*p>='A'&&*p<='F')
84
+				xuri->buf[i] += *p-'A'+10;
85
+			else return -1;
86
+		} else {
87
+			xuri->buf[i] = *p;
88
+		}
89
+		i++;
90
+	}
91
+	xuri->uri.s = xuri->buf;
92
+	xuri->uri.len = i;
93
+	xuri->uri.s[xuri->uri.len] = '\0';
94
+
95
+	xuri->nss = strstr(xuri->uri.s, XCAP_NSS);
96
+
97
+	xuri->root.s = xuri->uri.s;
98
+	s = xuri->uri;
99
+
100
+	if(xroot!=NULL)
101
+	{
102
+		if(xroot->len >= xuri->uri.len)
103
+		{
104
+			LM_ERR("invalid http uri - shorter than xcap-root\n");
105
+			return -1;
106
+		}
107
+		if(strncmp(xuri->uri.s, xroot->s, xroot->len)!=0)
108
+		{
109
+			LM_ERR("missing xcap-root in [%.*s]\n", xuri->uri.len,
110
+					xuri->uri.s);
111
+			return -1;
112
+		}
113
+
114
+		s.s   = xuri->uri.s + xroot->len;
115
+		s.len = xuri->uri.len - xroot->len;
116
+		xuri->root.len = xroot->len;
117
+	}
118
+	if(*s.s == '/')
119
+	{
120
+		 s.s++;
121
+		 s.len--;
122
+	}
123
+
124
+	/* auid */
125
+	xuri->auid.s = s.s;
126
+	if(s.len>11 && strncmp(s.s, "pres-rules/", 11)==0)
127
+	{
128
+		LM_DBG("matched pres-rules\n");
129
+		xuri->type = PRES_RULES;
130
+		xuri->auid.len = 10;
131
+	} else if(s.len>13 && strncmp(s.s, "rls-services/", 13)==0) {
132
+		LM_DBG("matched rls-services\n");
133
+		xuri->type = RLS_SERVICE;
134
+		xuri->auid.len = 12;
135
+	} else if(s.len>18 && strncmp(s.s, "pidf-manipulation/", 18)==0) {
136
+		LM_DBG("matched pidf-manipulation\n");
137
+		xuri->type = PIDF_MANIPULATION;
138
+		xuri->auid.len = 17;
139
+	} else if(s.len>15 && strncmp(s.s, "resource-lists/", 15)==0) {
140
+		LM_DBG("matched resource-lists\n");
141
+		xuri->type = RESOURCE_LIST;
142
+		xuri->auid.len = 14;
143
+	} else {
144
+		LM_ERR("unsupported auid in [%.*s]\n", xuri->uri.len,
145
+				xuri->uri.s);
146
+		return -1;
147
+	}
148
+
149
+	s.s   += xuri->auid.len + 1;
150
+	s.len -= xuri->auid.len + 1;
151
+
152
+	/* tree: users or global */
153
+	xuri->tree.s = s.s;
154
+	if(s.len>6 && strncmp(s.s, "users/", 6)==0) {
155
+		LM_DBG("matched users\n");
156
+		xuri->tree.len = 5;
157
+	} else if(s.len>7 && strncmp(s.s, "global/", 7)==0) {
158
+		LM_DBG("matched global\n");
159
+		xuri->tree.len = 6;
160
+	} else {
161
+		LM_ERR("unsupported sub-tree in [%.*s]\n", xuri->uri.len,
162
+				xuri->uri.s);
163
+		return -1;
164
+	}
165
+
166
+	s.s   += xuri->tree.len + 1;
167
+	s.len -= xuri->tree.len + 1;
168
+
169
+	/* xuid */
170
+	if(xuri->tree.s[0]=='u') {
171
+		xuri->xuid.s = s.s;
172
+		p = strchr(s.s, '/');
173
+		if(p==NULL) {
174
+			LM_ERR("no xuid in [%.*s]\n", xuri->uri.len,
175
+					xuri->uri.s);
176
+			return -1;
177
+		}
178
+		xuri->xuid.len = p - xuri->xuid.s;
179
+
180
+		s.s   += xuri->xuid.len + 1;
181
+		s.len -= xuri->xuid.len + 1;
182
+	}
183
+
184
+	/* file */
185
+	xuri->file.s = s.s;
186
+	if(xuri->nss==NULL) {
187
+		/* without node selector in http uri */
188
+		if(s.s[s.len-1]=='/')
189
+			xuri->file.len = s.len - 1;
190
+		else
191
+			xuri->file.len = s.len;
192
+	} else {
193
+		/* with node selector in http uri */
194
+		if(xuri->nss <= s.s) {
195
+			LM_ERR("no file in [%.*s]\n", xuri->uri.len,
196
+				xuri->uri.s);
197
+			return -1;
198
+		}
199
+		xuri->file.len = xuri->nss - s.s;
200
+		if(xuri->file.s[xuri->file.len-1]=='/')
201
+			xuri->file.len--;
202
+	}
203
+	/* doc: aboslute and relative */
204
+	xuri->adoc.s   = xuri->uri.s;
205
+	xuri->adoc.len = xuri->file.s + xuri->file.len - xuri->adoc.s;
206
+	xuri->rdoc.s   = xuri->auid.s;
207
+	xuri->rdoc.len = xuri->file.s + xuri->file.len - xuri->rdoc.s;
208
+
209
+	/* node */
210
+	if(xuri->nss!=NULL) {
211
+		xuri->node.s   = xuri->nss + 2;
212
+		xuri->node.len = xuri->uri.s + xuri->uri.len - xuri->node.s;
213
+	}
214
+
215
+	LM_DBG("----- uri: [%.*s]\n", xuri->uri.len, xuri->uri.s);
216
+	LM_DBG("----- root: [%.*s]\n", xuri->root.len, xuri->root.s);
217
+	LM_DBG("----- auid: [%.*s] (%d)\n", xuri->auid.len, xuri->auid.s,
218
+			xuri->type);
219
+	LM_DBG("----- tree: [%.*s]\n", xuri->tree.len, xuri->tree.s);
220
+	if(xuri->tree.s[0]=='u')
221
+		LM_DBG("----- xuid: [%.*s]\n", xuri->xuid.len, xuri->xuid.s);
222
+	LM_DBG("----- file: [%.*s]\n", xuri->file.len, xuri->file.s);
223
+	LM_DBG("----- adoc: [%.*s]\n", xuri->adoc.len, xuri->adoc.s);
224
+	LM_DBG("----- rdoc: [%.*s]\n", xuri->rdoc.len, xuri->rdoc.s);
225
+	if(xuri->nss!=NULL)
226
+		LM_DBG("----- node: [%.*s]\n", xuri->node.len, xuri->node.s);
227
+	return 0;
228
+}
229
+
230
+int xcaps_xpath_get(str *inbuf, str *xpaths, str *outbuf)
231
+{
232
+	xmlDocPtr doc = NULL;
233
+	xmlXPathContextPtr xpathCtx = NULL; 
234
+	xmlXPathObjectPtr xpathObj = NULL; 
235
+	xmlNodeSetPtr nodes;
236
+	xmlChar *keyword;
237
+	xmlBufferPtr psBuf;
238
+	int size;
239
+	int i;
240
+	char *p;
241
+	char *end;
242
+	char *pos;
243
+
244
+	doc = xmlParseMemory(inbuf->s, inbuf->len);
245
+	if(doc == NULL)
246
+		return -1;
247
+
248
+	xpathCtx = xmlXPathNewContext(doc);
249
+	if(xpathCtx == NULL)
250
+	{
251
+		LM_ERR("unable to create new XPath context\n");
252
+		goto error;
253
+	}
254
+	
255
+	/* Evaluate xpath expression */
256
+	// xcaps_xpath_register_ns(xpathCtx);
257
+	xpathObj = xmlXPathEvalExpression(
258
+					(const xmlChar*)xpaths->s, xpathCtx);
259
+	if(xpathObj == NULL)
260
+	{
261
+		LM_ERR("unable to evaluate xpath expression [%s]\n", xpaths->s);
262
+		goto error;
263
+	}
264
+    nodes = xpathObj->nodesetval;
265
+	if(nodes==NULL)
266
+	{
267
+		outbuf->len = 0;
268
+		outbuf->s[outbuf->len] = '\0';
269
+		goto done;
270
+	}
271
+	size = nodes->nodeNr;
272
+    p = outbuf->s;
273
+	end = outbuf->s + outbuf->len;
274
+	for(i = 0; i < size; ++i)
275
+	{
276
+		if(nodes->nodeTab[i]==NULL)
277
+			continue;
278
+		if(i!=0)
279
+		{
280
+			if(p>=end)
281
+			{
282
+				LM_ERR("output buffer overflow\n");
283
+				goto error;
284
+			}
285
+			*p = ',';
286
+			p++;
287
+		}
288
+		if(nodes->nodeTab[i]->type == XML_ATTRIBUTE_NODE)
289
+		{
290
+			keyword = xmlNodeListGetString(doc,
291
+				nodes->nodeTab[i]->children, 0);
292
+			if(keyword != NULL)
293
+			{
294
+				pos = p + strlen((char*)keyword);
295
+				if(pos>=end)
296
+				{
297
+					LM_ERR("output buffer overflow\n");
298
+					goto error;
299
+				}
300
+				strcpy(p, (char*)keyword);
301
+				p = pos;
302
+				xmlFree(keyword);
303
+				keyword = NULL;
304
+			}
305
+		} else {
306
+			if(nodes->nodeTab[i]->content!=NULL)
307
+			{
308
+				pos = p + strlen((char*)nodes->nodeTab[i]->content);
309
+				if(pos>=end)
310
+				{
311
+					LM_ERR("output buffer overflow\n");
312
+					goto error;
313
+				}
314
+				strcpy(p, (char*)nodes->nodeTab[i]->content);
315
+				p = pos;
316
+			} else {
317
+				psBuf = xmlBufferCreate();
318
+				if(psBuf != NULL && xmlNodeDump(psBuf, doc,
319
+						nodes->nodeTab[i], 0, 0)>0)
320
+				{
321
+					pos = p + strlen((char*)xmlBufferContent(psBuf));
322
+					if(pos>=end)
323
+					{
324
+						LM_ERR("output buffer overflow\n");
325
+						goto error;
326
+					}
327
+					strcpy(p, (char*)xmlBufferContent(psBuf));
328
+					p = pos;
329
+				}
330
+				if(psBuf != NULL) xmlBufferFree(psBuf);
331
+				psBuf = NULL;
332
+			}
333
+		}
334
+	}
335
+	outbuf->len = p - outbuf->s;
336
+	outbuf->s[outbuf->len] = '\0';
337
+
338
+done:
339
+	if(xpathObj!=NULL) xmlXPathFreeObject(xpathObj);
340
+	if(xpathCtx!=NULL) xmlXPathFreeContext(xpathCtx); 
341
+	if(doc!=NULL) xmlFreeDoc(doc);
342
+	xpathObj = NULL;
343
+	xpathCtx = NULL; 
344
+	doc = NULL; 
345
+	return 0;
346
+
347
+error:
348
+	if(xpathObj!=NULL) xmlXPathFreeObject(xpathObj);
349
+	if(xpathCtx!=NULL) xmlXPathFreeContext(xpathCtx); 
350
+	if(doc!=NULL) xmlFreeDoc(doc);
351
+	xpathObj = NULL;
352
+	xpathCtx = NULL; 
353
+	doc = NULL; 
354
+	outbuf->len = 0;
355
+	outbuf->s[outbuf->len] = '\0';
356
+	return -1;
357
+}
358
+
359
+
360
+int xcaps_xpath_set(str *inbuf, str *xpaths, str *val, str *outbuf)
361
+{
362
+	xmlDocPtr doc = NULL;
363
+	xmlDocPtr newnode = NULL;
364
+	xmlXPathContextPtr xpathCtx = NULL; 
365
+	xmlXPathObjectPtr xpathObj = NULL; 
366
+	xmlNodeSetPtr nodes;
367
+	const xmlChar* value = NULL;
368
+	xmlChar *xmem = NULL;
369
+	xmlNodePtr parent = NULL;
370
+	int size;
371
+	int i;
372
+
373
+	doc = xmlParseMemory(inbuf->s, inbuf->len);
374
+	if(doc == NULL)
375
+		return -1;
376
+
377
+	if(val!=NULL)
378
+		newnode = xmlParseMemory(val->s, val->len);
379
+
380
+	outbuf->s   = NULL;
381
+	outbuf->len = 0;
382
+
383
+	xpathCtx = xmlXPathNewContext(doc);
384
+	if(xpathCtx == NULL)
385
+	{
386
+		LM_ERR("unable to create new XPath context\n");
387
+		goto error;
388
+	}
389
+	
390
+	/* Evaluate xpath expression */
391
+	xpathObj = xmlXPathEvalExpression(
392
+					(const xmlChar*)xpaths->s, xpathCtx);
393
+	if(xpathObj == NULL)
394
+	{
395
+		LM_ERR("unable to evaluate xpath expression [%s]\n", xpaths->s);
396
+		goto error;
397
+	}
398
+    nodes = xpathObj->nodesetval;
399
+	if(nodes==NULL)
400
+	{
401
+		LM_DBG("no selection for xpath expression [%s]\n", xpaths->s);
402
+		goto done;
403
+	}
404
+	size = nodes->nodeNr;
405
+	if(val!=NULL)
406
+		value = (const xmlChar*)val->s;
407
+    
408
+	/*
409
+	 * NOTE: the nodes are processed in reverse order, i.e. reverse document
410
+	 *       order because xmlNodeSetContent can actually free up descendant
411
+	 *       of the node and such nodes may have been selected too ! Handling
412
+	 *       in reverse order ensure that descendant are accessed first, before
413
+	 *       they get removed. Mixing XPath and modifications on a tree must be
414
+	 *       done carefully !
415
+	 */
416
+	for(i = size - 1; i >= 0; i--) {
417
+		if(nodes->nodeTab[i]==NULL)
418
+			continue;
419
+	
420
+		if(nodes->nodeTab[i]->type==XML_ELEMENT_NODE)
421
+		{
422
+			parent = nodes->nodeTab[i]->parent;
423
+			xmlUnlinkNode(nodes->nodeTab[i]);
424
+			if(val!=NULL && newnode!=NULL)
425
+				xmlAddChild(parent, xmlCopyNode(newnode->children, 1));
426
+		} else {
427
+			if(val!=NULL)
428
+				xmlNodeSetContent(nodes->nodeTab[i], value);
429
+			else
430
+				xmlNodeSetContent(nodes->nodeTab[i], (const xmlChar*)"");
431
+		}
432
+		/*
433
+		 * All the elements returned by an XPath query are pointers to
434
+		 * elements from the tree *except* namespace nodes where the XPath
435
+		 * semantic is different from the implementation in libxml2 tree.
436
+		 * As a result when a returned node set is freed when
437
+		 * xmlXPathFreeObject() is called, that routine must check the
438
+		 * element type. But node from the returned set may have been removed
439
+		 * by xmlNodeSetContent() resulting in access to freed data.
440
+		 * This can be exercised by running
441
+		 *       valgrind xpath2 test3.xml '//discarded' discarded
442
+		 * There is 2 ways around it:
443
+		 *   - make a copy of the pointers to the nodes from the result set 
444
+		 *     then call xmlXPathFreeObject() and then modify the nodes
445
+		 * or
446
+		 *   - remove the reference to the modified nodes from the node set
447
+		 *     as they are processed, if they are not namespace nodes.
448
+		 */
449
+		if (nodes->nodeTab[i]->type != XML_NAMESPACE_DECL)
450
+			nodes->nodeTab[i] = NULL;
451
+	}
452
+
453
+	xmlDocDumpMemory(doc, &xmem, &size);
454
+	if(xmem==NULL)
455
+	{
456
+		LM_ERR("error printing output\n");
457
+		goto error;
458
+	}
459
+
460
+	if(size<=0)
461
+	{
462
+		LM_ERR("invalid output size\n");
463
+		xmlFree(xmem);
464
+		goto error;
465
+	}
466
+	outbuf->s = (char*)pkg_malloc(size+1);
467
+	if(size<=0)
468
+	{
469
+		LM_ERR("no pkg for output\n");
470
+		xmlFree(xmem);
471
+		goto error;
472
+	}
473
+	memcpy(outbuf->s, xmem, size);
474
+	outbuf->s[size] = '\0';
475
+	outbuf->len = size;
476
+	xmlFree(xmem);
477
+
478
+done:
479
+	if(xpathObj!=NULL) xmlXPathFreeObject(xpathObj);
480
+	if(xpathCtx!=NULL) xmlXPathFreeContext(xpathCtx); 
481
+	if(doc!=NULL) xmlFreeDoc(doc);
482
+	if(newnode!=NULL) xmlFreeDoc(newnode);
483
+	xpathObj = NULL;
484
+	xpathCtx = NULL; 
485
+	doc = NULL; 
486
+	return 0;
487
+
488
+error:
489
+	if(xpathObj!=NULL) xmlXPathFreeObject(xpathObj);
490
+	if(xpathCtx!=NULL) xmlXPathFreeContext(xpathCtx); 
491
+	if(doc!=NULL) xmlFreeDoc(doc);
492
+	if(newnode!=NULL) xmlFreeDoc(newnode);
493
+	xpathObj = NULL;
494
+	xpathCtx = NULL; 
495
+	doc = NULL; 
496
+	outbuf->s =   NULL;
497
+	outbuf->len = 0;
498
+	return -1;
499
+}
500
+
501
+
502
+void xcaps_xpath_register_ns(xmlXPathContextPtr xpathCtx)
503
+{
504
+	param_t *ns;
505
+	ns = _xcaps_xpath_ns_root;
506
+	while(ns) {
507
+		xmlXPathRegisterNs(xpathCtx, (xmlChar*)ns->name.s,
508
+				(xmlChar*)ns->body.s);
509
+		ns = ns->next;
510
+	}
511
+}
512
+
513
+int xcaps_xpath_ns_param(modparam_t type, void *val)
514
+{
515
+	char *p;
516
+	param_t *ns;
517
+
518
+	if(val==NULL)
519
+		goto error;
520
+	ns = (param_t*)pkg_malloc(sizeof(param_t));
521
+
522
+	if(ns==NULL)
523
+	{
524
+		LM_ERR("no more pkg\n");
525
+		goto error;
526
+	}
527
+	memset(ns, 0, sizeof(param_t));
528
+
529
+	p = strchr((const char*)val, '=');
530
+	if(p==NULL)
531
+	{
532
+		ns->name.s = "";
533
+		ns->body.s = (char*)val;
534
+		ns->body.len = strlen(ns->body.s);
535
+	} else {
536
+		*p = 0;
537
+		p++;
538
+		ns->name.s = (char*)val;
539
+		ns->name.len = strlen(ns->name.s);
540
+		ns->body.s = p;
541
+		ns->body.len = strlen(ns->body.s);
542
+	}
543
+	ns->next = _xcaps_xpath_ns_root;
544
+	_xcaps_xpath_ns_root = ns;
545
+	return 0;
546
+error:
547
+	return -1;
548
+
549
+}
550
+
0 551
new file mode 100644
... ...
@@ -0,0 +1,53 @@
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
+ * openser 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
+ * openser 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
+#ifndef _XCAP_MISC_H_
25
+#define _XCAP_MISC_H_
26
+
27
+#include "../../str.h"
28
+
29
+#define XCAP_MAX_URI_SIZE	127
30
+/* Node Selector Separator */
31
+#define XCAP_NSS	"~~"
32
+
33
+typedef struct xcap_uri {
34
+	char buf[XCAP_MAX_URI_SIZE+1];
35
+	str uri;
36
+	str root;
37
+	str auid;
38
+	int type;
39
+	str tree;
40
+	str xuid;
41
+	str file;
42
+	str adoc;
43
+	str rdoc;
44
+	char *nss;
45
+	str node;
46
+} xcap_uri_t;
47
+
48
+int xcap_parse_uri(str *huri, str *xroot, xcap_uri_t *xuri);
49
+int xcaps_xpath_set(str *inbuf, str *xpaths, str *val, str *outbuf);
50
+int xcaps_xpath_get(str *inbuf, str *xpaths, str *outbuf);
51
+
52
+#endif