浏览代码

- Merge of the implementation of the RFC3680 (reginfo-package) into master

Carsten Bock 14 年之前
父节点
当前提交
552425a497

+ 33 - 0
modules_k/presence_reginfo/Makefile

@@ -0,0 +1,33 @@
+#
+# Presence_MWI Makefile
+# 
+# 
+# WARNING: do not run this directly, it should be run by the master Makefile
+
+include ../../Makefile.defs
+auto_gen=
+NAME=presence_reginfo.so
+DEFS+=-I/usr/include/libxml2 -I$(LOCALBASE)/include/libxml2 \
+      -I$(LOCALBASE)/include
+LIBS+=-L$(LOCALBASE)/lib -lxml2
+
+DEFS+=-DOPENSER_MOD_INTERFACE
+
+include ../../Makefile.modules
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ 52 - 0
modules_k/presence_reginfo/add_events.c

@@ -0,0 +1,52 @@
+/*
+ * presence_reginfo module - Presence Handling of reg events
+ *
+ * Copyright (C) 2011 Carsten Bock, [email protected]
+ * http://www.ng-voice.com
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio 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
+ *
+ * Kamailio 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "../../parser/parse_content.h"
+#include "../presence/event_list.h"
+#include "presence_reginfo.h"
+
+int reginfo_add_events(void)
+{
+	pres_ev_t event;
+	
+	/* constructing message-summary event */
+	memset(&event, 0, sizeof(pres_ev_t));
+	event.name.s = "reg";
+	event.name.len = 3;
+
+	event.content_type.s = "application/reginfo+xml";
+	event.content_type.len = 23;
+	event.default_expires= 3600;
+	event.type = PUBL_TYPE;
+	event.req_auth = 0;
+	event.evs_publ_handl = 0;
+
+	if (pres_add_event(&event) < 0) {
+		LM_ERR("failed to add event \"reginfo\"\n");
+		return -1;
+	}		
+	return 0;
+}

+ 30 - 0
modules_k/presence_reginfo/add_events.h

@@ -0,0 +1,30 @@
+/*
+ * presence_reginfo module - Presence Handling of reg events
+ *
+ * Copyright (C) 2011 Carsten Bock, [email protected]
+ * http://www.ng-voice.com
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio 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
+ *
+ * Kamailio 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+#ifndef _REGINFO_ADD_EV_H_
+#define _REGINFO_ADD_EV_H_
+
+int reginfo_add_events(void);
+
+#endif

+ 4 - 0
modules_k/presence_reginfo/doc/Makefile

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

+ 39 - 0
modules_k/presence_reginfo/doc/presence_reginfo.xml

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

+ 74 - 0
modules_k/presence_reginfo/doc/presence_reginfo_admin.xml

@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding='ISO-8859-1'?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
+"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
+
+<!-- Include general documentation entities -->
+<!ENTITY % docentities SYSTEM "../../../docbook/entities.xml">
+%docentities;
+
+]>
+<!-- Module User's Guide -->
+
+<chapter>
+	
+
+	<title>&adminguide;</title>
+	
+	<section>
+	  <title>Overview</title>
+	    <para> 
+	      The module enables the handling of "Event: reg" (as defined 
+	      in RFC 3680) inside of the presence module. This can be used
+	      distribute the registration-info status to the subscribed watchers.
+	    </para>
+	    <para>
+ 	      The module does not currently implement any authorization
+	      rules.  It assumes that publish requests are only issued by
+	      an authorized application and subscribe requests only by
+	      authorized users.  Authorization can thus be easily done in 
+	      &kamailio; configuration file before calling handle_publish() 
+	      and handle_subscribe() functions.
+	    </para>
+	    <para>
+	      Note: This module only activates the processing of the "reg" 
+	      in the presence module. To send dialog-info to watchers you also 
+	      need a source which PUBLISH the reg info to the presence module.
+	      For example you can use the pua_reginfo module or any external
+	      component. This approach allows to have the presence server and the
+	      reg-info aware publisher (e.g. the main proxy) on different 
+	      &kamailio; instances.
+	    </para>
+	</section>
+
+	<section>
+	  <title>Dependencies</title>
+	  <section>
+		<title>&kamailio; Modules</title>
+		<para>
+		The following modules must be loaded before this module:
+			<itemizedlist>
+			<listitem>
+			<para>
+				<emphasis>presence</emphasis>.
+			</para>
+			</listitem>
+			</itemizedlist>
+		</para>
+	  </section>
+
+	  <section>
+		<title>External Libraries or Applications</title>
+		<para>
+		None.
+		</para>
+	  </section>
+	</section>
+
+	<section>
+		<title>Exported Functions</title>
+		<para>
+			None to be used in configuration file.
+		</para>
+	</section>
+
+</chapter>

+ 94 - 0
modules_k/presence_reginfo/presence_reginfo.c

@@ -0,0 +1,94 @@
+/*
+ * presence_reginfo module - Presence Handling of reg events
+ *
+ * Copyright (C) 2011 Carsten Bock, [email protected]
+ * http://www.ng-voice.com
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio 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
+ *
+ * Kamailio 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "../../sr_module.h"
+#include "../../dprint.h"
+#include "../../str.h"
+#include "../../parser/msg_parser.h"
+#include "../../mem/mem.h"
+#include "../presence/bind_presence.h"
+#include "add_events.h"
+#include "presence_reginfo.h"
+
+MODULE_VERSION
+
+/* module functions */
+static int mod_init(void);
+
+/* module variables */
+add_event_t pres_add_event;
+
+/* module exports */
+struct module_exports exports= {
+    "presence_reginfo",	/* module name */
+    DEFAULT_DLFLAGS,	/* dlopen flags */
+    0,			/* exported functions */
+    0,			/* exported parameters */
+    0,			/* exported statistics */
+    0,			/* exported MI functions */
+    0,			/* exported pseudo-variables */
+    0,			/* extra processes */
+    mod_init,		/* module initialization function */
+    0,			/* response handling function */
+    0,			/* destroy function */
+    0			/* per-child init function */
+};
+	
+/*
+ * init module function
+ */
+static int mod_init(void)
+{
+	presence_api_t pres;
+	bind_presence_t bind_presence;
+
+	bind_presence= (bind_presence_t)find_export("bind_presence", 1,0);
+	if (!bind_presence) {
+		LM_ERR("can't bind presence\n");
+		return -1;
+	}
+	if (bind_presence(&pres) < 0) {
+		LM_ERR("can't bind presence\n");
+		return -1;
+	}
+
+	pres_add_event = pres.add_event;
+	if (add_event == NULL) {
+		LM_ERR("could not import add_event\n");
+		return -1;
+	}
+	if(reginfo_add_events() < 0) {
+		LM_ERR("failed to add reginfo-info events\n");
+		return -1;		
+	}	
+    
+	return 0;
+}

+ 29 - 0
modules_k/presence_reginfo/presence_reginfo.h

@@ -0,0 +1,29 @@
+/*
+ * presence_reginfo module - Presence Handling of reg events
+ *
+ * Copyright (C) 2011 Carsten Bock, [email protected]
+ * http://www.ng-voice.com
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio 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
+ *
+ * Kamailio 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef _PRES_REGINFO_H_
+#define _PRES_REGINFO_H_
+
+extern add_event_t pres_add_event;
+
+#endif

+ 72 - 0
modules_k/pua/add_events.c

@@ -85,6 +85,19 @@ int pua_add_events(void)
 		return -1;
 	}
 	
+	/* add application/reginfo+xml */
+	if (dlginfo_increase_version) {
+		if(add_pua_event(REGINFO_EVENT, "reg", "application/reginfo+xml", reginfo_process_body)< 0) {
+			LM_ERR("while adding event application/reginfo+xml with version increase\n");
+			return -1;
+		}
+	} else {
+		if(add_pua_event(REGINFO_EVENT, "reg", "application/reginfo+xml", dlg_process_body)< 0) {
+			LM_ERR("while adding event application/reginfo+xml\n");
+			return -1;
+		}
+	}
+	
 	return 0;
 
 }	
@@ -308,6 +321,65 @@ error:
 	return -1;
 }
 
+int reginfo_process_body(publ_info_t* publ, str** fin_body, int ver, str** tuple)
+{
+	xmlNodePtr node= NULL;
+	xmlDocPtr doc= NULL;
+	char* version;
+	str* body= NULL;
+	int len;
+	str* init_body;
+
+	init_body= publ->body;
+
+	doc= xmlParseMemory(init_body->s, init_body->len );
+	if(doc== NULL) {
+		LM_ERR("while parsing xml memory\n");
+		goto error;
+	}
+	/* change version and state*/
+	node= xmlDocGetNodeByName(doc, "reginfo", NULL);
+	if(node == NULL) {
+		LM_ERR("while extracting dialog-info node\n");
+		goto error;
+	}
+	version= int2str(ver,&len);
+	version[len]= '\0';
+
+	if( xmlSetProp(node, (const xmlChar *)"version",(const xmlChar*)version)== NULL) {
+		LM_ERR("while setting version attribute\n");
+		goto error;	
+	}
+	body= (str*)pkg_malloc(sizeof(str));
+	if(body== NULL) {
+		LM_ERR("NO more memory left\n");
+		goto error;
+	}
+	memset(body, 0, sizeof(str));
+	xmlDocDumpFormatMemory(doc, (xmlChar**)(void*)&body->s, &body->len, 1);	
+
+	xmlFreeDoc(doc);
+	doc= NULL;
+	*fin_body= body;	
+	if(*fin_body== NULL)
+		LM_DBG("NULL fin_body\n");
+
+	xmlMemoryDump();
+	xmlCleanupParser();
+	LM_DBG("successful\n");
+	return 1;
+
+error:
+	if(doc)
+		xmlFreeDoc(doc);
+	if(body)
+		pkg_free(body);
+	
+	xmlMemoryDump();
+	xmlCleanupParser();
+	return -1;
+}
+
 int mwi_process_body(publ_info_t* publ, str** fin_body, int ver, str** tuple)
 {
 	*fin_body= publ->body;

+ 1 - 0
modules_k/pua/add_events.h

@@ -39,5 +39,6 @@ int pres_process_body(struct publ_info* publ, str** fin_body, int ver, str** tup
 int bla_process_body (struct publ_info* publ, str** fin_body, int ver, str** tuple);
 int mwi_process_body (struct publ_info* publ, str** fin_body, int ver, str** tuple);
 int dlg_process_body (struct publ_info* publ, str** fin_body, int ver, str** tuple);
+int reginfo_process_body (struct publ_info* publ, str** fin_body, int ver, str** tuple);
 
 #endif

+ 26 - 0
modules_k/pua/doc/pua_admin.xml

@@ -230,6 +230,32 @@ modparam("pua", "outbound_proxy", "sip:outbound.example.com")
 ...
 modparam("pua", "dlginfo_increase_version", 1)
 ...
+</programlisting>
+		</example>
+	</section>
+	<section>
+		<title><varname>reginfo_increase_version</varname> (int)</title>
+		<para>
+		When sending PUBLISHs for Event: reg, the body contains an
+		XML document according to RFC 4235(?). This XML document contains a 
+		version attribute to easily detect changes in the registration state.
+		By setting this parameter, the pua module parses the XML document and
+		sets the version attribute to the proper value. If the receiver of
+		the PUBLISH does not care about the version parameter (e.g. like
+		&kamailio; presence_reginfo module) it makes no sense to waste 
+		CPU resources for parsing the XML body and the parameter should be 
+		set to 0.
+		</para>
+		<para>
+		<emphasis>Default value is <quote>0</quote>.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>reginfo_increase_version</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("pua", "reginfo_increase_version", 1)
+...
 </programlisting>
 		</example>
 	</section>

+ 7 - 0
modules_k/pua/hash.h

@@ -40,6 +40,7 @@
 #define MSGSUM_EVENT        1<<3
 #define CONFERENCE_EVENT    1<<4
 #define DIALOG_EVENT        1<<5
+#define REGINFO_EVENT       1<<6
 
 #define UL_PUBLISH          1<<0
 #define BLA_PUBLISH         1<<1
@@ -53,6 +54,8 @@
 #define RLS_SUBSCRIBE       1<<9
 #define DIALOG_PUBLISH      1<<10
 #define PURPLE_PUBLISH      1<<11
+#define REGINFO_PUBLISH     1<<12
+#define REGINFO_SUBSCRIBE   1<<13
 
 #define NO_UPDATEDB_FLAG    1<<0
 #define UPDATEDB_FLAG       1<<1
@@ -135,6 +138,10 @@ static inline int get_event_flag(str* event)
 {
     switch (event->len) 
     {
+        case 3:
+            if (strncmp(event->s, "reg", 3) == 0)
+                return REGINFO_EVENT;
+            break;
         case 6:
             if (strncmp(event->s, "dialog", 6) == 0)
                 return DIALOG_EVENT;

+ 2 - 0
modules_k/pua/pua.c

@@ -67,6 +67,7 @@ int update_period= 100;
 str outbound_proxy = {0, 0};
 int startup_time = 0;
 int dlginfo_increase_version = 0;
+int reginfo_increase_version = 0;
 pua_event_t* pua_evlist= NULL;
 
 /* database connection */
@@ -123,6 +124,7 @@ static param_export_t params[]={
 	{"update_period",	 INT_PARAM, &update_period       },
 	{"outbound_proxy",	 STR_PARAM, &outbound_proxy.s    },
 	{"dlginfo_increase_version",	 INT_PARAM, &dlginfo_increase_version},
+	{"reginfo_increase_version",	 INT_PARAM, &reginfo_increase_version},
 	{0,							 0,			0            }
 };
 

+ 2 - 0
modules_k/pua/pua.h

@@ -43,4 +43,6 @@ extern int pua_ul_publish;
 extern int default_expires;
 extern str outbound_proxy;
 
+int reginfo_increase_version;
+
 #endif

+ 19 - 0
modules_k/pua_reginfo/Makefile

@@ -0,0 +1,19 @@
+# $Id: Makefile 1856 2007-03-15 21:06:00Z jblache $
+#
+# PUBLISH
+#
+# 
+# WARNING: do not run this directly, it should be run by the master Makefile
+
+include ../../Makefile.defs
+auto_gen=
+NAME=pua_reginfo.so
+LIBS=
+
+DEFS+=-I/usr/include/libxml2 -I$(LOCALBASE)/include/libxml2 \
+      -I$(LOCALBASE)/include
+LIBS+=-L$(LOCALBASE)/lib -lxml2
+
+DEFS+=-DOPENSER_MOD_INTERFACE
+
+include ../../Makefile.modules

+ 4 - 0
modules_k/pua_reginfo/doc/Makefile

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

+ 39 - 0
modules_k/pua_reginfo/doc/pua_reginfo.xml

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

+ 253 - 0
modules_k/pua_reginfo/doc/pua_reginfo_admin.xml

@@ -0,0 +1,253 @@
+<?xml version="1.0" encoding='ISO-8859-1'?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
+"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
+
+<!-- Include general documentation entities -->
+<!ENTITY % docentities SYSTEM "../../../docbook/entities.xml">
+%docentities;
+
+]>
+<!-- Module User's Guide -->
+
+<chapter>
+	
+
+	<title>&adminguide;</title>
+	
+	<section>
+	  <title>Overview</title>
+	    <para> 
+	      This modules publishes information about "reg"-events according to
+              to RFC 3680. This can be used distribute the registration-info
+              status to the subscribed watchers.
+	    </para>
+	    <para>
+ 	      This module "PUBLISH"es information when a new user registers
+              at this server (e.g. when "save()" is called) to users, which have
+              subscribed for the reg-info for this user.
+	    </para>
+	    <para>
+ 	      This module can "SUBSCRIBE" for information at another server, so it
+              will receive "NOTIFY"-requests, when the information about a user
+              changes.
+	    </para>
+	    <para>
+ 	      And finally, it can process received "NOTIFY" requests and it will 
+              update the local registry accordingly.
+	    </para>
+	    <para>
+ 	      Use cases for this might be:
+		<itemizedlist>
+		<listitem>Keeping different Servers in Sync regarding
+		the location database
+		</listitem>
+		<listitem>Get notified, when a user registers: A presence-server,
+		which handles offline message storage for an account, would get
+		notified, when the user comes online.
+		</listitem>
+		<listitem>A client could subscribe to it's own registration-status,
+		so he would get notified as soon as his account gets administratively
+		unregistered.
+		</listitem>
+		<listitem>...</listitem>
+		</itemizedlist>
+	    </para>
+
+	</section>
+
+	<section>
+	  <title>Dependencies</title>
+	  <section>
+		<title>&kamailio; Modules</title>
+		<para>
+		The following modules must be loaded before this module:
+			<itemizedlist>
+			<listitem>
+			<para>
+				<emphasis>pua</emphasis>.
+			</para>
+			</listitem>
+			<listitem>
+			<para>
+				<emphasis>usrloc</emphasis>.
+			</para>
+			</listitem>
+			</itemizedlist>
+		</para>
+	  </section>
+
+	  <section>
+		<title>External Libraries or Applications</title>
+		<para>
+		None.
+		</para>
+	  </section>
+	</section>
+	<section>
+	<title>Exported Parameters</title>
+	<section>
+		<title><varname>default_domain</varname>(str)</title>
+		<para>
+		The default domain for the registered users to be used when
+		constructing the uri for the registrar callback. 
+		</para>
+		<para>
+		<emphasis>	Default value is <quote>NULL</quote>.	
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>default_domain</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("pua_bla", "default_domain", "kamailio.org")
+...
+</programlisting>
+		</example>
+	</section>
+	<section>
+		<title><varname>default_domain</varname>(str)</title>
+		<para>
+		The domain to be used to publish information about a user.
+		</para>
+		<example>
+		<title>Set <varname>default_domain</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("pua_reginfo", "default_domain", "kamailio.org")
+...
+</programlisting>
+		</example>
+	</section>
+	<section>
+		<title><varname>publish_reginfo</varname>(int)</title>
+		<para>
+		Whether or not to generate PUBLISH requests.
+		</para>
+		<para>
+		<emphasis>	Default value is <quote>1</quote> (enabled).	
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>publish_reginfo</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("pua_reginfo", "publish_reginfo", 0)
+...
+</programlisting>
+		</example>
+	</section>
+	<section>
+		<title><varname>outbound_proxy</varname>(str)</title>
+		<para>
+		The outbound_proxy uri to be used when sending Subscribe requests.
+		</para>
+		<para>
+		<emphasis>	Default value is <quote>NULL</quote>.	
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>outbound_proxy</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("pua_reginfo", "outbound_proxy", "sip:[email protected]")
+...
+</programlisting>
+		</example>
+	</section>
+	<section>
+		<title><varname>server_address</varname>(str)</title>
+		<para>
+		The IP address of the server.
+		</para>
+		<example>
+		<title>Set <varname>server_address</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("pua_reginfo", "server_address", "sip:[email protected]")
+...
+</programlisting>
+		</example>
+	</section>
+	</section>	
+	<section>
+		<title>Exported Functions</title>
+		<section>
+			<title>
+				<function moreinfo="none">reginfo_handle_notify</function>
+			</title>
+			<para>
+				This function processes received "NOTIFY"-requests and updates
+				the local registry accordingly.
+			</para>
+			<para>
+				This method does not create any SIP-Reponse, this has to be done
+				the script-writer.
+			</para>
+			<para>Return codes:</para>
+			<itemizedlist>
+			<listitem>
+				<para>
+				<emphasis>2</emphasis> - contacts successfully updated,
+				but no more contacts online now.
+				</para>
+				<para>
+				<emphasis>1</emphasis> - contacts successfully updated and at
+				at least one contact still registered.
+				</para>
+				<para>
+				<emphasis>-1</emphasis> - Invalid NOTIFY or other error (see log-file)
+				</para>
+			</listitem>
+			</itemizedlist>
+
+			<example>
+				<title><function>reginfo_handle_notify</function> usage</title>
+				<programlisting format="linespecific">
+...
+if(is_method("NOTIFY")) 
+	if (reginfo_handle_notify())
+		send_reply("202", "Accepted");
+...
+				</programlisting>
+			</example>
+		</section>
+		<section>
+			<title>
+				<function moreinfo="none">reginfo_subscribe(uri[, expires])</function>
+			</title>
+			<para>
+				This function will subscribe for reginfo-information at the given
+				server URI.
+			</para>
+			<para>Meaning of the parameters is as follows:</para>
+			<itemizedlist>
+			<listitem>
+				<para>
+				<emphasis>uri</emphasis> - SIP-URI of the server, where to subscribe,
+				may contain pseudo-variables.
+				</para>
+				<para>
+				<emphasis>expires</emphasis> - Expiration date for this subscription, in seconds (default 3600)
+				</para>
+			</listitem>
+			</itemizedlist>
+			<example>
+				<title><function>reginfo_subscribe</function> usage</title>
+				<programlisting format="linespecific">
+...
+route {
+	t_on_reply("1");
+	t_relay();
+}
+
+reply_route[1] {
+	if (t_check_status("200")) 
+		reginfo_subscribe("$ru");		
+}
+...
+				</programlisting>
+			</example>
+		</section>
+	</section>
+
+</chapter>

+ 390 - 0
modules_k/pua_reginfo/notify.c

@@ -0,0 +1,390 @@
+/*
+ * pua_reginfo module - Presence-User-Agent Handling of reg events
+ *
+ * Copyright (C) 2011 Carsten Bock, [email protected]
+ * http://www.ng-voice.com
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio 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
+ *
+ * Kamailio 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "notify.h"
+#include "../../parser/parse_from.h"
+#include "../../parser/parse_content.h"
+#include "../../parser/parse_uri.h"
+#include "../../modules_k/usrloc/usrloc.h"
+#include <libxml/parser.h>
+#include "pua_reginfo.h"
+
+/*<?xml version="1.0"?>
+<reginfo xmlns="urn:ietf:params:xml:ns:reginfo" version="0" state="full">
+.<registration aor="sip:[email protected]" id="0xb33fa860" state="active">
+..<contact id="0xb33fa994" state="active" event="registered" expires="3600">
+...<uri>sip:[email protected]:43582;transport=udp</uri>
+...<unknown-param name="+g.3gpp.cs-voice"></unknown-param>
+...<unknown-param name="+g.3gpp.icsi-ref">urn0X0.0041FB74E7B54P-1022urn-70X0P+03gpp-application.ims.iari.gsma-vs</unknown-param>
+...<unknown-param name="audio"></unknown-param>
+...<unknown-param name="+g.oma.sip-im.large-message"></unknown-param>
+...<unknown-param name="+g.3gpp.smsip"></unknown-param>
+...<unknown-param name="language">en,fr</unknown-param>
+...<unknown-param name="+g.oma.sip-im"></unknown-param>
+...<unknown-param name="expires">600000</unknown-param>
+..</contact>
+.</registration>
+</reginfo> */
+
+#define STATE_ACTIVE 1
+#define STATE_TERMINATED 0
+#define STATE_UNKNOWN -1
+
+#define EVENT_UNKNOWN -1
+#define EVENT_REGISTERED 0
+#define EVENT_UNREGISTERED 1
+#define EVENT_TERMINATED 2
+#define EVENT_CREATED 3
+#define EVENT_REFRESHED 4
+#define EVENT_EXPIRED 5
+
+#define RESULT_ERROR -1
+#define RESULT_CONTACTS_FOUND 1
+#define RESULT_NO_CONTACTS 2
+
+int process_contact(udomain_t * domain, urecord_t ** ul_record, str aor, str callid, int cseq, int expires, int event, str contact_uri) {
+	str no_str = {0, 0};
+	static str no_ua = str_init("n/a");
+	static ucontact_info_t ci;
+	ucontact_t * ul_contact;
+
+	if (*ul_record == NULL) {
+		switch(event) {
+			case EVENT_REGISTERED:
+			case EVENT_CREATED:
+			case EVENT_REFRESHED:
+				/* In case, no record exists and new one should be created,
+				   create a new entry for this user in the usrloc-DB */
+				if (ul.insert_urecord(domain, &aor, ul_record) < 0) {
+					LM_ERR("failed to insert new user-record\n");
+					return RESULT_ERROR;
+				}
+				break;
+			default:
+				/* No entry in usrloc and the contact is expired, deleted, unregistered, whatever:
+                                   We do not need to do anything. */
+				return RESULT_NO_CONTACTS;
+				break;
+		}
+	}
+	
+	/* Make sure, no crap is in the structure: */
+	memset( &ci, 0, sizeof(ucontact_info_t));	
+	/* Get callid of the message */
+	ci.callid = &callid;
+	/* Get CSeq number of the message */
+	ci.cseq = cseq;
+	ci.sock = NULL;
+	/* additional info from message */
+	ci.user_agent = &no_ua;
+	ci.last_modified = time(0);
+
+	/* set expire time */
+	ci.expires = time(0) + expires;
+
+	/* Now we start looking for the contact: */
+	if (((*ul_record)->contacts == 0)
+		|| (ul.get_ucontact(*ul_record, &aor, &callid, &no_str, cseq+1, &ul_contact) != 0)) {
+		if (ul.insert_ucontact(*ul_record, &aor, &ci, &ul_contact) < 0) {
+			LM_ERR("failed to insert new contact\n");
+			return RESULT_ERROR;
+		}
+	} else {
+		if (ul.update_ucontact(*ul_record, ul_contact, &ci) < 0) {
+			LM_ERR("failed to update contact\n");
+			return RESULT_ERROR;
+		}
+	}
+	ul_contact = (*ul_record)->contacts;
+	while (ul_contact) {
+		if (VALID_CONTACT(ul_contact, time(0))) return RESULT_CONTACTS_FOUND;
+		ul_contact = ul_contact->next;
+	}
+
+	return RESULT_NO_CONTACTS;
+}
+
+xmlNodePtr xmlGetNodeByName(xmlNodePtr parent, const char *name) {
+	xmlNodePtr cur = parent;
+	xmlNodePtr match = NULL;
+	while (cur) {
+		if (xmlStrcasecmp(cur->name, (unsigned char*)name) == 0)
+			return cur;
+		match = xmlGetNodeByName(cur->children, name);
+		if (match)
+			return match;
+		cur = cur->next;
+	}
+	return NULL;
+}
+
+char * xmlGetAttrContentByName(xmlNodePtr node, const char *name) {
+	xmlAttrPtr attr = node->properties;
+	while (attr) {
+		if (xmlStrcasecmp(attr->name, (unsigned char*)name) == 0)
+			return (char*)xmlNodeGetContent(attr->children);
+		attr = attr->next;
+	}
+	return NULL;
+}
+
+int reginfo_parse_state(char * s) {
+	if (s == NULL) {
+		return STATE_UNKNOWN;
+	}
+	switch (strlen(s)) {
+		case 6:
+			if (strncmp(s, "active", 6) ==  0) return STATE_ACTIVE;
+			break;
+		case 10:
+			if (strncmp(s, "terminated", 10) ==  0) return STATE_TERMINATED;
+			break;
+		default:
+			LM_ERR("Unknown State %s\n", s);
+			return STATE_UNKNOWN;
+	}
+	LM_ERR("Unknown State %s\n", s);
+	return STATE_UNKNOWN;
+}
+
+int reginfo_parse_event(char * s) {
+	if (s == NULL) {
+		return EVENT_UNKNOWN;
+	}
+	switch (strlen(s)) {
+		case 7:
+			if (strncmp(s, "created", 7) ==  0) return EVENT_CREATED;
+			if (strncmp(s, "expired", 7) ==  0) return EVENT_EXPIRED;
+			break;
+		case 9:
+			if (strncmp(s, "refreshed", 9) ==  0) return EVENT_CREATED;
+			break;
+		case 10:
+			if (strncmp(s, "registered", 10) ==  0) return EVENT_REGISTERED;
+			if (strncmp(s, "terminated", 10) ==  0) return EVENT_TERMINATED;
+			break;
+		case 12:
+			if (strncmp(s, "unregistered", 12) ==  0) return EVENT_UNREGISTERED;
+			break;
+		default:
+			LM_ERR("Unknown Event %s\n", s);
+			return EVENT_UNKNOWN;
+	}
+	LM_ERR("Unknown Event %s\n", s);
+	return EVENT_UNKNOWN;
+}
+
+int process_body(str notify_body, udomain_t * domain) {
+	xmlDocPtr doc= NULL;
+	xmlNodePtr doc_root = NULL, registrations = NULL, contacts = NULL, uris = NULL;
+	str aor = {0, 0};
+	str callid = {0, 0};
+	str contact_uri = {0, 0};
+	int state, event, expires, result, final_result = RESULT_ERROR;
+	char * expires_char,  * cseq_char;
+	int cseq = 0;
+	urecord_t * ul_record;
+	ucontact_t * ul_contact;
+	struct sip_uri parsed_aor;
+
+	/* Temporary */
+	int mem_only = 1;
+
+	doc = xmlParseMemory(notify_body.s, notify_body.len);
+	if(doc== NULL)  {
+		LM_ERR("Error while parsing the xml body message, Body is:\n%.*s\n",
+			notify_body.len, notify_body.s);
+		return -1;
+	}
+	doc_root = xmlGetNodeByName(doc->children, "reginfo");
+	if(doc_root == NULL) {
+		LM_ERR("while extracting the reginfo node\n");
+		goto error;
+	}
+	registrations = doc_root->children;
+	while (registrations) {
+		/* Only process registration sub-items */
+		if (xmlStrcasecmp(registrations->name, BAD_CAST "registration") != 0)
+			goto next_registration;
+		state = reginfo_parse_state(xmlGetAttrContentByName(registrations, "state"));
+		if (state == STATE_UNKNOWN) {
+			LM_ERR("No state for this contact!\n");		
+			goto next_registration;
+		}
+		aor.s = xmlGetAttrContentByName(registrations, "aor");
+		if (aor.s == NULL) {
+			LM_ERR("No AOR for this contact!\n");		
+			goto next_registration;
+		}
+		aor.len = strlen(aor.s);
+		LM_DBG("AOR %.*s has state \"%d\"\n", aor.len, aor.s, state);
+
+		/* Get username part of the AOR, search for @ in the AOR. */
+		if (parse_uri(aor.s, aor.len, &parsed_aor) < 0) {
+			LM_ERR("failed to parse Address of Record (%.*s)\n",
+				aor.len, aor.s);
+			goto next_registration;
+		}
+
+		/* Now let's lock that domain for this AOR: */		
+		ul.lock_udomain(domain, &parsed_aor.user);
+		/* and retrieve the user-record for this user: */
+		result = ul.get_urecord(domain, &parsed_aor.user, &ul_record);
+		if (result < 0) {
+			ul.unlock_udomain(domain, &parsed_aor.user);
+			LM_ERR("failed to query usrloc (User %.*s)\n",
+				parsed_aor.user.len, parsed_aor.user.s);
+			goto next_registration;
+		}
+		/* If no contacts found, then set the ul_record to NULL */
+		if (result != 0) ul_record = NULL;
+
+		/* If the state is terminated, we just can delete all bindings */
+		if (state == STATE_TERMINATED) {
+			if (ul_record) {
+				ul_contact = ul_record->contacts;
+				while(ul_contact) {
+					if (mem_only) {
+						ul_contact->flags |= FL_MEM;
+					} else {
+						ul_contact->flags &= ~FL_MEM;
+					}
+					ul_contact = ul_contact->next;
+				}
+				if (ul.delete_urecord(domain, &parsed_aor.user, ul_record) < 0) {
+					LM_ERR("failed to remove record from usrloc\n");
+				}
+				/* If already a registration with contacts was found, then keep that result.
+				   otherwise the result is now "No contacts found" */
+				if (final_result != RESULT_CONTACTS_FOUND) final_result = RESULT_NO_CONTACTS;
+			}
+		/* Otherwise, process the content */
+		} else {
+			/* Now lets process the Contact's from this Registration: */
+			contacts = registrations->children;
+			while (contacts) {
+				if (xmlStrcasecmp(contacts->name, BAD_CAST "contact") != 0)
+					goto next_contact;
+				callid.s = xmlGetAttrContentByName(contacts, "callid");
+				if (callid.s == NULL) {
+					LM_ERR("No Call-ID for this contact!\n");		
+					goto next_contact;
+				}
+				callid.len = strlen(callid.s);
+
+				event = reginfo_parse_event(xmlGetAttrContentByName(contacts, "event"));
+				if (event == EVENT_UNKNOWN) {
+					LM_ERR("No event for this contact!\n");		
+					goto next_contact;
+				}
+				expires_char = xmlGetAttrContentByName(contacts, "expires");
+				if (expires_char == NULL) {
+					LM_ERR("No expires for this contact!\n");		
+					goto next_contact;
+				}
+				expires = atoi(expires_char);
+				if (expires < 0) {
+					LM_ERR("No valid expires for this contact!\n");		
+					goto next_contact;
+				}
+				LM_DBG("%.*s: Event \"%d\", expires %d\n",
+					callid.len, callid.s, event, expires);
+
+				cseq_char = xmlGetAttrContentByName(contacts, "cseq");
+				if (cseq_char == NULL) {
+					LM_WARN("No cseq for this contact!\n");		
+				} else {
+					cseq = atoi(cseq_char);
+					if (cseq < 0) {
+						LM_WARN("No valid cseq for this contact!\n");		
+					}
+				}
+
+				/* Now lets process the URI's from this Contact: */
+				uris = contacts->children;
+				while (uris) {
+					if (xmlStrcasecmp(uris->name, BAD_CAST "uri") != 0)
+						goto next_uri;
+					contact_uri.s = (char*)xmlNodeGetContent(uris);	
+					if (contact_uri.s == NULL) {
+						LM_ERR("No URI for this contact!\n");		
+						goto next_registration;
+					}
+					contact_uri.len = strlen(contact_uri.s);
+					LM_DBG("Contact: %.*s\n",
+						contact_uri.len, contact_uri.s);
+
+					/* Add to Usrloc: */
+					result = process_contact(domain, &ul_record, parsed_aor.user, callid, cseq, expires, event, contact_uri);
+				
+					/* Process the result */
+					if (final_result != RESULT_CONTACTS_FOUND) final_result = result;
+next_uri:
+					uris = uris->next;
+				}
+next_contact:
+				contacts = contacts->next;
+			}
+		}
+next_registration:
+		// if (ul_record) ul.release_urecord(ul_record);		
+		/* Unlock the domain for this AOR: */
+		ul.unlock_udomain(domain, &parsed_aor.user);
+
+		registrations = registrations->next;
+	}
+error:
+	/* Free the XML-Document */
+    	if(doc) xmlFreeDoc(doc);
+	return final_result;
+}
+
+int reginfo_handle_notify(struct sip_msg* msg, char* domain, char* s2) {
+ 	str body;
+	int result = 1;
+
+  	/* If not done yet, parse the whole message now: */
+  	if (parse_headers(msg, HDR_EOH_F, 0) == -1) {
+  		LM_ERR("Error parsing headers\n");
+  		return -1;
+  	}
+	if (get_content_length(msg) == 0) {
+   		LM_DBG("Content length = 0\n");
+		/* No Body? Then there is no published information available, which is ok. */
+   		return 1;
+   	} else {
+   		body.s=get_body(msg);
+   		if (body.s== NULL) {
+   			LM_ERR("cannot extract body from msg\n");
+   			return -1;
+   		}
+   		body.len = get_content_length(msg);
+   	}
+
+	LM_DBG("Body is %.*s\n", body.len, body.s);
+	
+	result = process_body(body, (udomain_t*)domain);
+
+	return result;
+}
+

+ 31 - 0
modules_k/pua_reginfo/notify.h

@@ -0,0 +1,31 @@
+/*
+ * pua_reginfo module - Presence-User-Agent Handling of reg events
+ *
+ * Copyright (C) 2011 Carsten Bock, [email protected]
+ * http://www.ng-voice.com
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio 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
+ *
+ * Kamailio 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef NOTIFY_H
+#define NOTIFY_H
+
+#include "../../parser/msg_parser.h"
+
+int reginfo_handle_notify(struct sip_msg*, char*, char*);
+
+#endif

+ 181 - 0
modules_k/pua_reginfo/pua_reginfo.c

@@ -0,0 +1,181 @@
+/*
+ * pua_reginfo module - Presence-User-Agent Handling of reg events
+ *
+ * Copyright (C) 2011 Carsten Bock, [email protected]
+ * http://www.ng-voice.com
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio 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
+ *
+ * Kamailio 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* Bindings to PUA */
+#include "../pua/pua_bind.h"
+/* Bindings to usrloc */
+#include "../usrloc/usrloc.h"
+
+#include "pua_reginfo.h"
+#include "subscribe.h"
+#include "notify.h"
+#include "usrloc_cb.h"
+
+MODULE_VERSION
+
+/* Default domain to be added, if none provided. */
+str default_domain = {NULL, 0};
+str outbound_proxy = {NULL, 0};
+str server_address = {NULL, 0};
+
+int publish_reginfo = 1;
+
+
+/** Fixup functions */
+static int domain_fixup(void** param, int param_no);
+
+/** module functions */
+static int mod_init(void);
+
+/* Commands */
+static cmd_export_t cmds[] = {
+	{"reginfo_subscribe", (cmd_function)reginfo_subscribe, 1, fixup_subscribe, 0, REQUEST_ROUTE|ONREPLY_ROUTE}, 	
+	{"reginfo_subscribe", (cmd_function)reginfo_subscribe2, 2, fixup_subscribe, 0, REQUEST_ROUTE|ONREPLY_ROUTE}, 	
+	{"reginfo_handle_notify", (cmd_function)reginfo_handle_notify, 1, domain_fixup, 0, REQUEST_ROUTE}, 	
+	{0, 0, 0, 0, 0, 0} 
+};
+
+static param_export_t params[]={
+ 	{"default_domain", STR_PARAM, &default_domain.s},
+	{"outbound_proxy", STR_PARAM, &outbound_proxy.s},
+	{"server_address", STR_PARAM, &server_address.s},
+	{"publish_reginfo", INT_PARAM, &publish_reginfo},
+	{0, 0, 0}
+};
+
+struct module_exports exports= {
+	"pua_reginfo",		/* module name */
+	DEFAULT_DLFLAGS,	/* dlopen flags */
+	cmds,			/* exported functions */
+	params,			/* exported parameters */
+	0,			/* exported statistics */
+	0,			/* exported MI functions */
+	0,			/* exported pseudo-variables */
+	0,			/* extra processes */
+	mod_init,		/* module initialization function */
+	0,			/* response handling function */
+	0,			/* destroy function */
+	NULL			/* per-child init function */
+};
+
+/**
+ * init module function
+ */
+static int mod_init(void)
+{
+	bind_pua_t bind_pua;
+	bind_usrloc_t bind_usrloc;
+
+	if (publish_reginfo == 1) {
+		/* Verify the default domain: */
+		if(default_domain.s == NULL ) {       
+		        LM_ERR("default domain parameter not set\n");
+		        return -1;
+		}
+		default_domain.len= strlen(default_domain.s);
+	}
+
+	if(server_address.s== NULL) {
+		LM_ERR("server_address parameter not set\n");
+		return -1;
+	}
+	server_address.len= strlen(server_address.s);
+
+	if(outbound_proxy.s == NULL)
+		LM_DBG("No outbound proxy set\n");
+	else
+		outbound_proxy.len= strlen(outbound_proxy.s);
+        
+	/* Bind to PUA: */
+	bind_pua= (bind_pua_t)find_export("bind_pua", 1,0);
+	if (!bind_pua) {
+		LM_ERR("Can't bind pua\n");
+		return -1;
+	}	
+	if (bind_pua(&pua) < 0) {
+		LM_ERR("Can't bind pua\n");
+		return -1;
+	}
+	/* Check for Publish/Subscribe methods */
+	if(pua.send_publish == NULL) {
+		LM_ERR("Could not import send_publish\n");
+		return -1;
+	}
+	if(pua.send_subscribe == NULL) {
+		LM_ERR("Could not import send_subscribe\n");
+		return -1;
+	}
+
+	/* Bind to URSLOC: */
+	bind_usrloc = (bind_usrloc_t)find_export("ul_bind_usrloc", 1, 0);
+	if (!bind_usrloc) {
+		LM_ERR("Can't bind usrloc\n");
+		return -1;
+	}
+	if (bind_usrloc(&ul) < 0) {
+		LM_ERR("Can't bind usrloc\n");
+		return -1;
+	}
+	if (publish_reginfo == 1) {
+		if(ul.register_ulcb == NULL) {
+			LM_ERR("Could not import ul_register_ulcb\n");
+			return -1;
+		}
+		if(ul.register_ulcb(UL_CONTACT_INSERT, reginfo_usrloc_cb , 0)< 0) {
+			LM_ERR("can not register callback for insert\n");
+			return -1;
+		}
+		if(ul.register_ulcb(UL_CONTACT_EXPIRE, reginfo_usrloc_cb, 0)< 0) {	
+			LM_ERR("can not register callback for expire\n");
+			return -1;
+		}
+		if(ul.register_ulcb(UL_CONTACT_UPDATE, reginfo_usrloc_cb, 0)< 0) {	
+			LM_ERR("can not register callback for update\n");
+			return -1;
+		}
+		if(ul.register_ulcb(UL_CONTACT_DELETE, reginfo_usrloc_cb, 0)< 0) {	
+			LM_ERR("can not register callback for delete\n");
+			return -1;
+		}
+	}
+	return 0;
+}
+
+/*! \brief
+ * Convert char* parameter to udomain_t* pointer
+ */
+static int domain_fixup(void** param, int param_no)
+{
+	udomain_t* d;
+
+	if (param_no == 1) {
+		if (ul.register_udomain((char*)*param, &d) < 0) {
+			LM_ERR("failed to register domain\n");
+			return E_UNSPEC;
+		}
+
+		*param = (void*)d;
+	}
+	return 0;
+}
+

+ 37 - 0
modules_k/pua_reginfo/pua_reginfo.h

@@ -0,0 +1,37 @@
+/*
+ * pua_reginfo module - Presence-User-Agent Handling of reg events
+ *
+ * Copyright (C) 2011 Carsten Bock, [email protected]
+ * http://www.ng-voice.com
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio 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
+ *
+ * Kamailio 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef _PUA_REGINFO_H
+#define _PUA_REGINFO_H
+
+#include "../pua/pua_bind.h"
+#include "../usrloc/usrloc.h"
+
+str default_domain;
+str outbound_proxy;
+str server_address;
+
+usrloc_api_t ul; /*!< Structure containing pointers to usrloc functions*/
+pua_api_t pua; /*!< Structure containing pointers to PUA functions*/
+
+#endif

+ 102 - 0
modules_k/pua_reginfo/subscribe.c

@@ -0,0 +1,102 @@
+/*
+ * pua_reginfo module - Presence-User-Agent Handling of reg events
+ *
+ * Copyright (C) 2011 Carsten Bock, [email protected]
+ * http://www.ng-voice.com
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio 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
+ *
+ * Kamailio 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "subscribe.h"
+#include "../../pvar.h"
+#include "../../mod_fix.h"
+#include "../pua/send_subscribe.h"
+#include "../pua/pua.h"
+#include "pua_reginfo.h"
+
+int reginfo_subscribe_real(struct sip_msg* msg, pv_elem_t* uri, int expires) {
+	str uri_str = {0, 0};
+	char uri_buf[512];
+	int uri_buf_len = 512;
+	subs_info_t subs;
+	
+	if (pv_printf(msg, uri, uri_buf, &uri_buf_len) < 0) {
+		LM_ERR("cannot print uri into the format\n");
+		return -1;
+	}
+	uri_str.s = uri_buf;
+	uri_str.len = uri_buf_len;
+
+	LM_DBG("Subscribing to %.*s\n", uri_str.len, uri_str.s);
+
+	memset(&subs, 0, sizeof(subs_info_t));
+
+	subs.remote_target = &uri_str;
+	subs.pres_uri= &uri_str;
+	subs.watcher_uri= &server_address;
+	subs.expires = expires;
+
+	subs.source_flag= REGINFO_SUBSCRIBE;
+	subs.event= REGINFO_EVENT;
+	subs.contact= &server_address;
+	
+	if(outbound_proxy.s && outbound_proxy.len)
+		subs.outbound_proxy= &outbound_proxy;
+
+	subs.flag|= UPDATE_TYPE;
+
+	if(pua.send_subscribe(&subs)< 0) {
+		LM_ERR("while sending subscribe\n");
+	}	
+
+	return 1;
+}
+
+int reginfo_subscribe(struct sip_msg* msg, char* uri, char* s2) {
+	return reginfo_subscribe_real(msg, (pv_elem_t*)uri, 3600);
+}
+
+int reginfo_subscribe2(struct sip_msg* msg, char* uri, char* param2) {
+	int expires;
+	if(fixup_get_ivalue(msg, (gparam_p)param2, &expires) != 0) {
+		LM_ERR("No expires provided!\n");
+		return -1;
+	}
+	return reginfo_subscribe_real(msg, (pv_elem_t*)uri, expires);
+}
+
+int fixup_subscribe(void** param, int param_no) {
+	pv_elem_t *model;
+	str s;
+	if (param_no == 1) {
+		if(*param) {
+			s.s = (char*)(*param);
+			s.len = strlen(s.s);
+			if(pv_parse_format(&s, &model)<0) {
+				LM_ERR("wrong format[%s]\n",(char*)(*param));
+				return E_UNSPEC;
+			}
+			*param = (void*)model;
+			return 1;
+		}
+		LM_ERR("null format\n");
+		return E_UNSPEC;
+	} else if (param_no == 2) {
+		return fixup_igp_igp(param, param_no);
+	} else return 1;
+}
+

+ 33 - 0
modules_k/pua_reginfo/subscribe.h

@@ -0,0 +1,33 @@
+/*
+ * pua_reginfo module - Presence-User-Agent Handling of reg events
+ *
+ * Copyright (C) 2011 Carsten Bock, [email protected]
+ * http://www.ng-voice.com
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio 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
+ *
+ * Kamailio 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef SUBSCRIBE_H
+#define SUBSCRIBE_H
+
+#include "../../parser/msg_parser.h"
+
+int reginfo_subscribe(struct sip_msg*, char*, char*);
+int reginfo_subscribe2(struct sip_msg*, char*, char*);
+int fixup_subscribe(void** param, int param_no);
+
+#endif

+ 288 - 0
modules_k/pua_reginfo/usrloc_cb.c

@@ -0,0 +1,288 @@
+/*
+ * pua_reginfo module - Presence-User-Agent Handling of reg events
+ *
+ * Copyright (C) 2011 Carsten Bock, [email protected]
+ * http://www.ng-voice.com
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio 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
+ *
+ * Kamailio 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "usrloc_cb.h"
+#include "pua_reginfo.h"
+#include <libxml/parser.h>
+#include "../pua/pua.h"
+#include "../pua/send_publish.h"
+
+/*
+Contact: <sip:[email protected]:44733;transport=udp>;expires=600000;+g.oma.sip-im;language="en,fr";+g.3gpp.smsip;+g.oma.sip-im.large-message;audio;+g.3gpp.icsi-ref="urn%3Aurn-7%3A3gpp-application.ims.iari.gsma-vs";+g.3gpp.cs-voice.
+Call-ID: 9ad9f89f-164d-bb86-1072-52e7e9eb5025.
+*/
+
+/*<?xml version="1.0"?>
+<reginfo xmlns="urn:ietf:params:xml:ns:reginfo" version="0" state="full">
+.<registration aor="sip:[email protected]" id="0xb33fa860" state="active">
+..<contact id="0xb33fa994" state="active" event="registered" expires="3600">
+...<uri>sip:[email protected]:43582;transport=udp</uri>
+...<unknown-param name="+g.3gpp.cs-voice"></unknown-param>
+...<unknown-param name="+g.3gpp.icsi-ref">urn0X0.0041FB74E7B54P-1022urn-70X0P+03gpp-application.ims.iari.gsma-vs</unknown-param>
+...<unknown-param name="audio"></unknown-param>
+...<unknown-param name="+g.oma.sip-im.large-message"></unknown-param>
+...<unknown-param name="+g.3gpp.smsip"></unknown-param>
+...<unknown-param name="language">en,fr</unknown-param>
+...<unknown-param name="+g.oma.sip-im"></unknown-param>
+...<unknown-param name="expires">600000</unknown-param>
+..</contact>
+.</registration>
+</reginfo> */
+
+
+str* build_reginfo_full(urecord_t * record, str uri, ucontact_t* c, int type) {
+	xmlDocPtr  doc = NULL; 
+	xmlNodePtr root_node = NULL;
+	xmlNodePtr registration_node = NULL;
+	xmlNodePtr contact_node = NULL;
+	xmlNodePtr uri_node = NULL;
+	str * body= NULL;
+	ucontact_t * ptr;
+	char buf[512];
+	int buf_len;
+
+	/* create the XML-Body */
+	doc = xmlNewDoc(BAD_CAST "1.0");
+	if(doc==0) {
+		LM_ERR("Unable to create XML-Doc\n");
+		return NULL;
+	}
+
+	root_node = xmlNewNode(NULL, BAD_CAST "reginfo");
+	if(root_node==0) {
+		LM_ERR("Unable to create reginfo-XML-Element\n");
+		return NULL;
+	}
+	/* This is our Root-Element: */
+    	xmlDocSetRootElement(doc, root_node);
+	
+	xmlNewProp(root_node, BAD_CAST "xmlns",	BAD_CAST "urn:ietf:params:xml:ns:reginfo");
+
+	/* we set the version to 0 but it should be set to the correct value in the pua module */
+	xmlNewProp(root_node, BAD_CAST "version", BAD_CAST "0");
+	xmlNewProp(root_node, BAD_CAST "state", BAD_CAST "full" );
+
+	/* Registration Node */
+	registration_node =xmlNewChild(root_node, NULL, BAD_CAST "registration", NULL) ;
+	if( registration_node ==NULL) {
+		LM_ERR("while adding child\n");
+		goto error;
+	}
+
+	/* Add the properties to this Node for AOR and ID: */
+	xmlNewProp(registration_node, BAD_CAST "aor", BAD_CAST uri.s);
+	buf_len = snprintf(buf, sizeof(buf), "%p", record);
+	xmlNewProp(registration_node, BAD_CAST "id", BAD_CAST buf);
+
+	/* look first for an un-expired and suported contact */
+	ptr = record->contacts;
+	while ((ptr) && !(VALID_CONTACT(ptr,time(0))))
+		ptr = ptr->next;
+	if (ptr==0)
+		xmlNewProp(registration_node, BAD_CAST "state", BAD_CAST "terminated");
+	else
+		xmlNewProp(registration_node, BAD_CAST "state", BAD_CAST "active");
+
+	ptr = record->contacts;
+	while (ptr) {
+		if (VALID_CONTACT(ptr, time(0))) {
+			LM_DBG("Contact %.*s, %p\n", ptr->c.len, ptr->c.s, ptr);
+			/* Contact-Node */
+			contact_node =xmlNewChild(registration_node, NULL, BAD_CAST "contact", NULL) ;
+			if( contact_node ==NULL) {
+				LM_ERR("while adding child\n");
+				goto error;
+			}
+			memset(buf, 0, sizeof(buf));
+			buf_len = snprintf(buf, sizeof(buf), "%p", ptr);
+			xmlNewProp(contact_node, BAD_CAST "id", BAD_CAST buf);
+			/* Check, if this is the modified contact: */
+			if (ptr == c) {
+				if ((type & UL_CONTACT_INSERT) || (type & UL_CONTACT_UPDATE))
+					xmlNewProp(contact_node, BAD_CAST "state", BAD_CAST "active");
+				else 
+					xmlNewProp(contact_node, BAD_CAST "state", BAD_CAST "terminated");
+				if (type & UL_CONTACT_INSERT) xmlNewProp(contact_node, BAD_CAST "event", BAD_CAST "created");
+				else if (type & UL_CONTACT_UPDATE) xmlNewProp(contact_node, BAD_CAST "event", BAD_CAST "refreshed");
+				else if (type & UL_CONTACT_EXPIRE) xmlNewProp(contact_node, BAD_CAST "event", BAD_CAST "expired");
+				else if (type & UL_CONTACT_DELETE) xmlNewProp(contact_node, BAD_CAST "event", BAD_CAST "unregistered");
+				else xmlNewProp(contact_node, BAD_CAST "event", BAD_CAST "unknown");
+				memset(buf, 0, sizeof(buf));
+				buf_len = snprintf(buf, sizeof(buf), "%i", (int)(ptr->expires-time(0)));
+				xmlNewProp(contact_node, BAD_CAST "expires", BAD_CAST buf);
+			} else {
+				xmlNewProp(contact_node, BAD_CAST "state", BAD_CAST "active");
+				xmlNewProp(contact_node, BAD_CAST "event", BAD_CAST "registered");
+				memset(buf, 0, sizeof(buf));
+				buf_len = snprintf(buf, sizeof(buf), "%i", (int)(ptr->expires-time(0)));
+				xmlNewProp(contact_node, BAD_CAST "expires", BAD_CAST buf);
+			}
+			if (ptr->q != Q_UNSPECIFIED) {
+				float q = (float)ptr->q/1000;
+				memset(buf, 0, sizeof(buf));
+				buf_len = snprintf(buf, sizeof(buf), "%.3f", q);
+				xmlNewProp(contact_node, BAD_CAST "q", BAD_CAST buf);
+			}
+			/* CallID Attribute */
+			memset(buf, 0, sizeof(buf));
+			buf_len = snprintf(buf, sizeof(buf), "%.*s", ptr->callid.len, ptr->callid.s);
+			xmlNewProp(contact_node, BAD_CAST "callid", BAD_CAST ptr->callid.s);
+			/* CSeq Attribute */
+			memset(buf, 0, sizeof(buf));
+			buf_len = snprintf(buf, sizeof(buf), "%d", ptr->cseq);
+			xmlNewProp(contact_node, BAD_CAST "cseq", BAD_CAST buf);
+			/* URI-Node */
+			memset(buf, 0, sizeof(buf));
+			buf_len = snprintf(buf, sizeof(buf), "%.*s", ptr->c.len, ptr->c.s);
+			uri_node = xmlNewChild(contact_node, NULL, BAD_CAST "uri", BAD_CAST buf) ;
+			if(uri_node == NULL) {
+				LM_ERR("while adding child\n");
+				goto error;
+			}
+		}
+		ptr = ptr->next;
+	}
+
+	/* create the body */
+	body = (str*)pkg_malloc(sizeof(str));
+	if(body == NULL) {
+		LM_ERR("while allocating memory\n");
+		return NULL;
+	}
+	memset(body, 0, sizeof(str));
+
+	/* Write the XML into the body */
+	xmlDocDumpFormatMemory(doc,(unsigned char**)(void*)&body->s,&body->len,1);
+
+	/*free the document */
+	xmlFreeDoc(doc);
+	xmlCleanupParser();
+
+	return body;
+error:
+	if(body) {
+		if(body->s) xmlFree(body->s);
+		pkg_free(body);
+	}
+	if(doc) xmlFreeDoc(doc);
+	return NULL;
+}	
+
+void reginfo_usrloc_cb(ucontact_t* c, int type, void* param) {
+	str* body= NULL;
+	publ_info_t publ;
+	str content_type;
+	udomain_t * domain;
+	urecord_t * record;
+	int res;
+	str uri = {NULL, 0};
+	char* at = NULL;
+	char id_buf[512];
+	int id_buf_len;
+
+	content_type.s = "application/reginfo+xml";
+	content_type.len = 23;
+	
+	/* Debug Output: */
+	LM_DBG("AOR: %.*s (%.*s)\n", c->aor->len, c->aor->s, c->domain->len, c->domain->s);
+	if(type & UL_CONTACT_INSERT) LM_DBG("type= UL_CONTACT_INSERT\n");
+	else if(type & UL_CONTACT_UPDATE) LM_DBG("type= UL_CONTACT_UPDATE\n");
+	else if(type & UL_CONTACT_EXPIRE) LM_DBG("type= UL_CONTACT_EXPIRE\n");
+	else if(type & UL_CONTACT_DELETE) LM_DBG("type= UL_CONTACT_DELETE\n");
+	else {
+		LM_ERR("Unknown Type %i\n", type);
+		return;
+	}
+
+	/* Get the UDomain for this account */
+	ul.get_udomain(c->domain->s, &domain);
+	/* Get the URecord for this AOR */
+	res = ul.get_urecord(domain, c->aor, &record);
+	if (res > 0) {
+		LM_ERR("' %.*s (%.*s)' Not found in usrloc\n", c->aor->len, c->aor->s, c->domain->len, c->domain->s);
+		return;
+	}
+
+	/* Create AOR to be published */
+	/* Search for @ in the AOR. In case no domain was provided, we will add the "default domain" */
+	at = memchr(record->aor.s, '@', record->aor.len);
+	if (!at) {
+		uri.len = record->aor.len + default_domain.len + 6;
+		uri.s = (char*)pkg_malloc(sizeof(char) * uri.len);
+		if(uri.s == NULL) {
+			LM_ERR("Error allocating memory for URI!\n");
+			goto error;
+		}
+		uri.len = snprintf(uri.s, uri.len, "sip:%.*s@%.*s", record->aor.len, record->aor.s, default_domain.len, default_domain.s);
+	} else {
+		uri.len = record->aor.len + 6;
+		uri.s = (char*)pkg_malloc(sizeof(char) * uri.len);
+		if(uri.s == NULL) {
+			LM_ERR("Error allocating memory for URI!\n");
+			goto error;
+		}
+		uri.len = snprintf(uri.s, uri.len, "sip:%.*s", record->aor.len, record->aor.s);
+	}
+	
+	/* Build the XML-Body: */
+	body = build_reginfo_full(record, uri, c, type);
+
+	if(body == NULL || body->s == NULL) {
+		LM_ERR("Error on creating XML-Body for publish\n");
+		goto error;
+	}
+	LM_DBG("XML-Body:\n%.*s\n", body->len, body->s);
+
+	LM_DBG("Contact %.*s, %p\n", c->c.len, c->c.s, c);
+
+	memset(&publ, 0, sizeof(publ_info_t));
+
+	publ.pres_uri = &uri;
+	publ.body = body;
+	id_buf_len = snprintf(id_buf, sizeof(id_buf), "REGINFO_PUBLISH.%.*s@%.*s",
+		c->aor->len, c->aor->s,
+		c->domain->len, c->domain->s);
+	publ.id.s = id_buf;
+	publ.id.len = id_buf_len;
+	publ.content_type = content_type;
+	publ.expires = 3600;
+	
+	/* make UPDATE_TYPE, as if this "publish dialog" is not found 
+	   by pua it will fallback to INSERT_TYPE anyway */
+	publ.flag|= UPDATE_TYPE;
+	publ.source_flag |= REGINFO_PUBLISH;
+	publ.event |= REGINFO_EVENT;
+	publ.extra_headers= NULL;
+
+	if(pua.send_publish(&publ) < 0) {
+		LM_ERR("Error while sending publish\n");
+	}	
+error:
+	if (uri.s) pkg_free(uri.s);
+	if(body) {
+		if(body->s) xmlFree(body->s);
+		pkg_free(body);
+	}
+
+	return;
+}	

+ 26 - 0
modules_k/pua_reginfo/usrloc_cb.h

@@ -0,0 +1,26 @@
+/*
+ * pua_reginfo module - Presence-User-Agent Handling of reg events
+ *
+ * Copyright (C) 2011 Carsten Bock, [email protected]
+ * http://www.ng-voice.com
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio 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
+ *
+ * Kamailio 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "../usrloc/usrloc.h"
+
+void reginfo_usrloc_cb(ucontact_t* c, int type, void* param);