瀏覽代碼

lib/presence: relocated to archive

Daniel-Constantin Mierla 9 月之前
父節點
當前提交
3c31181813

+ 2 - 0
src/lib/presence/.cvsignore

@@ -0,0 +1,2 @@
+libser_presence.so.*
+

+ 19 - 0
src/lib/presence/ChangeLog

@@ -0,0 +1,19 @@
+2006-07-26
+	* PIDF extensions support
+
+2006-04-11
+	* reduced memory allocations in QSA
+	* prepared for parameters to QSA subscriptions
+
+2006-04-10
+	* changed QSA - corrected content-type propagation
+
+2006-03-21
+	* subscription status propagation through QSA
+
+2006-03-20
+	* extra information (record_id, package, ...) in QSA notificaltions
+	(client_notify_info_t) replaced by pointer to subscription [may be a bit
+	problematic, but much simplier and saving memory for QSA subscriptions
+	used in PA, RLS, PRESENCE_B2B]
+

+ 21 - 0
src/lib/presence/Makefile

@@ -0,0 +1,21 @@
+
+# example library makefile
+#
+
+include ../../Makefile.defs
+auto_gen=
+NAME:=ser_presence
+MAJOR_VER=0
+MINOR_VER=1
+BUGFIX_VER=0
+DEFS+=-DSER
+libxml2_includes=-I/usr/include/libxml2 -I$(LOCALBASE)/include/libxml2 \
+					-I$(LOCALBASE)/include
+libxml2_libs=-L$(LOCALBASE)/lib -lxml2
+INCLUDES= -I$(CURDIR)/.. -I$(CURDIR)/../.. $(libxml2_includes) 
+LIBS=$(libxml2_libs)
+SERLIBPATH=..
+SER_LIBS=$(SERLIBPATH)/cds/ser_cds
+
+include ../../Makefile.libs
+

+ 12 - 0
src/lib/presence/Makefile.nonser

@@ -0,0 +1,12 @@
+DEFS     += 
+INCLUDES += -I/usr/include/libxml2 -I/usr/local/include/libxml2 -I/usr/local/include
+LIBS     += -L/usr/local/lib -lxml2 -lcds
+
+# name of result executable or library
+NAME = presence
+
+# TYPE=lib => shared or static library, executable otherwise
+TYPE = lib
+
+include ../Makefile.defs
+

+ 4 - 0
src/lib/presence/doc/Makefile

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

+ 31 - 0
src/lib/presence/doc/presence.xml

@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!DOCTYPE book PUBLIC '-//OASIS//DTD DocBook XML V4.2//EN'
+'http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd'[
+	<!ENTITY % local.common.attrib "xml:base  CDATA  #IMPLIED">
+]>
+
+<book lang="en">
+<bookinfo>
+	<title>Presence utilities and data types (libpresence)</title>
+	<author><firstname>Václav</firstname><surname>Kubart</surname></author>
+	<abstract><para>Description of helper Presence library.
+	</para></abstract>
+</bookinfo>
+
+<preface><title>Preface</title>
+<para>This library contains data structures for presence documents
+representation and helper functions.
+</para>
+
+<section id="libpresence.dependencies"><title>Dependencies</title>
+<para>
+<itemizedlist>
+	<listitem><para><application>libcds</application> (distributed with SER)</para></listitem>
+</itemizedlist>
+</para>
+</section>
+
+</preface>
+
+</book>

+ 158 - 0
src/lib/presence/domain_maintainer.c

@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2005 iptelorg GmbH
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <presence/domain_maintainer.h>
+#include <cds/ptr_vector.h>
+#include <cds/memory.h>
+#include <cds/logger.h>
+
+domain_maintainer_t *create_domain_maintainer()
+{
+	domain_maintainer_t *dm;
+
+	dm = (domain_maintainer_t *)cds_malloc(sizeof(domain_maintainer_t));
+	if(dm) {
+		ptr_vector_init(&dm->registered_domains, 8);
+		cds_mutex_init(&dm->mutex);
+		dm->rc_grp = create_reference_counter_group(16);
+	}
+	return dm;
+}
+
+void destroy_domain_maintainer(domain_maintainer_t *dm)
+{
+	int i, cnt;
+	notifier_domain_t *d;
+
+	if(!dm)
+		return;
+
+	DEBUG_LOG("destroying domain maintainer\n");
+
+	cnt = ptr_vector_size(&dm->registered_domains);
+	for(i = 0; i < cnt; i++) {
+		d = ptr_vector_get(&dm->registered_domains, i);
+		if(!d)
+			continue;
+		if(remove_reference(&d->ref)) {
+			DEBUG_LOG("freeing domain: \'%.*s\'\n", FMT_STR(d->name));
+			destroy_notifier_domain(d);
+		}
+	}
+	ptr_vector_destroy(&dm->registered_domains);
+	cds_mutex_destroy(&dm->mutex);
+	cds_free(dm);
+}
+
+static notifier_domain_t *find_domain_nolock(
+		domain_maintainer_t *dm, const str_t *name)
+{
+	notifier_domain_t *d = NULL;
+	int i, cnt;
+
+	cnt = ptr_vector_size(&dm->registered_domains);
+	for(i = 0; i < cnt; i++) {
+		d = ptr_vector_get(&dm->registered_domains, i);
+		if(!d)
+			continue;
+		if(str_case_equals(&d->name, name) == 0)
+			return d;
+	}
+	return NULL;
+}
+
+static notifier_domain_t *add_domain_nolock(
+		domain_maintainer_t *dm, const str_t *name)
+{
+	notifier_domain_t *d = create_notifier_domain(dm->rc_grp, name);
+
+	if(d) {
+		DEBUG_LOG("created domain: \'%.*s\'\n", FMT_STR(d->name));
+		ptr_vector_add(&dm->registered_domains, d);
+		return d;
+	} else
+		return NULL;
+}
+
+/* notifier_domain_t *find_notifier_domain(domain_maintainer_t *dm, const str_t *name)
+{
+	notifier_domain_t *d = NULL;
+
+	if (!dm) return NULL;
+	cds_mutex_lock(&dm->mutex);
+	d = find_domain_nolock(dm, name);
+	cds_mutex_unlock(&dm->mutex);
+	return d;
+} */
+
+notifier_domain_t *register_notifier_domain(
+		domain_maintainer_t *dm, const str_t *name)
+{
+	notifier_domain_t *d = NULL;
+
+	if(!dm)
+		return NULL;
+
+	cds_mutex_lock(&dm->mutex);
+	d = find_domain_nolock(dm, name);
+	if(!d)
+		d = add_domain_nolock(dm, name);
+	if(d) {
+		add_reference(&d->ref); /* add reference for client */
+	}
+	cds_mutex_unlock(&dm->mutex);
+	return d;
+}
+
+static void remove_notifier_domain(
+		domain_maintainer_t *dm, notifier_domain_t *domain)
+{
+	notifier_domain_t *d = NULL;
+	int i, cnt;
+
+	cnt = ptr_vector_size(&dm->registered_domains);
+	for(i = 0; i < cnt; i++) {
+		d = ptr_vector_get(&dm->registered_domains, i);
+		if(d == domain) {
+			ptr_vector_remove(&dm->registered_domains, i);
+			break;
+		}
+	}
+}
+
+void release_notifier_domain(domain_maintainer_t *dm, notifier_domain_t *domain)
+{
+	if((!dm) || (!domain))
+		return;
+
+	cds_mutex_lock(&dm->mutex);
+	if(remove_reference(&domain->ref)) {
+		/* last reference */
+		DEBUG_LOG("freeing domain: \'%.*s\'\n", FMT_STR(domain->name));
+		remove_notifier_domain(dm, domain);
+		destroy_notifier_domain(domain);
+	}
+	cds_mutex_unlock(&dm->mutex);
+}

+ 58 - 0
src/lib/presence/domain_maintainer.h

@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2005 iptelorg GmbH
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __DOMAIN_MAINTAINER_H
+#define __DOMAIN_MAINTAINER_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <presence/notifier_domain.h>
+#include <cds/ptr_vector.h>
+#include <cds/sync.h>
+
+	typedef struct
+	{
+		ptr_vector_t registered_domains;
+		reference_counter_group_t *rc_grp;
+		cds_mutex_t mutex;
+	} domain_maintainer_t;
+
+
+	domain_maintainer_t *create_domain_maintainer();
+	void destroy_domain_maintainer(domain_maintainer_t *dm);
+
+	notifier_domain_t *register_notifier_domain(
+			domain_maintainer_t *dm, const str_t *name);
+	void release_notifier_domain(
+			domain_maintainer_t *dm, notifier_domain_t *domain);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 106 - 0
src/lib/presence/lpidf.c

@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2005 iptelorg GmbH
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <presence/lpidf.h>
+#include <cds/dstring.h>
+#include <cds/memory.h>
+#include <cds/logger.h>
+#include <cds/list.h>
+#include <presence/xml_utils.h>
+#include <string.h>
+
+/* ------------------------------ LPIDF document creation ------------------------------ */
+
+static void doc_add_tuple(
+		dstring_t *buf, presentity_info_t *p, presence_tuple_info_t *t)
+{
+	char tmp[64];
+
+	if(t->status.basic == presence_tuple_closed)
+		return; /* do not append closed tuples */
+
+	dstr_append_zt(buf, "Contact: ");
+	dstr_append_str(buf, &t->contact);
+	dstr_append_zt(buf, ";q=");
+	sprintf(tmp, "%1.2f", t->priority);
+	dstr_append_zt(buf, tmp);
+	dstr_append_zt(buf, "\r\n");
+}
+
+static void doc_add_presentity(dstring_t *buf, presentity_info_t *p)
+{
+	presence_tuple_info_t *t;
+	/* presence_note_t *n; */
+
+	dstr_append_zt(buf, "To: ");
+	dstr_append_str(buf, &p->uri);
+	dstr_append_zt(buf, "\r\n");
+
+	t = p->first_tuple;
+	while(t) {
+		doc_add_tuple(buf, p, t);
+		t = t->next;
+	}
+}
+
+int create_lpidf_document(
+		presentity_info_t *p, str_t *dst, str_t *dst_content_type)
+{
+	dstring_t buf;
+	int err;
+
+	if(!dst)
+		return -1;
+
+	str_clear(dst);
+	if(dst_content_type)
+		str_clear(dst_content_type);
+
+	if(!p)
+		return -1;
+
+	if(dst_content_type) {
+		if(str_dup_zt(dst_content_type, "text/lpidf") < 0) {
+			return -1;
+		}
+	}
+
+	/*	if (!p->first_tuple) return 0;*/ /* no tuples => nothing to say */
+
+	dstr_init(&buf, 2048);
+
+	doc_add_presentity(&buf, p);
+
+	err = dstr_get_str(&buf, dst);
+	dstr_destroy(&buf);
+
+	if(err != 0) {
+		str_free_content(dst);
+		if(dst_content_type)
+			str_free_content(dst_content_type);
+	}
+
+	return err;
+}

+ 35 - 0
src/lib/presence/lpidf.h

@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2005 iptelorg GmbH
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __PRESENCE_XPIDF_H
+#define __PRESENCE_XPIDF_H
+
+#include <cds/sstr.h>
+#include <presence/pres_doc.h>
+
+int create_lpidf_document(
+		presentity_info_t *p, str_t *dst, str_t *dst_content_type);
+
+#endif

+ 106 - 0
src/lib/presence/notifier.h

@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2005 iptelorg GmbH
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __NOTIFIER_H
+#define __NOTIFIER_H
+
+/* Notifier functions for notifier_domain */
+
+#include <presence/notifier_domain.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+	/* Returns the created notifier. Indicates error if NULL. */
+	notifier_t *register_notifier(notifier_domain_t *domain,
+			const str_t *package, server_subscribe_func subscribe,
+			server_unsubscribe_func unsubscribe, void *user_data);
+
+	void unregister_notifier(notifier_domain_t *domain, notifier_t *info);
+
+	/** accepts subscription (internally adds reference to it), thus it can
+ * be handled by notifier which called this function
+ * MUST be called in notifier's subscribe function, otherwise the
+ * subscription can NOT be accepted
+ *
+ * Note: only for asynchonously processed subscriptions (synchronous
+ * don't need it) */
+	void accept_subscription(qsa_subscription_t *s);
+
+	/** releases accepted subscription - MUST be called on all accepted
+ * subscriptions (only on them!) to be freed from memory !
+ * Note: only for asynchonously processed subscriptions (synchronous
+ * don't need it) */
+	void release_subscription(qsa_subscription_t *s);
+
+	/** This structure is sent via message queue
+ * to client. It must contain all information
+ * for processing the status info. */
+
+	typedef enum
+	{
+		qsa_subscription_active,
+		qsa_subscription_pending,
+		qsa_subscription_terminated,
+		qsa_subscription_rejected
+	} qsa_subscription_status_t;
+
+	typedef struct
+	{
+		/* replacement for record_id, package, ... it is much more efficient */
+		qsa_subscription_t *subscription;
+		qsa_content_type_t *content_type;
+		void *data;
+		int data_len;
+		qsa_subscription_status_t status;
+	} client_notify_info_t;
+
+
+	void free_client_notify_info_content(client_notify_info_t *info);
+
+	/* notifications SHOULD be sent through this method */
+	int notify_subscriber(qsa_subscription_t *s, notifier_t *n,
+			qsa_content_type_t *content_type, void *data,
+			qsa_subscription_status_t status);
+
+	/* this can be called in notifier and the returned value is valid
+ * before finishes "unsubscribe" processing */
+	str_t *get_subscriber_id(qsa_subscription_t *s);
+
+	/* this can be called in notifier and the returned value is valid
+ * before finishes "unsubscribe" processing */
+	str_t *get_record_id(qsa_subscription_t *s);
+
+	/* this can be called in notifier and the returned value is valid
+ * before finishes "unsubscribe" processing */
+	void *get_subscriber_data(qsa_subscription_t *s);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 593 - 0
src/lib/presence/notifier_domain.c

@@ -0,0 +1,593 @@
+/*
+ * Copyright (C) 2005 iptelorg GmbH
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <cds/memory.h>
+#include <cds/logger.h>
+#include <cds/sync.h>
+#include <stdio.h>
+#include <presence/notifier_domain.h>
+#include <presence/notifier.h>
+#include <presence/subscriber.h>
+#include <cds/list.h>
+#include <cds/cds.h>
+#include <string.h>
+
+/*#define lock_subscription_data(s) if (s->mutex) cds_mutex_lock(s->mutex);
+#define unlock_subscription_data(s) if (s->mutex) cds_mutex_unlock(s->mutex);*/
+
+static void lock_subscription_data(qsa_subscription_t *s)
+{
+	/* is function due to debugging */
+	if(s->mutex)
+		cds_mutex_lock(s->mutex);
+}
+
+static void unlock_subscription_data(qsa_subscription_t *s)
+{
+	/* is function due to debugging */
+	if(s->mutex)
+		cds_mutex_unlock(s->mutex);
+}
+
+static void free_notifier(notifier_t *info);
+static void free_subscription(qsa_subscription_t *s);
+
+/* -------- package functions -------- */
+
+static notifier_package_t *find_package(notifier_domain_t *d, const str_t *name)
+{
+	notifier_package_t *p;
+
+	if(!d)
+		return NULL;
+	p = d->first_package;
+	while(p) {
+		if(str_case_equals(name, &p->name) == 0)
+			return p;
+		p = p->next;
+	}
+	return NULL;
+}
+
+static void add_package(notifier_domain_t *d, notifier_package_t *p)
+{
+	p->domain = d;
+	DOUBLE_LINKED_LIST_ADD(d->first_package, d->last_package, p);
+}
+
+static notifier_package_t *create_package(const str_t *name)
+{
+	notifier_package_t *p =
+			(notifier_package_t *)cds_malloc(sizeof(notifier_package_t));
+	if(p) {
+		p->first_subscription = NULL;
+		p->last_subscription = NULL;
+		p->first_notifier = NULL;
+		p->last_notifier = NULL;
+		p->next = NULL;
+		p->prev = NULL;
+		p->domain = NULL;
+		if(str_dup(&p->name, name) < 0) {
+			cds_free(p);
+			ERROR_LOG("can't allocate memory\n");
+			return NULL;
+		}
+	}
+	return p;
+}
+
+/** finds existing package or adds new if not exists */
+static notifier_package_t *get_package(notifier_domain_t *d, const str_t *name)
+{
+	notifier_package_t *p = NULL;
+
+	if(is_str_empty(name))
+		return NULL;
+
+	p = find_package(d, name);
+	if(!p) {
+		p = create_package(name);
+		if(p)
+			add_package(d, p);
+	}
+	return p;
+}
+
+static void destroy_package(notifier_package_t *p)
+{
+	/* notifier_t *e, *n; */
+	qsa_subscription_t *s, *ns;
+
+	/* release all subscriptions ???  */
+	s = p->first_subscription;
+	while(s) {
+		ns = s->next;
+		/* CAN NOT be called !!!! : unsubscribe(p->domain, s) */
+		release_subscription(s);
+		s = ns;
+	}
+
+	/* !!! don't release notifiers - it is their job !!! */
+	/* it may lead to errors there */
+	/* e = p->first_notifier;
+	while (e) {
+		n = e->next;
+		free_notifier(e);
+		e = n;
+	} */
+
+	p->first_notifier = NULL;
+	p->last_notifier = NULL;
+	str_free_content(&p->name);
+
+	cds_free(p);
+}
+
+/* -------- content type functions -------- */
+
+static qsa_content_type_t *find_content_type(
+		notifier_domain_t *d, const str_t *name)
+{
+	qsa_content_type_t *p;
+
+	if(!d)
+		return NULL;
+	p = d->first_content_type;
+	while(p) {
+		if(str_case_equals(name, &p->name) == 0)
+			return p;
+		p = p->next;
+	}
+	return NULL;
+}
+
+static void add_content_type(notifier_domain_t *d, qsa_content_type_t *p)
+{
+	DOUBLE_LINKED_LIST_ADD(d->first_content_type, d->last_content_type, p);
+}
+
+static qsa_content_type_t *create_content_type(
+		const str_t *name, destroy_function_f destroy_func)
+{
+	qsa_content_type_t *p = (qsa_content_type_t *)cds_malloc(
+			sizeof(qsa_content_type_t) + str_len(name));
+	if(p) {
+		p->next = NULL;
+		p->prev = NULL;
+		p->name.s = p->buf;
+		if(str_len(name) > 0) {
+			memcpy(p->name.s, name->s, name->len);
+			p->name.len = name->len;
+		} else
+			p->name.len = 0;
+		p->destroy_func = destroy_func;
+	}
+	return p;
+}
+
+/** finds existing package or adds new if not exists */
+qsa_content_type_t *register_content_type(notifier_domain_t *d,
+		const str_t *name, destroy_function_f destroy_func)
+{
+	qsa_content_type_t *p = NULL;
+
+	if(is_str_empty(name))
+		return NULL;
+
+	p = find_content_type(d, name);
+	if(!p) {
+		p = create_content_type(name, destroy_func);
+		if(p)
+			add_content_type(d, p);
+	}
+	return p;
+}
+
+static void destroy_content_type(qsa_content_type_t *p)
+{
+	cds_free(p);
+}
+
+/* -------- Helper functions -------- */
+
+static void free_notifier(notifier_t *info)
+{
+	cds_free(info);
+}
+
+static void free_subscription(qsa_subscription_t *s)
+{
+	DEBUG_LOG("freeing subscription to %p\n", s);
+	cds_free(s);
+}
+
+
+/* -------- Domain initialization/destruction functions -------- */
+
+/** Creates a new domain using cds memory functions. */
+notifier_domain_t *create_notifier_domain(
+		reference_counter_group_t *g, const str_t *name)
+{
+	notifier_domain_t *d =
+			(notifier_domain_t *)cds_malloc(sizeof(notifier_domain_t));
+	if(d) {
+		d->first_package = NULL;
+		d->last_package = NULL;
+		d->first_content_type = NULL;
+		d->last_content_type = NULL;
+		d->rc_grp = g;
+		if(str_dup(&d->name, name) < 0) {
+			cds_free(d);
+			ERROR_LOG("can't allocate memory\n");
+			return NULL;
+		}
+		cds_mutex_init(&d->mutex);
+		cds_mutex_init(&d->data_mutex);
+		init_reference_counter(g, &d->ref);
+	}
+	return d;
+}
+
+/** Destroys domain and whole information stored in internal
+ * structures. If there are any subscribers, they are unsubscribed,
+ * if there are any notifiers, they are unregistered. */
+void destroy_notifier_domain(notifier_domain_t *domain)
+{
+	notifier_package_t *p, *n;
+	qsa_content_type_t *c, *tmp;
+
+	/* this function is always called only if no only one reference
+	 * to domain exists (see domain maintainer), this should mean, that
+	 * all subscribers freed their subscriptions */
+
+	lock_notifier_domain(domain);
+
+	/* remove packages */
+	p = domain->first_package;
+	while(p) {
+		n = p->next;
+		destroy_package(p);
+		p = n;
+	}
+	domain->first_package = NULL;
+	domain->last_package = NULL;
+
+	c = domain->first_content_type;
+	while(c) {
+		tmp = c;
+		c = c->next;
+		destroy_content_type(tmp);
+	}
+	domain->first_content_type = NULL;
+	domain->last_content_type = NULL;
+
+	unlock_notifier_domain(domain);
+
+	str_free_content(&domain->name);
+	cds_mutex_destroy(&domain->mutex);
+	cds_mutex_init(&domain->data_mutex);
+	cds_free(domain);
+}
+
+/* -------- Notifier functions -------- */
+
+/* Returns the id of created notifier. Indicates error if less than 0 */
+notifier_t *register_notifier(notifier_domain_t *domain, const str_t *package,
+		server_subscribe_func subscribe, server_unsubscribe_func unsubscribe,
+		void *user_data)
+{
+	notifier_t *info;
+	notifier_package_t *p;
+	qsa_subscription_t *s;
+
+	lock_notifier_domain(domain);
+	p = get_package(domain, package);
+	if(!p) {
+		unlock_notifier_domain(domain);
+		return NULL;
+	}
+
+	info = cds_malloc(sizeof(notifier_t));
+	if(!info)
+		return info;
+
+	info->subscribe = subscribe;
+	info->unsubscribe = unsubscribe;
+	info->user_data = user_data;
+	info->package = p;
+	DEBUG_LOG("registered notifier for %.*s\n", FMT_STR(*package));
+
+	DOUBLE_LINKED_LIST_ADD(p->first_notifier, p->last_notifier, info);
+
+	/* go through all subscriptions for package and
+	 * add them to this notifier */
+	s = p->first_subscription;
+	while(s) {
+		info->subscribe(info, s);
+		s = s->next;
+	}
+
+	unlock_notifier_domain(domain);
+
+	return info;
+}
+
+void unregister_notifier(notifier_domain_t *domain, notifier_t *info)
+{
+	notifier_package_t *p;
+
+	if((!info) || (!domain))
+		return;
+
+	/* maybe: test if the NOTIFIER is registered before unregistration */
+
+	lock_notifier_domain(domain);
+
+	p = info->package;
+	if(p) {
+		/* accepted subscriptions MUST be removed by the notifier
+		 * how to solve this ? */
+
+		/* qsa_subscription_t *s;
+		s = p->first_subscription;
+		while (s) {
+			CAN NOT be called !!!!! info->unsubscribe(info, s);
+			s = s->next;
+		}*/
+
+		DOUBLE_LINKED_LIST_REMOVE(p->first_notifier, p->last_notifier, info);
+		/* DEBUG_LOG("UNregistered notifier for %.*s\n", FMT_STR(p->name)); */
+		free_notifier(info);
+	}
+	unlock_notifier_domain(domain);
+}
+
+/* -------- Subscriber functions -------- */
+
+/* If a notifier publishing watched state registers after subscribe
+ * call, it receives the subscription automatically too! */
+qsa_subscription_t *subscribe(notifier_domain_t *domain, str_t *package,
+		qsa_subscription_data_t *data)
+{
+	qsa_subscription_t *s;
+	notifier_t *e;
+	notifier_package_t *p;
+	int cnt = 0;
+
+	lock_notifier_domain(domain);
+	p = get_package(domain, package);
+	if(!p) {
+		ERROR_LOG("can't find/add package for subscription\n");
+		unlock_notifier_domain(domain);
+		return NULL;
+	}
+
+	s = cds_malloc(sizeof(qsa_subscription_t));
+	if(!s) {
+		ERROR_LOG("can't allocate memory\n");
+		unlock_notifier_domain(domain);
+		return s;
+	}
+
+	s->package = p;
+	s->mutex = &domain->data_mutex;
+	s->data = data;
+	s->allow_notifications = 1;
+	init_reference_counter(domain->rc_grp, &s->ref);
+
+	DOUBLE_LINKED_LIST_ADD(p->first_subscription, p->last_subscription, s);
+
+	/* add a reference for calling subscriber */
+	add_reference(&s->ref);
+
+	/* browse all notifiers in given package and subscribe to them
+	 * and add them to notifiers list */
+	cnt = 0;
+	e = p->first_notifier;
+	while(e) {
+		cnt++;
+		/* each notifier MUST add its own reference if
+		 * it wants to accept the subscription !!! */
+		e->subscribe(e, s);
+		e = e->next;
+	}
+	unlock_notifier_domain(domain);
+	DEBUG_LOG("subscribed to %d notifier(s)\n", cnt);
+
+	return s;
+}
+
+void release_subscription(qsa_subscription_t *s)
+{
+	if(!s)
+		return;
+	if(remove_reference(&s->ref))
+		free_subscription(s);
+}
+
+void accept_subscription(qsa_subscription_t *s)
+{
+	if(!s)
+		return;
+	add_reference(&s->ref);
+}
+
+/** Destroys an existing subscription - can be called ONLY by client !!! */
+void unsubscribe(notifier_domain_t *domain, qsa_subscription_t *s)
+{
+	notifier_package_t *p;
+	notifier_t *e;
+
+	/* mark subscription as un-notifyable */
+	lock_subscription_data(s);
+	s->allow_notifications = 0;
+	unlock_subscription_data(s);
+
+	lock_notifier_domain(domain);
+
+	/* maybe: test if the SUBSCRIBER is subscribed before unsubsc. */
+	p = s->package;
+	if(!p) {
+		unlock_notifier_domain(domain);
+		return;
+	}
+
+	DOUBLE_LINKED_LIST_REMOVE(p->first_subscription, p->last_subscription, s);
+
+	e = p->first_notifier;
+	while(e) {
+		e->unsubscribe(e, s);
+		e = e->next;
+	}
+
+	unlock_notifier_domain(domain);
+
+	/* mark subscription data as invalid */
+	lock_subscription_data(s);
+	s->data = NULL;
+	unlock_subscription_data(s);
+
+	/* remove clients reference (don't give references to client?) */
+	remove_reference(&s->ref);
+
+	release_subscription(s);
+}
+
+/* void notify_subscriber(qsa_subscription_t *s, mq_message_t *msg) */
+int notify_subscriber(qsa_subscription_t *s, notifier_t *n,
+		qsa_content_type_t *content_type, void *data,
+		qsa_subscription_status_t status)
+{
+	int ok = 1;
+	int sent = 0;
+	mq_message_t *msg = NULL;
+	client_notify_info_t *info = NULL;
+
+	if(!s) {
+		ERROR_LOG("BUG: sending notify for <null> subscription\n");
+		ok = 0;
+	}
+
+	if(!content_type) {
+		ERROR_LOG("BUG: content type not given! Possible memory leaks!\n");
+		return -1;
+	}
+
+	if(ok) {
+		msg = create_message_ex(sizeof(client_notify_info_t));
+		if(!msg) {
+			ERROR_LOG("can't create notify message!\n");
+			ok = 0;
+		}
+	}
+
+	if(ok) {
+		set_data_destroy_function(
+				msg, (destroy_function_f)free_client_notify_info_content);
+		info = (client_notify_info_t *)msg->data;
+
+		info->subscription = s;
+		info->content_type = content_type;
+		info->data = data;
+		info->status = status;
+
+		lock_subscription_data(s);
+		if((s->allow_notifications) && (s->data)) {
+			if(s->data->dst) {
+				if(push_message(s->data->dst, msg) < 0)
+					ok = 0;
+				else
+					sent = 1;
+			}
+		}
+		unlock_subscription_data(s);
+	}
+
+	if(!sent) {
+		/* free unsent messages */
+		if(msg)
+			free_message(msg);
+		else if(data)
+			content_type->destroy_func(data);
+	}
+
+	if(ok)
+		return 0;
+	else
+		return 1; /* !!! Warning: data are destroyed !!! */
+}
+
+void free_client_notify_info_content(client_notify_info_t *info)
+{
+	DEBUG_LOG(" ... freeing notify info content\n");
+	/*	str_free_content(&info->package);
+	str_free_content(&info->record_id);
+	str_free_content(&info->notifier); */
+	DEBUG_LOG(" ... calling destroy func on data\n");
+	if(info->content_type) {
+		if(info->data)
+			info->content_type->destroy_func(info->data);
+	} else
+		ERROR_LOG("BUG: content-type not given! Possible memory leaks!\n");
+}
+
+/* this can be called in notifier and the returned value is valid
+ * before finishes "unsubscribe" processing */
+str_t *get_subscriber_id(qsa_subscription_t *s)
+{
+	if(!s)
+		return NULL;
+	if(!s->data)
+		return NULL;
+	return &s->data->subscriber_id;
+}
+
+/* this can be called in notifier and the returned value is valid
+ * before finishes "unsubscribe" processing */
+str_t *get_record_id(qsa_subscription_t *s)
+{
+	if(!s)
+		return NULL;
+	if(!s->data)
+		return NULL;
+	return &s->data->record_id;
+}
+
+/* this can be called in notifier and the returned value is valid
+ * before finishes "unsubscribe" processing */
+void *get_subscriber_data(qsa_subscription_t *s)
+{
+	if(!s)
+		return NULL;
+	if(!s->data)
+		return NULL;
+	return s->data->subscriber_data;
+}
+
+void clear_subscription_data(qsa_subscription_data_t *data)
+{
+	if(data)
+		memset(data, 0, sizeof(*data));
+}

+ 146 - 0
src/lib/presence/notifier_domain.h

@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2005 iptelorg GmbH
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __NOTIFIER_DOMAIN_H
+#define __NOTIFIER_DOMAIN_H
+
+#include <cds/sstr.h>
+#include <cds/ptr_vector.h>
+#include <cds/sync.h>
+
+#include <presence/qsa_params.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+	struct _qsa_subscription_t;
+	typedef struct _qsa_subscription_t qsa_subscription_t;
+	struct _notifier_package_t;
+	typedef struct _notifier_package_t notifier_package_t;
+	struct _notifier_t;
+	typedef struct _notifier_t notifier_t;
+	struct _notifier_domain_t;
+	typedef struct _notifier_domain_t notifier_domain_t;
+
+	/* data hold by subscriber for the time of subscription duration
+ * (from subscribe to unsubscribe; after calling unsubscribe can
+ * be destroyed contents of them) */
+	typedef struct _qsa_subscription_data_t
+	{
+		msg_queue_t *dst;
+		str_t record_id;
+		str_t subscriber_id;
+		qsa_subscription_params_t *first_param;
+		void *subscriber_data;
+	} qsa_subscription_data_t;
+
+	/** Internal structure holding informations about
+ * created client subscriptions.
+ */
+	struct _qsa_subscription_t
+	{
+		/* client_notify_func notify; */
+		cds_mutex_t *mutex;
+		notifier_package_t *package;
+		int allow_notifications;
+		qsa_subscription_data_t *data;
+		struct _qsa_subscription_t *prev, *next;
+		reference_counter_data_t ref;
+	};
+
+	/* typedef void (*client_notify_func)(client_notify_info_t *info); */
+
+	typedef int (*server_subscribe_func)(
+			notifier_t *n, qsa_subscription_t *subscription);
+
+	typedef void (*server_unsubscribe_func)(
+			notifier_t *n, qsa_subscription_t *subscription);
+
+	typedef struct _qsa_content_type_t
+	{
+		struct _qsa_content_type_t *next, *prev;
+		str_t name;
+		destroy_function_f destroy_func;
+		char buf[1]; /* buffer for name allocation together with the structure */
+	} qsa_content_type_t;
+
+	/** Internal structure storing registered notifiers. */
+	struct _notifier_t
+	{
+		server_subscribe_func subscribe;
+		server_unsubscribe_func unsubscribe;
+		void *user_data; /* private data for this notifier */
+		notifier_package_t *package;
+		struct _notifier_t *prev, *next;
+	};
+
+	struct _notifier_package_t
+	{
+		str_t name;
+		/* maybe: serialize and deserialize methods */
+		notifier_domain_t *domain;
+		notifier_t *first_notifier,
+				*last_notifier; /* notifiers are linked in theirs package! */
+		qsa_subscription_t *first_subscription, *last_subscription;
+		notifier_package_t *next, *prev;
+	};
+
+	struct _notifier_domain_t
+	{
+		cds_mutex_t mutex;
+		cds_mutex_t
+				data_mutex; /* mutex for locking standalone subscription data, may be changed to mutex pool */
+		str_t name;
+		notifier_package_t *first_package, *last_package;
+		qsa_content_type_t *first_content_type, *last_content_type;
+		reference_counter_data_t ref;
+		reference_counter_group_t *rc_grp;
+	};
+
+	/* -------- Domain initialization/destruction functions -------- */
+
+	/** Creates a new domain using cds memory functions. */
+	notifier_domain_t *create_notifier_domain(
+			reference_counter_group_t *g, const str_t *name);
+
+	/** Destroys domain and whole information stored in internal
+ * structures. If there are any subscribers, they are unsubscribed,
+ * if there are any notifiers, they are unregistered. */
+	void destroy_notifier_domain(notifier_domain_t *domain);
+
+	qsa_content_type_t *register_content_type(notifier_domain_t *d,
+			const str_t *name, destroy_function_f destroy_func);
+
+#define lock_notifier_domain(d) cds_mutex_lock(&(d->mutex))
+#define unlock_notifier_domain(d) cds_mutex_unlock(&(d->mutex))
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif

+ 571 - 0
src/lib/presence/pidf.c

@@ -0,0 +1,571 @@
+/*
+ * PIDF parser
+ *
+ * $Id$
+ *
+ * Copyright (C) 2005 iptelorg GmbH
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <presence/pidf.h>
+#include <cds/dstring.h>
+#include <cds/memory.h>
+#include <cds/logger.h>
+#include <cds/list.h>
+#include <presence/xml_utils.h>
+#include <string.h>
+
+/* ------------------------------ PIDF document creation ------------------------------ */
+
+static void doc_add_tuple_note(dstring_t *buf, presence_note_t *n)
+{
+	DEBUG_LOG("doc_add_tuple_note()\n");
+
+	dstr_append_zt(buf, "\t\t<note");
+	if(n->lang.len > 0) {
+		dstr_append_zt(buf, " lang=\"");
+		dstr_append_str(buf, &n->lang);
+		dstr_append_zt(buf, "\"");
+	}
+	dstr_append_zt(buf, ">");
+	dstr_append_str(buf, &n->value);
+	dstr_append_zt(buf, "</note>\r\n");
+}
+
+static inline void doc_add_extension(dstring_t *buf, extension_element_t *ex)
+{
+	dstr_append_str(buf, &ex->element);
+	dstr_append_zt(buf, "\r\n");
+}
+
+static void doc_add_tuple(
+		dstring_t *buf, presentity_info_t *p, presence_tuple_info_t *t)
+{
+	presence_note_t *n;
+	extension_element_t *e;
+	char tmp[32];
+
+	DEBUG_LOG("doc_add_tuple()\n");
+
+	dstr_append_zt(buf, "\t<tuple id=\"");
+	dstr_append_str(buf, &t->id);
+	dstr_append_zt(buf, "\">\r\n");
+
+	dstr_append_zt(buf, "\t\t<status>\r\n");
+	if(t->status.basic != presence_tuple_undefined_status) {
+		/* do not add unknown status it is not mandatory in PIDF */
+		dstr_append_zt(buf, "\t\t\t<basic>");
+		dstr_append_str(buf, tuple_status2str(t->status.basic));
+		dstr_append_zt(buf, "</basic>\r\n");
+	}
+	/* add extension status elements */
+	e = t->status.first_unknown_element;
+	while(e) {
+		doc_add_extension(buf, e);
+		e = e->next;
+	}
+	dstr_append_zt(buf, "\t\t</status>\r\n");
+
+	/* add extension elements */
+	e = t->first_unknown_element;
+	while(e) {
+		doc_add_extension(buf, e);
+		e = e->next;
+	}
+
+	if(!is_str_empty(&t->contact)) {
+		dstr_append_zt(buf, "\t\t<contact priority=\"");
+		sprintf(tmp, "%1.2f", t->priority);
+		dstr_append_zt(buf, tmp);
+		dstr_append_zt(buf, "\">");
+		dstr_append_str(buf, &t->contact);
+		dstr_append_zt(buf, "</contact>\r\n");
+	}
+
+	n = t->first_note;
+	while(n) {
+		doc_add_tuple_note(buf, n);
+		n = n->next;
+	}
+
+	dstr_append_zt(buf, "\t</tuple>\r\n");
+}
+
+static void doc_add_empty_tuple(dstring_t *buf)
+{
+	/* "empty" tuple is needed in PIDF by Microsoft Windows Messenger v. 5.1 and linphone 1.2) */
+	DEBUG_LOG("doc_add_empty_tuple()\n");
+
+	dstr_append_zt(buf, "\t<tuple id=\"none\">\r\n");
+	dstr_append_zt(buf, "\t\t<status><basic>closed</basic></status>\r\n");
+
+	dstr_append_zt(buf, "\t</tuple>\r\n");
+}
+
+static void doc_add_note(
+		dstring_t *buf, presentity_info_t *p, presence_note_t *n)
+{
+	DEBUG_LOG("doc_add_note()\n");
+
+	dstr_append_zt(buf, "\t<note");
+	if(n->lang.len > 0) {
+		dstr_append_zt(buf, " lang=\"");
+		dstr_append_str(buf, &n->lang);
+		dstr_append_zt(buf, "\"");
+	}
+	dstr_append_zt(buf, ">");
+	dstr_append_str(buf, &n->value);
+	dstr_append_zt(buf, "</note>\r\n");
+}
+
+static void dstr_put_pres_uri(dstring_t *buf, str_t *uri)
+{
+	char *c;
+	int len = 0;
+
+	if(!uri)
+		return;
+
+	c = str_strchr(uri, ':');
+	if(c) {
+		len = uri->len - (c - uri->s) - 1;
+		if(len > 0)
+			c++;
+	} else {
+		c = uri->s;
+		len = uri->len;
+	}
+	if(len > 0) {
+		dstr_append_zt(buf, "pres:");
+		dstr_append(buf, c, len);
+	}
+}
+
+static void doc_add_presentity(
+		dstring_t *buf, presentity_info_t *p, int use_cpim_pidf_ns)
+{
+	presence_tuple_info_t *t;
+	presence_note_t *n;
+	extension_element_t *e;
+
+	DEBUG_LOG("doc_add_presentity()\n");
+	if(use_cpim_pidf_ns)
+		dstr_append_zt(buf,
+				"<presence xmlns=\"urn:ietf:params:xml:ns:cpim-pidf\" "
+				"entity=\"");
+	else
+		dstr_append_zt(buf,
+				"<presence xmlns=\"urn:ietf:params:xml:ns:pidf\" entity=\"");
+	/* !!! there SHOULD be pres URI of presentity !!! */
+	dstr_put_pres_uri(buf, &p->uri);
+	/* dstr_append_str(buf, &p->presentity); */ /* only for test !!! */
+	dstr_append_zt(buf, "\">\r\n");
+
+	DEBUG_LOG("adding tuples\n");
+	t = p->first_tuple;
+	if(!t)
+		doc_add_empty_tuple(buf); /* correction for some strange clients :-) */
+	while(t) {
+		doc_add_tuple(buf, p, t);
+		t = t->next;
+	}
+
+	DEBUG_LOG("adding notes\n");
+	n = p->first_note;
+	while(n) {
+		doc_add_note(buf, p, n);
+		n = n->next;
+	}
+
+	/* add extension elements */
+	DEBUG_LOG("adding extension elements\n");
+	e = p->first_unknown_element;
+	while(e) {
+		doc_add_extension(buf, e);
+		e = e->next;
+	}
+
+	dstr_append_zt(buf, "</presence>\r\n");
+}
+
+int create_pidf_document_ex(presentity_info_t *p, str_t *dst,
+		str_t *dst_content_type, int use_cpim_pidf_ns)
+{
+	dstring_t buf;
+	int err;
+
+	if(!dst)
+		return -1;
+
+	str_clear(dst);
+	if(dst_content_type)
+		str_clear(dst_content_type);
+
+	if(!p)
+		return -1;
+
+	if(dst_content_type) {
+		if(use_cpim_pidf_ns)
+			err = str_dup_zt(dst_content_type, "application/cpim-pidf+xml");
+		else
+			err = str_dup_zt(
+					dst_content_type, "application/pidf+xml;charset=\"UTF-8\"");
+		if(err < 0)
+			return -1;
+	}
+
+	/*	if (!p->first_tuple) return 0;*/ /* no tuples => nothing to say */
+
+	dstr_init(&buf, 2048);
+
+	dstr_append_zt(&buf, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n");
+	doc_add_presentity(&buf, p, use_cpim_pidf_ns);
+
+	err = dstr_get_str(&buf, dst);
+	dstr_destroy(&buf);
+
+	if(err != 0) {
+		str_free_content(dst);
+		if(dst_content_type)
+			str_free_content(dst_content_type);
+	}
+
+	return err;
+}
+
+int create_pidf_document(
+		presentity_info_t *p, str_t *dst, str_t *dst_content_type)
+{
+	return create_pidf_document_ex(p, dst, dst_content_type, 0);
+}
+
+/* ------------------------------ PIDF document parsing ------------------------------ */
+
+static char *pidf_ns = "urn:ietf:params:xml:ns:pidf";
+/* static char *rpid_ns = "urn:ietf:params:xml:ns:pidf:rpid"; */
+/* static char *data_model_ns = "urn:ietf:params:xml:ns:pidf:data-model"; */
+
+static int read_note(xmlNode *node, presence_note_t **dst)
+{
+	const char *note = NULL;
+	const char *lang = NULL;
+
+	note = get_node_value(node);
+	lang = get_attr_value(find_attr(node->properties, "lang"));
+
+	*dst = create_presence_note_zt(note, lang);
+	if(!dst)
+		return -1;
+
+	return 0;
+}
+
+static int get_whole_node_content(xmlNode *n, str_t *dst, xmlDocPtr doc)
+{
+	int res = 0;
+
+	str_clear(dst);
+	if(n) {
+		n = xmlCopyNode(
+				n, 1); /* this inserts namespaces into element correctly */
+		if(!n) {
+			ERROR_LOG("can't duplicate XML node\n");
+			return -1;
+		}
+	}
+	if(n) {
+		xmlBufferPtr buf;
+		buf = xmlBufferCreate();
+		if(buf == NULL) {
+			ERROR_LOG("Error creating the xml buffer\n");
+			return -1;
+		}
+		if(xmlNodeDump(buf, doc, n, 0, 0) < 0)
+			res = -1;
+		if((res == 0) && (buf->use > 0)) {
+			str_t s;
+			s.s = (char *)buf->content;
+			s.len = buf->use;
+			res = str_dup(dst, &s);
+		}
+		xmlBufferFree(buf);
+		xmlFreeNode(n); /* was duplicated due to namespaces! */
+	}
+	return res;
+}
+
+static int read_extension(xmlNode *ex, extension_element_t **dst, xmlDocPtr doc)
+{
+	extension_element_t *e;
+	/* xmlNode *n; */
+
+	if(!dst)
+		return -1;
+	*dst = NULL;
+
+	e = (extension_element_t *)cds_malloc(sizeof(extension_element_t));
+	if(!e)
+		return -1;
+
+	memset(e, 0, sizeof(*e));
+	*dst = e;
+
+	/* do not care about internals - take whole element ! */
+	if(get_whole_node_content(ex, &e->element, doc) != 0) {
+		cds_free(e);
+		*dst = NULL;
+		return -1;
+	}
+
+	return 0;
+}
+
+static int read_tuple(xmlNode *tuple, presence_tuple_info_t **dst,
+		int ignore_ns, xmlDocPtr doc)
+{
+	str_t contact, id;
+	basic_tuple_status_t status;
+	xmlNode *n, *status_node;
+	double priority = 0;
+	const char *s;
+	int res = 0;
+	presence_note_t *note;
+	char *ns = ignore_ns ? NULL : pidf_ns;
+	extension_element_t *ex;
+
+	*dst = NULL;
+
+	DEBUG_LOG("read_tuple()\n");
+	/* process contact (only one node) */
+	n = find_node(tuple, "contact", ns);
+	if(!n) {
+		/* ERROR_LOG("contact not found\n"); */
+		str_clear(&contact);
+		/* return -1; */
+	} else {
+		s = get_attr_value(find_attr(n->properties, "priority"));
+		if(s)
+			priority = atof(s);
+		s = get_node_value(n);
+		contact.s = (char *)s;
+		if(s)
+			contact.len = strlen(s);
+		else
+			contact.len = 0;
+		if(contact.len < 1) {
+			ERROR_LOG("empty contact using default\n");
+			/* return -1; */
+		}
+	}
+
+	/* process status (only one node) */
+	status_node = find_node(tuple, "status", ns);
+	if(!status_node) {
+		ERROR_LOG("status not found\n");
+		return -1;
+	}
+	n = find_node(status_node, "basic", ns);
+	if(!n) {
+		ERROR_LOG("basic status not found - using \'closed\'\n");
+		/* return -1; */
+		s = "closed";
+	} else
+		s = get_node_value(n);
+	if(!s) {
+		ERROR_LOG("basic status without value\n");
+		return -1;
+	}
+
+	/* translate status */
+	status = presence_tuple_closed; /* default value */
+	if(strcasecmp(s, "open") == 0)
+		status = presence_tuple_open;
+	if(strcasecmp(s, "closed") == 0)
+		status = presence_tuple_closed;
+	/* NOTE: handle not standardized variants too (add note to basic status) */
+
+	/* get ID from tuple node attribute? */
+	id.s = (char *)get_attr_value(find_attr(tuple->properties, "id"));
+	if(id.s)
+		id.len = strlen(id.s);
+	else
+		id.len = 0;
+
+	*dst = create_tuple_info(&contact, &id, status);
+	if(!(*dst))
+		return -1;
+
+	(*dst)->priority = priority;
+
+	/* handle nested elements */
+	n = tuple->children;
+	while(n) {
+		if(n->type == XML_ELEMENT_NODE) {
+			if(cmp_node(n, "note", ns) >= 0) {
+				res = read_note(n, &note);
+				if((res == 0) && note) {
+					DOUBLE_LINKED_LIST_ADD(
+							(*dst)->first_note, (*dst)->last_note, note);
+				} else
+					break;
+			} else if(cmp_node(n, "contact", ns) >= 0) {
+				/* skip, already processed */
+			} else if(cmp_node(n, "status", ns) >= 0) {
+				/* skip, already processed */
+			} else if(cmp_node(n, "timestamp", ns) >= 0) {
+				/* TODO: process */
+			} else { /* PIDF extensions - only from non-PIDF namespace? */
+				res = read_extension(n, &ex, doc);
+				if((res == 0) && ex)
+					DOUBLE_LINKED_LIST_ADD((*dst)->first_unknown_element,
+							(*dst)->last_unknown_element, ex);
+			}
+		}
+		n = n->next;
+	}
+
+	/* handle nested elements in status */
+	if(status_node)
+		n = status_node->children;
+	else
+		n = NULL;
+	while(n) {
+		if(n->type == XML_ELEMENT_NODE) {
+			if(cmp_node(n, "basic", ns) >= 0) {
+				/* skip, already processed */
+			} else { /* PIDF extensions - only from non-PIDF namespace? */
+				res = read_extension(n, &ex, doc);
+				if((res == 0) && ex)
+					DOUBLE_LINKED_LIST_ADD((*dst)->status.first_unknown_element,
+							(*dst)->status.last_unknown_element, ex);
+			}
+		}
+		n = n->next;
+	}
+
+	return res;
+}
+
+static int read_presentity(
+		xmlNode *root, presentity_info_t **dst, int ignore_ns, xmlDocPtr doc)
+{
+	xmlNode *n;
+	str_t entity;
+	presence_tuple_info_t *t;
+	presence_note_t *note;
+	int res = 0;
+	char *ns = ignore_ns ? NULL : pidf_ns;
+	extension_element_t *ex;
+
+	/* TRACE_LOG("read_presentity(ns=%s)\n", ns ? ns : ""); */
+	if(cmp_node(root, "presence", ns) < 0) {
+		ERROR_LOG("document is not presence \n");
+		return -1;
+	}
+
+	entity = zt2str(
+			(char *)get_attr_value(find_attr(root->properties, "entity")));
+	*dst = create_presentity_info(&entity);
+	if(!(*dst))
+		return -1; /* memory */
+
+	n = root->children;
+	while(n) {
+		if(n->type == XML_ELEMENT_NODE) {
+			if(cmp_node(n, "tuple", ns) >= 0) {
+				res = read_tuple(n, &t, ignore_ns, doc);
+				if((res == 0) && t)
+					add_tuple_info(*dst, t);
+				else
+					break;
+			} else if(cmp_node(n, "note", ns) >= 0) {
+				res = read_note(n, &note);
+				if((res == 0) && note) {
+					DOUBLE_LINKED_LIST_ADD(
+							(*dst)->first_note, (*dst)->last_note, note);
+				} else
+					break;
+			} else { /* PIDF extensions - only from non-PIDF namespace? */
+				res = read_extension(n, &ex, doc);
+				if((res == 0) && ex)
+					DOUBLE_LINKED_LIST_ADD((*dst)->first_unknown_element,
+							(*dst)->last_unknown_element, ex);
+				/*if (res != 0) break; ignore errors there */
+			}
+		}
+		n = n->next;
+	}
+
+	return res;
+}
+
+/* ignore ns added for cpim-pidf+xml, draft version 07 (differs only in ns) */
+int parse_pidf_document_ex(
+		presentity_info_t **dst, const char *data, int data_len, int ignore_ns)
+{
+	int res = 0;
+	xmlDocPtr doc;
+
+	if(!dst)
+		return -1;
+	if((!data) || (data_len < 1))
+		return -2;
+
+	*dst = NULL;
+	doc = xmlReadMemory(data, data_len, NULL, NULL, xml_parser_flags);
+	if(doc == NULL) {
+		ERROR_LOG("can't parse document\n");
+		return -1;
+	}
+
+	res = read_presentity(xmlDocGetRootElement(doc), dst, ignore_ns, doc);
+	if(res != 0) {
+		/* may be set => must be freed */
+		if(*dst)
+			free_presentity_info(*dst);
+		*dst = NULL;
+	}
+
+	xmlFreeDoc(doc);
+	return res;
+}
+
+/* libxml2 must be initialized before calling this function ! */
+int parse_pidf_document(presentity_info_t **dst, const char *data, int data_len)
+{
+	return parse_pidf_document_ex(dst, data, data_len, 0);
+}
+
+/* --------------- CPIM_PIDF document creation/parsing ---------------- */
+
+int parse_cpim_pidf_document(
+		presentity_info_t **dst, const char *data, int data_len)
+{
+	return parse_pidf_document_ex(dst, data, data_len, 1);
+}
+
+int create_cpim_pidf_document(
+		presentity_info_t *p, str_t *dst, str_t *dst_content_type)
+{
+	return create_pidf_document_ex(p, dst, dst_content_type, 1);
+}

+ 42 - 0
src/lib/presence/pidf.h

@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2005 iptelorg GmbH
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __PRESENCE_PIDF_H
+#define __PRESENCE_PIDF_H
+
+#include <cds/sstr.h>
+#include <presence/pres_doc.h>
+
+int create_pidf_document(
+		presentity_info_t *p, str_t *dst, str_t *dst_content_type);
+int create_cpim_pidf_document(
+		presentity_info_t *p, str_t *dst, str_t *dst_content_type);
+
+int parse_pidf_document(
+		presentity_info_t **dst, const char *data, int data_len);
+int parse_cpim_pidf_document(
+		presentity_info_t **dst, const char *data, int data_len);
+
+#endif

+ 408 - 0
src/lib/presence/pres_doc.c

@@ -0,0 +1,408 @@
+/*
+ * Copyright (C) 2005 iptelorg GmbH
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <presence/pres_doc.h>
+#include <cds/memory.h>
+#include <cds/logger.h>
+#include <cds/list.h>
+
+#include <string.h>
+
+/* ---------------------------------------------------------------- */
+/* Helper functions */
+
+static str_t open = STR_STATIC_INIT("open");
+static str_t closed = STR_STATIC_INIT("closed");
+static str_t unknown = STR_STATIC_INIT("undefined");
+
+str_t *tuple_status2str(basic_tuple_status_t status)
+{
+	switch(status) {
+		case presence_tuple_open:
+			return &open;
+		case presence_tuple_closed:
+			return &closed;
+		case presence_tuple_undefined_status:
+			return &unknown;
+	}
+	return &unknown;
+}
+
+basic_tuple_status_t str2tuple_status(const str_t *s)
+{
+	if(str_nocase_equals(s, &open) == 0)
+		return presence_tuple_open;
+	if(str_nocase_equals(s, &closed) == 0)
+		return presence_tuple_closed;
+	return presence_tuple_undefined_status;
+}
+
+/* ---------------------------------------------------------------- */
+
+presentity_info_t *create_presentity_info(const str_t *presentity_uri)
+{
+	presentity_info_t *p;
+	int len = 0;
+
+	if(!is_str_empty(presentity_uri))
+		len = presentity_uri->len;
+	p = (presentity_info_t *)cds_malloc(sizeof(presentity_info_t) + len);
+	if(!p) {
+		ERROR_LOG("can't allocate memory for presentity info\n");
+		return p;
+	}
+	p->uri.len = len;
+	if(len > 0) {
+		p->uri.s = p->presentity_data;
+		memcpy(p->uri.s, presentity_uri->s, len);
+	} else
+		p->uri.s = NULL;
+	p->first_tuple = NULL;
+	p->last_tuple = NULL;
+	p->first_note = NULL;
+	p->last_note = NULL;
+
+	/* extensions */
+	p->first_unknown_element = NULL;
+	p->last_unknown_element = NULL;
+
+	return p;
+}
+
+presence_tuple_info_t *create_tuple_info(
+		const str_t *contact, const str_t *id, basic_tuple_status_t status)
+{
+	presence_tuple_info_t *t;
+	t = (presence_tuple_info_t *)cds_malloc(sizeof(*t));
+	if(!t) {
+		ERROR_LOG("can't allocate memory for presence tuple info\n");
+		return t;
+	}
+	/* str_clear(&t->contact.s); */
+	if(str_dup(&t->contact, contact) != 0) {
+		ERROR_LOG("can't allocate memory for contact\n");
+		cds_free(t);
+		return NULL;
+	}
+	if(str_dup(&t->id, id) != 0) {
+		ERROR_LOG("can't allocate memory for id\n");
+		str_free_content(&t->contact);
+		cds_free(t);
+		return NULL;
+	}
+	t->prev = NULL;
+	t->next = NULL;
+	t->status.basic = status;
+	t->status.first_unknown_element = NULL;
+	t->status.last_unknown_element = NULL;
+	t->priority = 0.0;
+	t->first_note = NULL;
+	t->last_note = NULL;
+	t->first_unknown_element = NULL;
+	t->last_unknown_element = NULL;
+	return t;
+}
+
+void add_tuple_info(presentity_info_t *p, presence_tuple_info_t *t)
+{
+	DOUBLE_LINKED_LIST_ADD(p->first_tuple, p->last_tuple, t);
+}
+
+void free_presence_note(presence_note_t *n)
+{
+	if(n) {
+		str_free_content(&n->value);
+		str_free_content(&n->lang);
+		cds_free(n);
+	}
+}
+
+void free_extension_element(extension_element_t *p)
+{
+	if(p) {
+		/* TODO: allocate element together with the structure */
+		str_free_content(&p->element);
+	}
+	cds_free(p);
+}
+
+void free_tuple_info(presence_tuple_info_t *t)
+{
+	presence_note_t *n, *nn;
+	extension_element_t *e, *ne;
+
+	if(!t)
+		return;
+	str_free_content(&t->contact);
+	str_free_content(&t->id);
+
+	n = t->first_note;
+	while(n) {
+		nn = n->next;
+		free_presence_note(n);
+		n = nn;
+	}
+
+	e = t->first_unknown_element;
+	while(e) {
+		ne = e->next;
+		free_extension_element(e);
+		e = ne;
+	}
+
+	e = t->status.first_unknown_element;
+	while(e) {
+		ne = e->next;
+		free_extension_element(e);
+		e = ne;
+	}
+
+	cds_free(t);
+}
+
+void free_presentity_info(presentity_info_t *p)
+{
+	presence_tuple_info_t *t, *tt;
+	presence_note_t *n, *nn;
+	extension_element_t *np, *ps;
+
+	if(!p)
+		return;
+	t = p->first_tuple;
+	while(t) {
+		tt = t->next;
+		free_tuple_info(t);
+		t = tt;
+	}
+
+	n = p->first_note;
+	while(n) {
+		nn = n->next;
+		free_presence_note(n);
+		n = nn;
+	}
+
+	ps = p->first_unknown_element;
+	while(ps) {
+		np = ps->next;
+		free_extension_element(ps);
+		ps = np;
+	}
+
+	cds_free(p);
+}
+
+raw_presence_info_t *create_raw_presence_info(const str_t *uri)
+{
+	raw_presence_info_t *p;
+	int len = 0;
+
+	if(!is_str_empty(uri))
+		len = uri->len;
+	p = (raw_presence_info_t *)cds_malloc(sizeof(raw_presence_info_t) + len);
+	if(!p) {
+		ERROR_LOG("can't allocate memory for list presence info\n");
+		return p;
+	}
+	p->uri.len = len;
+	if(len > 0) {
+		p->uri.s = p->uri_data;
+		memcpy(p->uri.s, uri->s, len);
+	} else
+		p->uri.s = NULL;
+
+	str_clear(&p->pres_doc);
+	str_clear(&p->content_type);
+
+	return p;
+}
+
+void free_raw_presence_info(raw_presence_info_t *p)
+{
+	if(p) {
+		DEBUG_LOG(" ... freeing doc\n");
+		str_free_content(&p->pres_doc);
+		DEBUG_LOG(" ... freeing content type\n");
+		str_free_content(&p->content_type);
+		DEBUG_LOG(" ... freeing list presence info\n");
+		cds_free(p);
+	}
+}
+
+presence_note_t *create_presence_note(const str_t *note, const str_t *lang)
+{
+	presence_note_t *t;
+	t = (presence_note_t *)cds_malloc(sizeof(*t));
+	if(!t) {
+		ERROR_LOG("can't allocate memory for presence note\n");
+		return t;
+	}
+	/* str_clear(&t->contact.s); */
+	if(str_dup(&t->value, note) < 0) {
+		ERROR_LOG("can't duplicate note value\n");
+		cds_free(t);
+		return NULL;
+	}
+	if(str_dup(&t->lang, lang) < 0) {
+		ERROR_LOG("can't duplicate note lang\n");
+		str_free_content(&t->value);
+		cds_free(t);
+		return NULL;
+	}
+	t->prev = NULL;
+	t->next = NULL;
+	return t;
+}
+
+presence_note_t *create_presence_note_zt(const char *note, const char *lang)
+{
+	str_t note_s;
+	str_t lang_s;
+
+	note_s = zt2str((char *)note);
+	lang_s = zt2str((char *)lang);
+
+	return create_presence_note(&note_s, &lang_s);
+}
+
+extension_element_t *create_extension_element(const str_t *element)
+{
+	extension_element_t *t;
+	t = (extension_element_t *)cds_malloc(sizeof(*t));
+	if(!t) {
+		ERROR_LOG("can't allocate memory for person\n");
+		return t;
+	}
+	if(str_dup(&t->element, element) < 0) {
+		ERROR_LOG("can't duplicate person element\n");
+		cds_free(t);
+		return NULL;
+	}
+
+	t->prev = NULL;
+	t->next = NULL;
+	return t;
+}
+
+/*************************************************************/
+static int copy_tuple_notes(
+		presence_tuple_info_t *dst_info, const presence_tuple_info_t *src)
+{
+	presence_note_t *n, *nn;
+
+	n = src->first_note;
+	while(n) {
+		nn = create_presence_note(&n->value, &n->lang);
+		if(!nn) {
+			ERROR_LOG("can't create presence note\n");
+			return -1;
+		}
+		DOUBLE_LINKED_LIST_ADD(dst_info->first_note, dst_info->last_note, nn);
+		n = n->next;
+	}
+	return 0;
+}
+
+presentity_info_t *dup_presentity_info(presentity_info_t *p)
+{
+	presentity_info_t *pinfo;
+	presence_tuple_info_t *tinfo, *t;
+	presence_note_t *n, *pan;
+	extension_element_t *ps, *paps;
+	int err = 0;
+
+	/* DBG("p2p_info()\n"); */
+	if(!p)
+		return NULL;
+	/*	pinfo = (presentity_info_t*)cds_malloc(sizeof(*pinfo)); */
+	pinfo = create_presentity_info(&p->uri);
+	if(!pinfo) {
+		ERROR_LOG("can't allocate memory\n");
+		return NULL;
+	}
+	/* DBG("p2p_info(): created presentity info\n"); */
+
+	t = p->first_tuple;
+	while(t) {
+		tinfo = create_tuple_info(&t->contact, &t->id, t->status.basic);
+		if(!tinfo) {
+			ERROR_LOG("can't create tuple info\n");
+			err = 1;
+			break;
+		}
+		tinfo->priority = t->priority;
+		/* tinfo->expires = t->expires; ??? */
+		add_tuple_info(pinfo, tinfo);
+		if(copy_tuple_notes(tinfo, t) < 0) {
+			ERROR_LOG("can't copy tuple notes\n");
+			err = 1;
+			break;
+		}
+		/* TODO: duplicate status extension elements */
+		/* TODO: duplicate extension elements */
+		t = t->next;
+	}
+
+	/* notes */
+	if(!err) {
+		pan = p->first_note;
+		while(pan) {
+			n = create_presence_note(&pan->value, &pan->lang);
+			if(n)
+				DOUBLE_LINKED_LIST_ADD(pinfo->first_note, pinfo->last_note, n);
+			else {
+				ERROR_LOG("can't copy presence notes\n");
+				err = 1;
+				break;
+			}
+			pan = pan->next;
+		}
+	}
+
+	/* extension elements */
+	if(!err) {
+		paps = p->first_unknown_element;
+		while(paps) {
+			ps = create_extension_element(&paps->element);
+			if(ps)
+				DOUBLE_LINKED_LIST_ADD(pinfo->first_unknown_element,
+						pinfo->last_unknown_element, ps);
+			else {
+				ERROR_LOG("can't copy person elements\n");
+				err = 1;
+				break;
+			}
+			paps = paps->next;
+		}
+	}
+
+	if(err) {
+		free_presentity_info(pinfo);
+		return NULL;
+	}
+
+	/* DBG("p2p_info() finished\n"); */
+	return pinfo;
+}

+ 140 - 0
src/lib/presence/pres_doc.h

@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2005 iptelorg GmbH
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __PRESENCE_INFO_H
+#define __PRESENCE_INFO_H
+
+#include <cds/sstr.h>
+#include <cds/ptr_vector.h>
+#include <time.h>
+
+typedef struct _presence_note_t
+{
+	str_t value;
+	str_t lang;
+	struct _presence_note_t *prev, *next;
+} presence_note_t;
+
+typedef enum
+{
+	presence_tuple_open,
+	presence_tuple_closed,
+	presence_tuple_undefined_status
+} basic_tuple_status_t;
+
+typedef enum
+{
+	presence_auth_rejected,
+	presence_auth_polite_block,
+	presence_auth_unresolved,
+	presence_auth_granted
+} presence_authorization_status_t;
+
+/* additional data which need not to be understood by us */
+typedef struct _extension_element_t
+{
+	str_t element;
+	/* TODO: add mustUnderstand attribute and its handling */
+	struct _extension_element_t *next,
+			*prev; /* there can be more person elements in PIDF */
+} extension_element_t;
+
+typedef struct
+{
+	basic_tuple_status_t basic;
+	extension_element_t *first_unknown_element, *last_unknown_element;
+} presence_tuple_status_t;
+
+typedef struct _presence_tuple_info_t
+{
+	str_t contact;
+	str_t id;
+	double priority;
+	presence_tuple_status_t status;
+	extension_element_t *first_unknown_element, *last_unknown_element;
+	struct _presence_tuple_info_t *next, *prev;
+	presence_note_t *first_note, *last_note; /* published notes */
+											 /* TODO: add timestamp element */
+} presence_tuple_info_t;
+
+typedef struct
+{
+	str_t uri; /* do not modify this !*/
+	presence_tuple_info_t *first_tuple, *last_tuple;
+	presence_note_t *first_note, *last_note; /* published notes */
+	extension_element_t *first_unknown_element, *last_unknown_element;
+
+	char presentity_data[1];
+} presentity_info_t;
+
+typedef struct
+{
+	str_t uri; /* do not modify this !*/
+
+	str_t pres_doc;
+	str_t content_type;
+	char uri_data[1];
+} raw_presence_info_t;
+
+typedef struct
+{
+	str_t list_uri; /* do not modify this !*/
+
+	str_t pres_doc;
+	str_t content_type;
+	char uri_data[1];
+} presence_info_t;
+
+presentity_info_t *create_presentity_info(const str_t *presentity);
+presence_tuple_info_t *create_tuple_info(
+		const str_t *contact, const str_t *id, basic_tuple_status_t status);
+void add_tuple_info(presentity_info_t *p, presence_tuple_info_t *t);
+void free_presentity_info(presentity_info_t *p);
+
+raw_presence_info_t *create_raw_presence_info(const str_t *uri);
+void free_raw_presence_info(raw_presence_info_t *p);
+
+presence_note_t *create_presence_note(const str_t *note, const str_t *lang);
+presence_note_t *create_presence_note_zt(const char *note, const char *lang);
+void free_presence_note(presence_note_t *n);
+
+extension_element_t *create_extension_element(const str_t *element);
+void free_extension_element(extension_element_t *p);
+
+/** returns pointer to constant string (do not free it!),
+ * the return value is never NULL */
+str_t *tuple_status2str(basic_tuple_status_t status);
+
+basic_tuple_status_t str2tuple_status(const str_t *s);
+
+/* duplicates presentity info */
+presentity_info_t *dup_presentity_info(presentity_info_t *p);
+
+/* content type names usable with QSA */
+#define CT_PRESENCE_INFO "structured/presence-info" /* uses presence_info_t */
+#define CT_PIDF_XML "application/pidf+xml"			/* carries XML */
+#define CT_RAW "raw" /* uses raw_presence_info_t */
+
+#endif

+ 111 - 0
src/lib/presence/qsa.c

@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2005 iptelorg GmbH
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <presence/qsa.h>
+#include <cds/logger.h>
+#include <cds/cds.h>
+#include <presence/domain_maintainer.h>
+
+typedef struct
+{
+	int init_cnt;
+	domain_maintainer_t *dm;
+} init_data_t;
+
+static init_data_t *init = NULL;
+
+int qsa_initialize()
+{
+	int res = 0;
+
+	/* initialization should be called from one process/thread
+	 * it is not synchronized because it is impossible ! */
+	if(!init) {
+		init = (init_data_t *)cds_malloc(sizeof(init_data_t));
+		if(!init)
+			return -1;
+		init->init_cnt = 0;
+	}
+
+	if(init->init_cnt > 0) { /* already initialized */
+		init->init_cnt++;
+		return 0;
+	} else {
+		DEBUG_LOG("init the content\n");
+
+		/* !!! put the real initialization here !!! */
+		init->dm = create_domain_maintainer();
+		if(!init->dm) {
+			ERROR_LOG("qsa_initialize error - can't initialize domain "
+					  "maintainer\n");
+			res = -1;
+		}
+	}
+
+	if(res == 0)
+		init->init_cnt++;
+	return res;
+}
+
+void qsa_cleanup()
+{
+	if(init) {
+		if(--init->init_cnt == 0) {
+			DEBUG_LOG("cleaning the content\n");
+
+			/* !!! put the real destruction here !!! */
+			if(init->dm)
+				destroy_domain_maintainer(init->dm);
+
+			cds_free(init);
+			init = NULL;
+		}
+	}
+}
+
+notifier_domain_t *qsa_register_domain(const str_t *name)
+{
+	notifier_domain_t *d = NULL;
+
+	if(!init) {
+		ERROR_LOG("qsa_initialize was not called - can't register domain\n");
+		return NULL;
+	}
+	if(init->dm)
+		d = register_notifier_domain(init->dm, name);
+	return d;
+}
+
+notifier_domain_t *qsa_get_default_domain()
+{
+	return qsa_register_domain(NULL);
+}
+
+void qsa_release_domain(notifier_domain_t *domain)
+{
+	if(init)
+		if(init->dm)
+			release_notifier_domain(init->dm, domain);
+}

+ 48 - 0
src/lib/presence/qsa.h

@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2005 iptelorg GmbH
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __QSA_H
+#define __QSA_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <cds/sstr.h>
+#include <presence/notifier_domain.h>
+
+	int qsa_initialize();
+	void qsa_cleanup();
+
+	notifier_domain_t *qsa_register_domain(const str_t *name);
+	notifier_domain_t *qsa_get_default_domain();
+	void qsa_release_domain(notifier_domain_t *domain);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 1 - 0
src/lib/presence/qsa_params.c

@@ -0,0 +1 @@
+#include <presence/qsa_params.h>

+ 25 - 0
src/lib/presence/qsa_params.h

@@ -0,0 +1,25 @@
+#ifndef __QSA_PARAMS_H
+#define __QSA_PARAMS_H
+
+#include <cds/sstr.h>
+#include <cds/ptr_vector.h>
+#include <cds/msg_queue.h>
+#include <cds/ref_cntr.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+	typedef struct _qsa_subscription_params_t
+	{
+		str_t name;
+		str_t value; /* whatever */
+		char buf[1];
+	} qsa_subscription_params_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 57 - 0
src/lib/presence/subscriber.h

@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2005 iptelorg GmbH
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __SUBSCRIBER_H
+#define __SUBSCRIBER_H
+
+/* Subscriber functions for notifier_domain */
+
+#include <presence/notifier_domain.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+	/* If a notifier publishing watched state registers after subscribe
+ * call, it receives the subscription automatically too! */
+	/*qsa_subscription_t *subscribe(notifier_domain_t *domain,
+		qsa_subscription_t *params);*/
+	qsa_subscription_t *subscribe(notifier_domain_t *domain, str_t *package,
+			qsa_subscription_data_t *data);
+
+	/** Destroys an existing subscription */
+	void unsubscribe(notifier_domain_t *domain, qsa_subscription_t *s);
+
+	void set_subscriber_data(qsa_subscription_t *s, void *data);
+	void *get_subscriber_data(qsa_subscription_t *s);
+
+	void clear_subscription_data(qsa_subscription_data_t *data);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 42 - 0
src/lib/presence/subscription_info.h

@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2005 iptelorg GmbH
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __SUBSCRIPTION_INFO
+#define __SUBSCRIPTION_INFO
+
+#include <presence/notifier_domain.h>
+
+typedef struct _subscription_info_t
+{
+
+	client_notify_func notify;
+	str_t record_id;
+	str_t package;
+	subscription_t *subscription;
+
+	struct _subscription_info_t *prev, *next;
+} subscription_info_t;
+
+#endif

+ 56 - 0
src/lib/presence/utils.c

@@ -0,0 +1,56 @@
+#include "utils.h"
+#include <stdio.h>
+#include <string.h>
+
+#ifdef SER
+
+int extract_server_contact(struct sip_msg *m, str *dst, int uri_only)
+{
+	char *tmp = "";
+	if(!dst)
+		return -1;
+
+	switch(m->rcv.bind_address->proto) {
+		case PROTO_NONE:
+			break;
+		case PROTO_UDP:
+			break;
+		case PROTO_TCP:
+			tmp = ";transport=tcp";
+			break;
+		case PROTO_TLS:
+			tmp = ";transport=tls";
+			break;
+		case PROTO_SCTP:
+			tmp = ";transport=sctp";
+			break;
+		default:
+			LOG(L_CRIT, "BUG: extract_server_contact: unknown proto %d\n",
+					m->rcv.bind_address->proto);
+	}
+
+	dst->len = 7 + m->rcv.bind_address->name.len
+			   + m->rcv.bind_address->port_no_str.len + strlen(tmp);
+	if(!uri_only)
+		dst->len += 11;
+	dst->s = (char *)cds_malloc(dst->len + 1);
+	if(!dst->s) {
+		dst->len = 0;
+		return -1;
+	}
+	if(uri_only) {
+		snprintf(dst->s, dst->len + 1, "<sip:%.*s:%.*s%s>",
+				m->rcv.bind_address->name.len, m->rcv.bind_address->name.s,
+				m->rcv.bind_address->port_no_str.len,
+				m->rcv.bind_address->port_no_str.s, tmp);
+	} else {
+		snprintf(dst->s, dst->len + 1, "Contact: <sip:%.*s:%.*s%s>\r\n",
+				m->rcv.bind_address->name.len, m->rcv.bind_address->name.s,
+				m->rcv.bind_address->port_no_str.len,
+				m->rcv.bind_address->port_no_str.s, tmp);
+	}
+
+	return 0;
+}
+
+#endif

+ 10 - 0
src/lib/presence/utils.h

@@ -0,0 +1,10 @@
+#ifdef SER
+
+#include <cds/sstr.h>
+#include "parser/msg_parser.h"
+#include <cds/memory.h>
+
+/* if set uri_only it extracts only uri, not full Contact header */
+int extract_server_contact(struct sip_msg *m, str *dst, int uri_only);
+
+#endif

+ 156 - 0
src/lib/presence/xml_utils.c

@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2005 iptelorg GmbH
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#include <cds/logger.h>
+#include <xcap/xml_utils.h>
+
+int xml_parser_flags = XML_PARSE_NOERROR | XML_PARSE_NOWARNING;
+
+static int str2int(const char *s, int *dst)
+{
+	/* if not null sets the given integer to value */
+	if(!s) {
+		*dst = 0;
+		return -1;
+	} else
+		*dst = atoi(s);
+	return 0;
+}
+
+int get_int_attr(xmlNode *n, const char *attr_name, int *dst)
+{
+	const char *s = get_attr_value(find_attr(n->properties, attr_name));
+	if(!s)
+		return 1;
+	return str2int(s, dst);
+}
+
+int get_str_attr(xmlNode *n, const char *attr_name, str_t *dst)
+{
+	const char *s = get_attr_value(find_attr(n->properties, attr_name));
+	if(!s) {
+		str_clear(dst);
+		return 1;
+	} else
+		return str_dup_zt(dst, s);
+}
+
+int xmlstrcmp(const xmlChar *xmls, const char *name)
+{
+	if(!xmls)
+		return -1;
+	if(!name)
+		return 1;
+	return strcmp((const char *)xmls, name);
+}
+
+/* xmlNode *find_node(xmlNode *parent, const char *name) */
+xmlNode *find_node(xmlNode *parent, const char *name, const char *nspace)
+{
+	if(!parent)
+		return NULL;
+	xmlNode *n = parent->children;
+	while(n) {
+		if(cmp_node(n, name, nspace) >= 0)
+			break;
+		n = n->next;
+	}
+	return n;
+}
+
+const char *find_value(xmlNode *first_child)
+{
+	const char *s = NULL;
+
+	xmlNode *c = first_child;
+	while(c) {
+		if(c->type == XML_TEXT_NODE) {
+			if(c->content)
+				s = (const char *)c->content;
+			break;
+		}
+		c = c->next;
+	}
+
+	return s;
+}
+
+const char *get_node_value(xmlNode *n)
+{
+	if(!n)
+		return NULL;
+	return find_value(n->children);
+}
+
+xmlAttr *find_attr(xmlAttr *first, const char *name)
+{
+	xmlAttr *a = first;
+	while(a) {
+		if(xmlstrcmp(a->name, name) == 0)
+			break;
+		a = a->next;
+	}
+	return a;
+}
+
+const char *get_attr_value(xmlAttr *a)
+{
+	if(!a)
+		return NULL;
+	return find_value(a->children);
+}
+
+int cmp_node(xmlNode *node, const char *name, const char *nspace)
+{
+	if(!node)
+		return -1;
+	if(node->type != XML_ELEMENT_NODE)
+		return -1;
+
+	if(xmlstrcmp(node->name, name) != 0)
+		return -1;
+	if(!nspace)
+		return 0;
+	if(!node->ns) {
+		/* DEBUG_LOG("nemam NS!!!!!!!\n"); */
+		return 1;
+	}
+	if(xmlstrcmp(node->ns->href, nspace) == 0)
+		return 0;
+	return -1;
+}
+
+time_t xmltime2time(const char *xt)
+{
+	/* TODO: translate XML time in input parameter to time_t structure */
+	ERROR_LOG("can't translate xmltime to time_t: not finished yet!\n");
+	return 0;
+}

+ 56 - 0
src/lib/presence/xml_utils.h

@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2005 iptelorg GmbH
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+
+#include <cds/sstr.h>
+
+int xmlstrcmp(const xmlChar *xmls, const char *name);
+xmlAttr *find_attr(xmlAttr *first, const char *name);
+const char *find_value(xmlNode *first_child);
+const char *get_node_value(xmlNode *n);
+xmlNode *find_node(xmlNode *parent, const char *name, const char *nspace);
+const char *get_attr_value(xmlAttr *a);
+int cmp_node(xmlNode *node, const char *name, const char *nspace);
+void get_int_attr(xmlNode *n, const char *attr_name, int *dst);
+void get_str_attr(xmlNode *n, const char *attr_name, str_t *dst);
+
+time_t xmltime2time(const char *xt);
+
+#define SEQUENCE(type) type *
+#define SEQUENCE_ABLE(type) type *__next;
+#define SEQUENCE_ADD(first, last, e) \
+	do {                             \
+		if(last)                     \
+			last->__next = e;        \
+		else                         \
+			first = e;               \
+		last = e;                    \
+	} while(0);
+#define SEQUENCE_FIRST(first) first
+#define SEQUENCE_NEXT(e) (e)->__next
+
+extern int xml_parser_flags;

+ 178 - 0
src/lib/presence/xpidf.c

@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2005 iptelorg GmbH
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <presence/xpidf.h>
+#include <cds/dstring.h>
+#include <cds/memory.h>
+#include <cds/logger.h>
+#include <cds/list.h>
+#include <presence/xml_utils.h>
+#include <string.h>
+
+/* ------------------------------ XPIDF document creation ------------------------------ */
+
+static void doc_add_tuple_note(dstring_t *buf, presence_note_t *n)
+{
+	dstr_append_zt(buf, "\t\t\t<note>");
+	dstr_append_str(buf, &n->value);
+	dstr_append_zt(buf, "</note>\r\n");
+}
+
+/*static void doc_add_note(dstring_t *buf, presentity_info_t *p, presence_note_t *n)
+{
+	DEBUG_LOG("doc_add_note()\n");
+
+	dstr_append_zt(buf, "\t<note");
+	if (n->lang.len > 0) {
+		dstr_append_zt(buf, " lang=\"");
+		dstr_append_str(buf, &n->lang);
+		dstr_append_zt(buf, "\"");
+	}
+	dstr_append_zt(buf, ">");
+	dstr_append_str(buf, &n->value);
+	dstr_append_zt(buf, "</note>\r\n");
+}*/
+
+static void doc_add_tuple(
+		dstring_t *buf, presentity_info_t *p, presence_tuple_info_t *t)
+{
+	presence_note_t *n;
+	char tmp[32];
+
+	dstr_append_zt(buf, "\t<atom id=\"");
+	dstr_append_str(buf, &t->id);
+	dstr_append_zt(buf, "\">\r\n");
+
+	dstr_append_zt(buf, "\t\t<address uri=\"");
+	dstr_append_str(buf, &t->contact);
+	dstr_append_zt(buf, "\" priority=\"");
+	/*	dstr_append_zt(buf, ";user=ip\" priority=\"");*/
+	sprintf(tmp, "%1.2f", t->priority);
+	dstr_append_zt(buf, tmp);
+	dstr_append_zt(buf, "\">\r\n");
+	if(t->status.basic == presence_tuple_open)
+		dstr_append_zt(buf, "\t\t\t<status status=\"open\"/>\r\n");
+	else
+		dstr_append_zt(buf, "\t\t\t<status status=\"closed\"/>\r\n");
+	dstr_append_zt(buf, "\t\t</address>\r\n");
+
+	/* add tuple notes */
+	n = t->first_note;
+	while(n) {
+		doc_add_tuple_note(buf, n);
+		n = n->next;
+	}
+	/* --- */
+
+	dstr_append_zt(buf, "\t</atom>\r\n");
+}
+
+static void doc_add_empty_tuple(dstring_t *buf, presentity_info_t *p)
+{
+	dstr_append_zt(buf, "\t<atom id=\"none\">\r\n");
+
+	dstr_append_zt(buf, "\t\t<address uri=\"");
+	dstr_append_str(buf, &p->uri);
+	dstr_append_zt(buf, "\" priority=\"1\">\r\n");
+	/*	dstr_append_zt(buf, ";user=ip\" priority=\"1\">\r\n");*/
+	dstr_append_zt(buf, "\t\t\t<status status=\"closed\"/>\r\n");
+	dstr_append_zt(buf, "\t\t</address>\r\n");
+
+	dstr_append_zt(buf, "\t</atom>\r\n");
+}
+
+static void doc_add_presentity(dstring_t *buf, presentity_info_t *p)
+{
+	presence_tuple_info_t *t;
+	/* presence_note_t *n; */
+
+	dstr_append_zt(buf, "<presence>\r\n");
+	/* !!! there SHOULD be pres URI of presentity !!! */
+	dstr_append_zt(buf, "<presentity uri=\"");
+	/* dstr_put_pres_uri(buf, &p->presentity); */
+	dstr_append_str(buf, &p->uri);
+	dstr_append_zt(buf, ";method=SUBSCRIBE\"/>\r\n");
+
+	t = p->first_tuple;
+	if(!t)
+		doc_add_empty_tuple(buf, p);
+	while(t) {
+		doc_add_tuple(buf, p, t);
+		t = t->next;
+	}
+
+	/*
+	n = p->first_note;
+	while (n) {
+		doc_add_note(buf, p, n);
+		n = n->next;
+	}
+*/
+	dstr_append_zt(buf, "</presence>\r\n");
+}
+
+int create_xpidf_document(
+		presentity_info_t *p, str_t *dst, str_t *dst_content_type)
+{
+	dstring_t buf;
+	int err = 0;
+
+	if(!dst)
+		return -1;
+
+	str_clear(dst);
+	if(dst_content_type)
+		str_clear(dst_content_type);
+
+	if(!p)
+		return -1;
+
+	if(dst_content_type)
+		if(str_dup_zt(
+				   dst_content_type, "application/xpidf+xml;charset=\"UTF-8\"")
+				< 0) {
+			return -1;
+		}
+
+	/*	if (!p->first_tuple) return 0;*/ /* no tuples => nothing to say */
+
+	dstr_init(&buf, 2048);
+
+	dstr_append_zt(&buf, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n");
+	dstr_append_zt(&buf, "<!DOCTYPE presence PUBLIC \"-//IETF//DTD RFCxxxx "
+						 "XPIDF 1.0//EN\" \"xpidf.dtd\">\r\n");
+	doc_add_presentity(&buf, p);
+
+	err = dstr_get_str(&buf, dst);
+	dstr_destroy(&buf);
+
+	if(err != 0) {
+		str_free_content(dst);
+		if(dst_content_type)
+			str_free_content(dst_content_type);
+	}
+
+	return err;
+}

+ 35 - 0
src/lib/presence/xpidf.h

@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2005 iptelorg GmbH
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __PRESENCE_LPIDF_H
+#define __PRESENCE_LPIDF_H
+
+#include <cds/sstr.h>
+#include <presence/pres_doc.h>
+
+int create_xpidf_document(
+		presentity_info_t *p, str_t *dst, str_t *dst_content_type);
+
+#endif