1 /*
2  * PIDF parser
3  *
4  * $Id$
5  *
6  * Copyright (C) 2005 iptelorg GmbH
7  *
8  * This file is part of ser, a free SIP server.
9  *
10  * ser is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version
14  *
15  * For a license to use the ser software under conditions
16  * other than those described here, or to purchase support for this
17  * software, please contact iptel.org by e-mail at the following addresses:
18  *    info@iptel.org
19  *
20  * ser is distributed in the hope that it will be useful,
21  * but WITHOUT ANY WARRANTY; without even the implied warranty of
22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23  * GNU General Public License for more details.
24  *
25  * You should have received a copy of the GNU General Public License
26  * along with this program; if not, write to the Free Software
27  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
28  */
29 
30 #include <presence/pidf.h>
31 #include <cds/dstring.h>
32 #include <cds/memory.h>
33 #include <cds/logger.h>
34 #include <cds/list.h>
35 #include <presence/xml_utils.h>
36 #include <string.h>
37 
38 /* ------------------------------ PIDF document creation ------------------------------ */
39 
doc_add_tuple_note(dstring_t * buf,presence_note_t * n)40 static void doc_add_tuple_note(dstring_t *buf, presence_note_t *n)
41 {
42 	DEBUG_LOG("doc_add_tuple_note()\n");
43 
44 	dstr_append_zt(buf, "\t\t<note");
45 	if (n->lang.len > 0) {
46 		dstr_append_zt(buf, " lang=\"");
47 		dstr_append_str(buf, &n->lang);
48 		dstr_append_zt(buf, "\"");
49 	}
50 	dstr_append_zt(buf, ">");
51 	dstr_append_str(buf, &n->value);
52 	dstr_append_zt(buf, "</note>\r\n");
53 }
54 
doc_add_extension(dstring_t * buf,extension_element_t * ex)55 static inline void doc_add_extension(dstring_t *buf, extension_element_t *ex)
56 {
57 	dstr_append_str(buf, &ex->element);
58 	dstr_append_zt(buf, "\r\n");
59 }
60 
doc_add_tuple(dstring_t * buf,presentity_info_t * p,presence_tuple_info_t * t)61 static void doc_add_tuple(dstring_t *buf, presentity_info_t *p, presence_tuple_info_t *t)
62 {
63 	presence_note_t *n;
64 	extension_element_t *e;
65 	char tmp[32];
66 
67 	DEBUG_LOG("doc_add_tuple()\n");
68 
69 	dstr_append_zt(buf, "\t<tuple id=\"");
70 	dstr_append_str(buf, &t->id);
71 	dstr_append_zt(buf, "\">\r\n");
72 
73 	dstr_append_zt(buf, "\t\t<status>\r\n");
74 	if (t->status.basic != presence_tuple_undefined_status) {
75 		/* do not add unknown status it is not mandatory in PIDF */
76 		dstr_append_zt(buf, "\t\t\t<basic>");
77 		dstr_append_str(buf, tuple_status2str(t->status.basic));
78 		dstr_append_zt(buf, "</basic>\r\n");
79 	}
80 	/* add extension status elements */
81 	e = t->status.first_unknown_element;
82 	while (e) {
83 		doc_add_extension(buf, e);
84 		e = e->next;
85 	}
86 	dstr_append_zt(buf, "\t\t</status>\r\n");
87 
88 	/* add extension elements */
89 	e = t->first_unknown_element;
90 	while (e) {
91 		doc_add_extension(buf, e);
92 		e = e->next;
93 	}
94 
95 	if (!is_str_empty(&t->contact)) {
96 		dstr_append_zt(buf, "\t\t<contact priority=\"");
97 		sprintf(tmp, "%1.2f", t->priority);
98 		dstr_append_zt(buf, tmp);
99 		dstr_append_zt(buf, "\">");
100 		dstr_append_str(buf, &t->contact);
101 		dstr_append_zt(buf, "</contact>\r\n");
102 	}
103 
104 	n = t->first_note;
105 	while (n) {
106 		doc_add_tuple_note(buf, n);
107 		n = n->next;
108 	}
109 
110 	dstr_append_zt(buf, "\t</tuple>\r\n");
111 }
112 
doc_add_empty_tuple(dstring_t * buf)113 static void doc_add_empty_tuple(dstring_t *buf)
114 {
115 	/* "empty" tuple is needed in PIDF by Microsoft Windows Messenger v. 5.1 and linphone 1.2) */
116 	DEBUG_LOG("doc_add_empty_tuple()\n");
117 
118 	dstr_append_zt(buf, "\t<tuple id=\"none\">\r\n");
119 	dstr_append_zt(buf, "\t\t<status><basic>closed</basic></status>\r\n");
120 
121 	dstr_append_zt(buf, "\t</tuple>\r\n");
122 }
123 
doc_add_note(dstring_t * buf,presentity_info_t * p,presence_note_t * n)124 static void doc_add_note(dstring_t *buf, presentity_info_t *p, presence_note_t *n)
125 {
126 	DEBUG_LOG("doc_add_note()\n");
127 
128 	dstr_append_zt(buf, "\t<note");
129 	if (n->lang.len > 0) {
130 		dstr_append_zt(buf, " lang=\"");
131 		dstr_append_str(buf, &n->lang);
132 		dstr_append_zt(buf, "\"");
133 	}
134 	dstr_append_zt(buf, ">");
135 	dstr_append_str(buf, &n->value);
136 	dstr_append_zt(buf, "</note>\r\n");
137 }
138 
dstr_put_pres_uri(dstring_t * buf,str_t * uri)139 static void dstr_put_pres_uri(dstring_t *buf, str_t *uri)
140 {
141 	char *c;
142 	int len = 0;
143 
144 	if (!uri) return;
145 
146 	c = str_strchr(uri, ':');
147 	if (c) {
148 		len = uri->len - (c - uri->s) - 1;
149 		if (len > 0) c++;
150 	}
151 	else {
152 		c = uri->s;
153 		len = uri->len;
154 	}
155 	if (len > 0) {
156 		dstr_append_zt(buf, "pres:");
157 		dstr_append(buf, c, len);
158 	}
159 }
160 
doc_add_presentity(dstring_t * buf,presentity_info_t * p,int use_cpim_pidf_ns)161 static void doc_add_presentity(dstring_t *buf, presentity_info_t *p, int use_cpim_pidf_ns)
162 {
163 	presence_tuple_info_t *t;
164 	presence_note_t *n;
165 	extension_element_t *e;
166 
167 	DEBUG_LOG("doc_add_presentity()\n");
168 	if (use_cpim_pidf_ns)
169 		dstr_append_zt(buf, "<presence xmlns=\"urn:ietf:params:xml:ns:cpim-pidf\" entity=\"");
170 	else
171 		dstr_append_zt(buf, "<presence xmlns=\"urn:ietf:params:xml:ns:pidf\" entity=\"");
172 	/* !!! there SHOULD be pres URI of presentity !!! */
173 	dstr_put_pres_uri(buf, &p->uri);
174 	/* dstr_append_str(buf, &p->presentity); */ /* only for test !!! */
175 	dstr_append_zt(buf, "\">\r\n");
176 
177 	DEBUG_LOG("adding tuples\n");
178 	t = p->first_tuple;
179 	if (!t) doc_add_empty_tuple(buf); /* correction for some strange clients :-) */
180 	while (t) {
181 		doc_add_tuple(buf, p, t);
182 		t = t->next;
183 	}
184 
185 	DEBUG_LOG("adding notes\n");
186 	n = p->first_note;
187 	while (n) {
188 		doc_add_note(buf, p, n);
189 		n = n->next;
190 	}
191 
192 	/* add extension elements */
193 	DEBUG_LOG("adding extension elements\n");
194 	e = p->first_unknown_element;
195 	while (e) {
196 		doc_add_extension(buf, e);
197 		e = e->next;
198 	}
199 
200 	dstr_append_zt(buf, "</presence>\r\n");
201 }
202 
create_pidf_document_ex(presentity_info_t * p,str_t * dst,str_t * dst_content_type,int use_cpim_pidf_ns)203 int create_pidf_document_ex(presentity_info_t *p, str_t *dst, str_t *dst_content_type, int use_cpim_pidf_ns)
204 {
205 	dstring_t buf;
206 	int err;
207 
208 	if (!dst) return -1;
209 
210 	str_clear(dst);
211 	if (dst_content_type) str_clear(dst_content_type);
212 
213 	if (!p) return -1;
214 
215 	if (dst_content_type) {
216 		if (use_cpim_pidf_ns)
217 			err = str_dup_zt(dst_content_type, "application/cpim-pidf+xml");
218 		else
219 			err = str_dup_zt(dst_content_type, "application/pidf+xml;charset=\"UTF-8\"");
220 		if (err < 0) return -1;
221 	}
222 
223 /*	if (!p->first_tuple) return 0;*/	/* no tuples => nothing to say */
224 
225 	dstr_init(&buf, 2048);
226 
227 	dstr_append_zt(&buf, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n");
228 	doc_add_presentity(&buf, p, use_cpim_pidf_ns);
229 
230 	err = dstr_get_str(&buf, dst);
231 	dstr_destroy(&buf);
232 
233 	if (err != 0) {
234 		str_free_content(dst);
235 		if (dst_content_type) str_free_content(dst_content_type);
236 	}
237 
238 	return err;
239 }
240 
create_pidf_document(presentity_info_t * p,str_t * dst,str_t * dst_content_type)241 int create_pidf_document(presentity_info_t *p, str_t *dst, str_t *dst_content_type)
242 {
243 	return create_pidf_document_ex(p, dst, dst_content_type, 0);
244 }
245 
246 /* ------------------------------ PIDF document parsing ------------------------------ */
247 
248 static char *pidf_ns = "urn:ietf:params:xml:ns:pidf";
249 /* static char *rpid_ns = "urn:ietf:params:xml:ns:pidf:rpid"; */
250 /* static char *data_model_ns = "urn:ietf:params:xml:ns:pidf:data-model"; */
251 
read_note(xmlNode * node,presence_note_t ** dst)252 static int read_note(xmlNode *node, presence_note_t **dst)
253 {
254 	const char *note = NULL;
255 	const char *lang = NULL;
256 
257 	note = get_node_value(node);
258 	lang = get_attr_value(find_attr(node->properties, "lang"));
259 
260 	*dst = create_presence_note_zt(note, lang);
261 	if (!dst) return -1;
262 
263 	return 0;
264 }
265 
get_whole_node_content(xmlNode * n,str_t * dst,xmlDocPtr doc)266 static int get_whole_node_content(xmlNode *n, str_t *dst, xmlDocPtr doc)
267 {
268 	int res = 0;
269 
270 	str_clear(dst);
271 	if (n) {
272 		n = xmlCopyNode(n, 1); /* this inserts namespaces into element correctly */
273 		if (!n) {
274 			ERROR_LOG("can't duplicate XML node\n");
275 			return -1;
276 		}
277 	}
278 	if (n) {
279 		xmlBufferPtr buf;
280 		buf = xmlBufferCreate();
281 		if (buf == NULL) {
282 			ERROR_LOG("Error creating the xml buffer\n");
283 			return -1;
284 		}
285 		if (xmlNodeDump(buf, doc, n, 0, 0) < 0) res = -1;
286 		if ((res == 0) && (buf->use > 0)) {
287 			str_t s;
288 			s.s = (char *)buf->content;
289 			s.len = buf->use;
290 			res = str_dup(dst, &s);
291 		}
292 		xmlBufferFree(buf);
293 		xmlFreeNode(n); /* was duplicated due to namespaces! */
294 	}
295 	return res;
296 }
297 
read_extension(xmlNode * ex,extension_element_t ** dst,xmlDocPtr doc)298 static int read_extension(xmlNode *ex, extension_element_t **dst, xmlDocPtr doc)
299 {
300 	extension_element_t *e;
301 	/* xmlNode *n; */
302 
303 	if (!dst) return -1;
304 	*dst = NULL;
305 
306 	e = (extension_element_t*)cds_malloc(sizeof(extension_element_t));
307 	if (!e) return -1;
308 
309 	memset(e, 0, sizeof(*e));
310 	*dst = e;
311 
312 	/* do not care about internals - take whole element ! */
313 	if (get_whole_node_content(ex, &e->element, doc) != 0) {
314 		cds_free(e);
315 		*dst = NULL;
316 		return -1;
317 	}
318 
319 	return 0;
320 }
321 
read_tuple(xmlNode * tuple,presence_tuple_info_t ** dst,int ignore_ns,xmlDocPtr doc)322 static int read_tuple(xmlNode *tuple, presence_tuple_info_t **dst, int ignore_ns, xmlDocPtr doc)
323 {
324 	str_t contact, id;
325 	basic_tuple_status_t status;
326 	xmlNode *n, *status_node;
327 	double priority = 0;
328 	const char *s;
329 	int res = 0;
330 	presence_note_t *note;
331 	char *ns = ignore_ns ? NULL: pidf_ns;
332 	extension_element_t *ex;
333 
334 	*dst = NULL;
335 
336 	DEBUG_LOG("read_tuple()\n");
337 	/* process contact (only one node) */
338 	n = find_node(tuple, "contact", ns);
339 	if (!n) {
340 		/* ERROR_LOG("contact not found\n"); */
341 		str_clear(&contact);
342 		/* return -1; */
343 	}
344 	else {
345 		s = get_attr_value(find_attr(n->properties, "priority"));
346 		if (s) priority = atof(s);
347 		s = get_node_value(n);
348 		contact.s = (char *)s;
349 		if (s) contact.len = strlen(s);
350 		else contact.len = 0;
351 		if (contact.len < 1) {
352 			ERROR_LOG("empty contact using default\n");
353 			/* return -1; */
354 		}
355 	}
356 
357 	/* process status (only one node) */
358 	status_node = find_node(tuple, "status", ns);
359 	if (!status_node) {
360 		ERROR_LOG("status not found\n");
361 		return -1;
362 	}
363 	n = find_node(status_node, "basic", ns);
364 	if (!n) {
365 		ERROR_LOG("basic status not found - using \'closed\'\n");
366 		/* return -1; */
367 		s = "closed";
368 	}
369 	else s = get_node_value(n);
370 	if (!s) {
371 		ERROR_LOG("basic status without value\n");
372 		return -1;
373 	}
374 
375 	/* translate status */
376 	status = presence_tuple_closed; /* default value */
377 	if (strcasecmp(s, "open") == 0) status = presence_tuple_open;
378 	if (strcasecmp(s, "closed") == 0) status = presence_tuple_closed;
379 	/* FIXME: handle not standardized variants too (add note to basic status) */
380 
381 	/* get ID from tuple node attribute? */
382 	id.s = (char *)get_attr_value(find_attr(tuple->properties, "id"));
383 	if (id.s) id.len = strlen(id.s);
384 	else id.len = 0;
385 
386 	*dst = create_tuple_info(&contact, &id, status);
387 	if (!(*dst)) return -1;
388 
389 	(*dst)->priority = priority;
390 
391 	/* handle nested elements */
392 	n = tuple->children;
393 	while (n) {
394 		if (n->type == XML_ELEMENT_NODE) {
395 			if (cmp_node(n, "note", ns) >= 0) {
396 				res = read_note(n, &note);
397 				if ((res == 0) && note) {
398 					DOUBLE_LINKED_LIST_ADD((*dst)->first_note,
399 							(*dst)->last_note, note);
400 				}
401 				else break;
402 			}
403 			else if (cmp_node(n, "contact", ns) >= 0) {
404 				/* skip, already processed */
405 			}
406 			else if (cmp_node(n, "status", ns) >= 0) {
407 				/* skip, already processed */
408 			}
409 			else if (cmp_node(n, "timestamp", ns) >= 0) {
410 				/* FIXME: process */
411 			}
412 			else { /* PIDF extensions - only from non-PIDF namespace? */
413 				res = read_extension(n, &ex, doc);
414 				if ((res == 0) && ex)
415 					DOUBLE_LINKED_LIST_ADD((*dst)->first_unknown_element,
416 							(*dst)->last_unknown_element, ex);
417 			}
418 		}
419 		n = n->next;
420 	}
421 
422 	/* handle nested elements in status */
423 	if (status_node) n = status_node->children;
424 	else n = NULL;
425 	while (n) {
426 		if (n->type == XML_ELEMENT_NODE) {
427 			if (cmp_node(n, "basic", ns) >= 0) {
428 				/* skip, already processed */
429 			}
430 			else { /* PIDF extensions - only from non-PIDF namespace? */
431 				res = read_extension(n, &ex, doc);
432 				if ((res == 0) && ex)
433 					DOUBLE_LINKED_LIST_ADD((*dst)->status.first_unknown_element,
434 							(*dst)->status.last_unknown_element, ex);
435 			}
436 		}
437 		n = n->next;
438 	}
439 
440 	return res;
441 }
442 
read_presentity(xmlNode * root,presentity_info_t ** dst,int ignore_ns,xmlDocPtr doc)443 static int read_presentity(xmlNode *root, presentity_info_t **dst, int ignore_ns, xmlDocPtr doc)
444 {
445 	xmlNode *n;
446 	str_t entity;
447 	presence_tuple_info_t *t;
448 	presence_note_t *note;
449 	int res = 0;
450 	char *ns = ignore_ns ? NULL: pidf_ns;
451 	extension_element_t *ex;
452 
453 	/* TRACE_LOG("read_presentity(ns=%s)\n", ns ? ns : ""); */
454 	if (cmp_node(root, "presence", ns) < 0) {
455 		ERROR_LOG("document is not presence \n");
456 		return -1;
457 	}
458 
459 	entity = zt2str((char*)get_attr_value(find_attr(root->properties, "entity")));
460 	*dst = create_presentity_info(&entity);
461 	if (!(*dst)) return -1; /* memory */
462 
463 	n = root->children;
464 	while (n) {
465 		if (n->type == XML_ELEMENT_NODE) {
466 			if (cmp_node(n, "tuple", ns) >= 0) {
467 				res = read_tuple(n, &t, ignore_ns, doc);
468 				if ((res == 0) && t) add_tuple_info(*dst, t);
469 				else break;
470 			}
471 			else if (cmp_node(n, "note", ns) >= 0) {
472 					res = read_note(n, &note);
473 					if ((res == 0) && note) {
474 						DOUBLE_LINKED_LIST_ADD((*dst)->first_note,
475 								(*dst)->last_note, note);
476 					}
477 					else break;
478 				}
479 			else { /* PIDF extensions - only from non-PIDF namespace? */
480 				res = read_extension(n, &ex, doc);
481 				if ((res == 0) && ex)
482 					DOUBLE_LINKED_LIST_ADD((*dst)->first_unknown_element,
483 							(*dst)->last_unknown_element, ex);
484 				/*if (res != 0) break; ignore errors there */
485 			}
486 
487 		}
488 		n = n->next;
489 	}
490 
491 	return res;
492 }
493 
494 /* ignore ns added for cpim-pidf+xml, draft version 07 (differs only in ns) */
parse_pidf_document_ex(presentity_info_t ** dst,const char * data,int data_len,int ignore_ns)495 int parse_pidf_document_ex(presentity_info_t **dst, const char *data, int data_len, int ignore_ns)
496 {
497 	int res = 0;
498 	xmlDocPtr doc;
499 
500 	if (!dst) return -1;
501 	if ((!data) || (data_len < 1)) return -2;
502 
503 	*dst = NULL;
504 	doc = xmlReadMemory(data, data_len, NULL, NULL, xml_parser_flags);
505 	if (doc == NULL) {
506 		ERROR_LOG("can't parse document\n");
507 		return -1;
508 	}
509 
510 	res = read_presentity(xmlDocGetRootElement(doc), dst, ignore_ns, doc);
511 	if (res != 0) {
512 		/* may be set => must be freed */
513 		if (*dst) free_presentity_info(*dst);
514 		*dst = NULL;
515 	}
516 
517 	xmlFreeDoc(doc);
518 	return res;
519 }
520 
521 /* libxml2 must be initialized before calling this function ! */
parse_pidf_document(presentity_info_t ** dst,const char * data,int data_len)522 int parse_pidf_document(presentity_info_t **dst, const char *data, int data_len)
523 {
524 	return parse_pidf_document_ex(dst, data, data_len, 0);
525 }
526 
527 /* --------------- CPIM_PIDF document creation/parsing ---------------- */
528 
parse_cpim_pidf_document(presentity_info_t ** dst,const char * data,int data_len)529 int parse_cpim_pidf_document(presentity_info_t **dst, const char *data, int data_len)
530 {
531 	return parse_pidf_document_ex(dst, data, data_len, 1);
532 }
533 
create_cpim_pidf_document(presentity_info_t * p,str_t * dst,str_t * dst_content_type)534 int create_cpim_pidf_document(presentity_info_t *p, str_t *dst, str_t *dst_content_type)
535 {
536 	return create_pidf_document_ex(p, dst, dst_content_type, 1);
537 }
538 
539