2
0
Эх сурвалжийг харах

uac(k): added remote registration capability

- uac module can register contacts to a remote server
- can auth REGISTER challenges
- uses db table uacreg to load credentials at startup
- no reload for now (have to restart to get the db updates)
- rpc function uac.reg_dump to dump in-memory table
Daniel-Constantin Mierla 15 жил өмнө
parent
commit
679a7c1167

+ 1 - 0
modules_k/uac/Makefile

@@ -14,4 +14,5 @@ DEFS+=-DOPENSER_MOD_INTERFACE
 
 
 SERLIBPATH=../../lib
 SERLIBPATH=../../lib
 SER_LIBS+=$(SERLIBPATH)/kcore/kcore
 SER_LIBS+=$(SERLIBPATH)/kcore/kcore
+SER_LIBS+=$(SERLIBPATH)/srdb1/srdb1
 include ../../Makefile.modules
 include ../../Makefile.modules

+ 189 - 103
modules_k/uac/README

@@ -14,40 +14,43 @@ Ramona-Elena Modroiu
 
 
    <[email protected]>
    <[email protected]>
 
 
-   Copyright © 2005 Voice Sistem
+   Copyright © 2009-2010 asipto.com
 
 
-   Copyright © 2009 Asipto.com
-     __________________________________________________________
+   Copyright © 2005 Voice Sistem
+     __________________________________________________________________
 
 
    Table of Contents
    Table of Contents
 
 
    1. Admin Guide
    1. Admin Guide
 
 
-        1.1. Overview
-        1.2. Dependencies
+        1. Overview
+        2. Dependencies
 
 
-              1.2.1. Kamailio Modules
-              1.2.2. External Libraries or Applications
+              2.1. Kamailio Modules
+              2.2. External Libraries or Applications
 
 
-        1.3. Exported Parameters
+        3. Exported Parameters
 
 
-              1.3.1. rr_store_param (string)
-              1.3.2. from_restore_mode (string)
-              1.3.3. from_passwd (string)
-              1.3.4. credential (string)
-              1.3.5. auth_realm_avp (string)
-              1.3.6. auth_username_avp (string)
-              1.3.7. auth_password_avp (string)
+              3.1. rr_store_param (string)
+              3.2. from_restore_mode (string)
+              3.3. from_passwd (string)
+              3.4. credential (string)
+              3.5. auth_realm_avp (string)
+              3.6. auth_username_avp (string)
+              3.7. auth_password_avp (string)
+              3.8. reg_db_url (string)
+              3.9. reg_contact_addr (string)
 
 
-        1.4. Exported Functions
+        4. Exported Functions
 
 
-              1.4.1. uac_replace_from(display,uri)
-              1.4.2. uac_replace_from(uri)
-              1.4.3. uac_restore_from()
-              1.4.4. uac_auth()
-              1.4.5. uac_req_send()
+              4.1. uac_replace_from(display,uri)
+              4.2. uac_replace_from(uri)
+              4.3. uac_restore_from()
+              4.4. uac_auth()
+              4.5. uac_req_send()
+              4.6. uac_reg_lookup(uuid, dst)
 
 
-        1.5. Exported pseudo-variables
+        5. Exported pseudo-variables
 
 
    List of Examples
    List of Examples
 
 
@@ -58,48 +61,94 @@ Ramona-Elena Modroiu
    1.5. Set auth_realm_avp parameter
    1.5. Set auth_realm_avp parameter
    1.6. Set auth_username_avp parameter
    1.6. Set auth_username_avp parameter
    1.7. Set auth_password_avp parameter
    1.7. Set auth_password_avp parameter
-   1.8. uac_replace_from usage
-   1.9. uac_replace_from usage
-   1.10. uac_restore_from usage
-   1.11. uac_auth usage
-   1.12. uac_req_send usage
+   1.8. Set reg_db_url parameter
+   1.9. Set reg_contact_addr parameter
+   1.10. uac_replace_from usage
+   1.11. uac_replace_from usage
+   1.12. uac_restore_from usage
+   1.13. uac_auth usage
+   1.14. uac_req_send usage
+   1.15. uac_reg_lookup usage
 
 
 Chapter 1. Admin Guide
 Chapter 1. Admin Guide
 
 
-1.1. Overview
+   Table of Contents
+
+   1. Overview
+   2. Dependencies
+
+        2.1. Kamailio Modules
+        2.2. External Libraries or Applications
+
+   3. Exported Parameters
+
+        3.1. rr_store_param (string)
+        3.2. from_restore_mode (string)
+        3.3. from_passwd (string)
+        3.4. credential (string)
+        3.5. auth_realm_avp (string)
+        3.6. auth_username_avp (string)
+        3.7. auth_password_avp (string)
+        3.8. reg_db_url (string)
+        3.9. reg_contact_addr (string)
+
+   4. Exported Functions
 
 
-   UAC (User Agent Client) module provides some basic UAC
-   functionalities like FROM header manipulation (anonymization)
-   or client authentication. From version 1.5.0 it has function to
-   send SIP message from configuration file.
+        4.1. uac_replace_from(display,uri)
+        4.2. uac_replace_from(uri)
+        4.3. uac_restore_from()
+        4.4. uac_auth()
+        4.5. uac_req_send()
+        4.6. uac_reg_lookup(uuid, dst)
+
+   5. Exported pseudo-variables
+
+1. Overview
+
+   UAC (User Agent Client) module provides some basic UAC functionalities
+   like FROM header manipulation (anonymization) or client authentication.
+   From version 1.5.0 it has function to send SIP message from
+   configuration file. Version 3.1.0 adds user registration functionality.
 
 
    Known limitations in this version:
    Known limitations in this version:
-     * authentication does not support qop auth-int, just qop
-       auth;
-     * CSeq not increased during authentication - the response may
-       be rejected.
+     * authentication does not support qop auth-int, just qop auth;
+     * CSeq not increased during authentication - the response may be
+       rejected.
+
+2. Dependencies
 
 
-1.2. Dependencies
+   2.1. Kamailio Modules
+   2.2. External Libraries or Applications
 
 
-1.2.1. Kamailio Modules
+2.1. Kamailio Modules
 
 
    The following modules must be loaded before this module:
    The following modules must be loaded before this module:
      * TM - Transaction Module
      * TM - Transaction Module
-     * RR - Record-Route Module, but only if restore mode for FROM
-       URI is set to "auto".
+     * RR - Record-Route Module, but only if restore mode for FROM URI is
+       set to "auto".
 
 
-1.2.2. External Libraries or Applications
+2.2. External Libraries or Applications
 
 
-   The following libraries or applications must be installed
-   before running Kamailio with this module loaded:
+   The following libraries or applications must be installed before
+   running Kamailio with this module loaded:
      * None
      * None
 
 
-1.3. Exported Parameters
+3. Exported Parameters
 
 
-1.3.1. rr_store_param (string)
+   3.1. rr_store_param (string)
+   3.2. from_restore_mode (string)
+   3.3. from_passwd (string)
+   3.4. credential (string)
+   3.5. auth_realm_avp (string)
+   3.6. auth_username_avp (string)
+   3.7. auth_password_avp (string)
+   3.8. reg_db_url (string)
+   3.9. reg_contact_addr (string)
 
 
-   Name of Record-Route header parameter that will be used to
-   store (encoded) the original FROM URI.
+3.1. rr_store_param (string)
+
+   Name of Record-Route header parameter that will be used to store
+   (encoded) the original FROM URI.
 
 
    This parameter is optional, it's default value being "vsf".
    This parameter is optional, it's default value being "vsf".
 
 
@@ -108,16 +157,16 @@ Chapter 1. Admin Guide
 modparam("uac","rr_store_param","my_param")
 modparam("uac","rr_store_param","my_param")
 ...
 ...
 
 
-1.3.2. from_restore_mode (string)
+3.2. from_restore_mode (string)
 
 
    There are 3 mode of restoring the original FROM URI:
    There are 3 mode of restoring the original FROM URI:
-     * "none" - no information about original URI is stored;
-       restoration is not possible.
-     * "manual" - all following replies will be restored, but not
-       also the sequential requests - this must be manually
-       updated based on original URI.
-     * "auto" - all sequential requests and replies will be
-       automatically updated based on stored original URI.
+     * "none" - no information about original URI is stored; restoration
+       is not possible.
+     * "manual" - all following replies will be restored, but not also the
+       sequential requests - this must be manually updated based on
+       original URI.
+     * "auto" - all sequential requests and replies will be automatically
+       updated based on stored original URI.
 
 
    This parameter is optional, it's default value being "auto".
    This parameter is optional, it's default value being "auto".
 
 
@@ -126,10 +175,10 @@ modparam("uac","rr_store_param","my_param")
 modparam("uac","from_restore_mode","auto")
 modparam("uac","from_restore_mode","auto")
 ...
 ...
 
 
-1.3.3. from_passwd (string)
+3.3. from_passwd (string)
 
 
-   String password to be used to encrypt the RR storing parameter.
-   If empty, no encryption will be used.
+   String password to be used to encrypt the RR storing parameter. If
+   empty, no encryption will be used.
 
 
    Default value of this parameter is empty.
    Default value of this parameter is empty.
 
 
@@ -138,7 +187,7 @@ modparam("uac","from_restore_mode","auto")
 modparam("uac","from_passwd","my_secret_passwd")
 modparam("uac","from_passwd","my_secret_passwd")
 ...
 ...
 
 
-1.3.4. credential (string)
+3.4. credential (string)
 
 
    Contains a multiple definition of credentials used to perform
    Contains a multiple definition of credentials used to perform
    authentication.
    authentication.
@@ -150,63 +199,86 @@ modparam("uac","from_passwd","my_secret_passwd")
 modparam("uac","credential","username:domain:password")
 modparam("uac","credential","username:domain:password")
 ...
 ...
 
 
-1.3.5. auth_realm_avp (string)
+3.5. auth_realm_avp (string)
 
 
-   The definition of an AVP that might contain the realm to be
-   used to perform authentication.
+   The definition of an AVP that might contain the realm to be used to
+   perform authentication.
 
 
    If you define it, you also need to define "auth_username_avp"
    If you define it, you also need to define "auth_username_avp"
-   (Section 1.3.6, "auth_username_avp (string)") and
-   "auth_username_avp" (Section 1.3.7, "auth_password_avp
-   (string)").
+   (Section 3.6, "auth_username_avp (string)") and "auth_username_avp"
+   (Section 3.7, "auth_password_avp (string)").
 
 
    Example 1.5. Set auth_realm_avp parameter
    Example 1.5. Set auth_realm_avp parameter
 ...
 ...
 modparam("uac","auth_realm_avp","$avp(i:10)")
 modparam("uac","auth_realm_avp","$avp(i:10)")
 ...
 ...
 
 
-1.3.6. auth_username_avp (string)
+3.6. auth_username_avp (string)
 
 
-   The definition of an AVP that might contain the username to be
-   used to perform authentication.
+   The definition of an AVP that might contain the username to be used to
+   perform authentication.
 
 
    If you define it, you also need to define "auth_realm_avp"
    If you define it, you also need to define "auth_realm_avp"
-   (Section 1.3.5, "auth_realm_avp (string)") and
-   "auth_username_avp" (Section 1.3.7, "auth_password_avp
-   (string)").
+   (Section 3.5, "auth_realm_avp (string)") and "auth_username_avp"
+   (Section 3.7, "auth_password_avp (string)").
 
 
    Example 1.6. Set auth_username_avp parameter
    Example 1.6. Set auth_username_avp parameter
 ...
 ...
 modparam("uac","auth_username_avp","$avp(i:11)")
 modparam("uac","auth_username_avp","$avp(i:11)")
 ...
 ...
 
 
-1.3.7. auth_password_avp (string)
+3.7. auth_password_avp (string)
 
 
-   The definition of an AVP that might contain the password to be
-   used to perform authentication.
+   The definition of an AVP that might contain the password to be used to
+   perform authentication.
 
 
    If you define it, you also need to define "auth_password_avp"
    If you define it, you also need to define "auth_password_avp"
-   (Section 1.3.7, "auth_password_avp (string)") and
-   "auth_username_avp" (Section 1.3.7, "auth_password_avp
-   (string)").
+   (Section 3.7, "auth_password_avp (string)") and "auth_username_avp"
+   (Section 3.7, "auth_password_avp (string)").
 
 
    Example 1.7. Set auth_password_avp parameter
    Example 1.7. Set auth_password_avp parameter
 ...
 ...
 modparam("uac","auth_password_avp","$avp(i:12)")
 modparam("uac","auth_password_avp","$avp(i:12)")
 ...
 ...
 
 
-1.4. Exported Functions
+3.8. reg_db_url (string)
+
+   DB URL to fetch user profiles for registration.
 
 
-1.4.1.  uac_replace_from(display,uri)
+   Example 1.8. Set reg_db_url parameter
+...
+modparam("uac", "reg_db_url",
+    "mysql://openser:openserrw@localhost/openser")
+...
+
+3.9. reg_contact_addr (string)
+
+   Address to be used to build contact address. Must be at least host
+   part, can have port and parameters. Must not include 'sip:'.
+
+   Example 1.9. Set reg_contact_addr parameter
+...
+modparam("uac", "reg_contact_addr", "192.168.1.2:5080")
+...
+
+4. Exported Functions
+
+   4.1. uac_replace_from(display,uri)
+   4.2. uac_replace_from(uri)
+   4.3. uac_restore_from()
+   4.4. uac_auth()
+   4.5. uac_req_send()
+   4.6. uac_reg_lookup(uuid, dst)
+
+4.1.  uac_replace_from(display,uri)
 
 
    Replace in FROM header the display name and the URI part.
    Replace in FROM header the display name and the URI part.
 
 
    display and URI parameters can include pseudo-variables.
    display and URI parameters can include pseudo-variables.
 
 
-   This function can be used from REQUEST_ROUTE and from
-   BRANCH_ROUTE.
+   This function can be used from REQUEST_ROUTE and from BRANCH_ROUTE.
 
 
-   Example 1.8. uac_replace_from usage
+   Example 1.10. uac_replace_from usage
 ...
 ...
 # replace both display and uri
 # replace both display and uri
 uac_replace_from("$avp(s:display)","$avp(s:uri)");
 uac_replace_from("$avp(s:display)","$avp(s:uri)");
@@ -218,56 +290,54 @@ uac_replace_from("","sip:[email protected]");
 uac_replace_from("","");
 uac_replace_from("","");
 ...
 ...
 
 
-1.4.2.  uac_replace_from(uri)
+4.2.  uac_replace_from(uri)
 
 
-   Replace in FROM header the URI part without altering the
-   display name.
+   Replace in FROM header the URI part without altering the display name.
 
 
    URI parameter can include pseudo-variables.
    URI parameter can include pseudo-variables.
 
 
-   This function can be used from REQUEST_ROUTE and from
-   BRANCH_ROUTE.
+   This function can be used from REQUEST_ROUTE and from BRANCH_ROUTE.
 
 
-   Example 1.9. uac_replace_from usage
+   Example 1.11. uac_replace_from usage
 ...
 ...
 uac_replace_from("sip:[email protected]");
 uac_replace_from("sip:[email protected]");
 ...
 ...
 
 
-1.4.3.  uac_restore_from()
+4.3.  uac_restore_from()
 
 
-   This function will check if the FROM URI was modified and will
-   use the information stored in header parameter to restore the
-   original FROM URI value.
+   This function will check if the FROM URI was modified and will use the
+   information stored in header parameter to restore the original FROM URI
+   value.
 
 
    This function can be used from REQUEST_ROUTE.
    This function can be used from REQUEST_ROUTE.
 
 
-   Example 1.10. uac_restore_from usage
+   Example 1.12. uac_restore_from usage
 ...
 ...
 uac_restore_from();
 uac_restore_from();
 ...
 ...
 
 
-1.4.4.  uac_auth()
+4.4.  uac_auth()
 
 
-   This function can be called only from failure route and will
-   build the authentication response header and insert it into the
-   request without sending anything.
+   This function can be called only from failure route and will build the
+   authentication response header and insert it into the request without
+   sending anything.
 
 
    This function can be used from FAILURE_ROUTE.
    This function can be used from FAILURE_ROUTE.
 
 
-   Example 1.11. uac_auth usage
+   Example 1.13. uac_auth usage
 ...
 ...
 uac_auth();
 uac_auth();
 ...
 ...
 
 
-1.4.5.  uac_req_send()
+4.5.  uac_req_send()
 
 
-   This function sends a SIP message from the configuration file.
-   The message is built out of $uac_req(...) pseudo-variable.
+   This function sends a SIP message from the configuration file. The
+   message is built out of $uac_req(...) pseudo-variable.
 
 
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE,
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE,
    BRANCH_ROUTE, ONREPLY_ROUTE, LOCAL_ROUTE.
    BRANCH_ROUTE, ONREPLY_ROUTE, LOCAL_ROUTE.
 
 
-   Example 1.12. uac_req_send usage
+   Example 1.14. uac_req_send usage
 ...
 ...
 $uac_req(method)="OPTIONS";
 $uac_req(method)="OPTIONS";
 $uac_req(ruri)="sip:kamailio.org";
 $uac_req(ruri)="sip:kamailio.org";
@@ -276,7 +346,23 @@ $uac_req(turi)="sip:kamailio.org";
 uac_req_send();
 uac_req_send();
 ...
 ...
 
 
-1.5. Exported pseudo-variables
+4.6.  uac_reg_lookup(uuid, dst)
+
+   This function sets the PV dst to SIP URI that correspond to uuid in uac
+   registations table. uuid and dst must be pseudo-variables.
+
+   This function can be used from ANY_ROUTE.
+
+   Example 1.15. uac_reg_lookup usage
+...
+
+if(uac_reg_lookup("$rU", "$ru"))
+{
+    lookup("location");
+}
+...
+
+5. Exported pseudo-variables
 
 
      * $uac_req(key)
      * $uac_req(key)
 
 

+ 4 - 4
modules_k/uac/doc/uac.xml

@@ -30,12 +30,12 @@
 		</editor>
 		</editor>
 	</authorgroup>
 	</authorgroup>
 	<copyright>
 	<copyright>
-		<year>2005</year>
-		<holder>Voice Sistem</holder>
+		<year>2009-2010</year>
+		<holder>asipto.com</holder>
 	</copyright>
 	</copyright>
 	<copyright>
 	<copyright>
-		<year>2009</year>
-		<holder>Asipto.com</holder>
+		<year>2005</year>
+		<holder>Voice Sistem</holder>
 	</copyright>
 	</copyright>
 	</bookinfo>
 	</bookinfo>
 	<toc></toc>
 	<toc></toc>

+ 56 - 1
modules_k/uac/doc/uac_admin.xml

@@ -20,7 +20,8 @@
 		UAC (User Agent Client) module provides some basic UAC
 		UAC (User Agent Client) module provides some basic UAC
 		functionalities like FROM header manipulation (anonymization)
 		functionalities like FROM header manipulation (anonymization)
 		or client authentication. From version 1.5.0 it has function to
 		or client authentication. From version 1.5.0 it has function to
-		send SIP message from configuration file.
+		send SIP message from configuration file. Version 3.1.0 adds user
+		registration functionality.
 		</para>
 		</para>
 		<para>
 		<para>
 		Known limitations in this version:
 		Known limitations in this version:
@@ -244,6 +245,36 @@ modparam("uac","auth_username_avp","$avp(i:11)")
 				<programlisting format="linespecific">
 				<programlisting format="linespecific">
 ...
 ...
 modparam("uac","auth_password_avp","$avp(i:12)")
 modparam("uac","auth_password_avp","$avp(i:12)")
+...
+				</programlisting>
+			</example>
+		</section>
+		<section id="reg-db-url-id">
+			<title><varname>reg_db_url</varname> (string)</title>
+			<para>
+			DB URL to fetch user profiles for registration.
+			</para>
+			<example>
+				<title>Set <varname>reg_db_url</varname> parameter</title>
+				<programlisting format="linespecific">
+...
+modparam("uac", "reg_db_url",
+    "mysql://openser:openserrw@localhost/openser")
+...
+				</programlisting>
+			</example>
+		</section>
+		<section id="reg-contact-addr-id">
+			<title><varname>reg_contact_addr</varname> (string)</title>
+			<para>
+			Address to be used to build contact address. Must be at least
+			host part, can have port and parameters. Must not include 'sip:'.
+			</para>
+			<example>
+				<title>Set <varname>reg_contact_addr</varname> parameter</title>
+				<programlisting format="linespecific">
+...
+modparam("uac", "reg_contact_addr", "192.168.1.2:5080")
 ...
 ...
 				</programlisting>
 				</programlisting>
 			</example>
 			</example>
@@ -370,6 +401,30 @@ $uac_req(ruri)="sip:kamailio.org";
 $uac_req(furi)="sip:kamailio.org";
 $uac_req(furi)="sip:kamailio.org";
 $uac_req(turi)="sip:kamailio.org";
 $uac_req(turi)="sip:kamailio.org";
 uac_req_send();
 uac_req_send();
+...
+				</programlisting>
+			</example>
+		</section>
+		<section>
+			<title>
+				<function moreinfo="none">uac_reg_lookup(uuid, dst)</function>
+			</title>
+			<para>
+			This function sets the PV dst to SIP URI that correspond to uuid
+			in uac registations table. uuid and dst must be pseudo-variables.
+			</para>
+			<para>
+			This function can be used from ANY_ROUTE.
+			</para>
+			<example>
+				<title><function>uac_reg_lookup</function> usage</title>
+				<programlisting format="linespecific">
+...
+
+if(uac_reg_lookup("$rU", "$ru"))
+{
+    lookup("location");
+}
 ...
 ...
 				</programlisting>
 				</programlisting>
 			</example>
 			</example>

+ 90 - 1
modules_k/uac/uac.c

@@ -35,19 +35,27 @@
 #include <stdio.h>
 #include <stdio.h>
 #include <string.h>
 #include <string.h>
 #include <stdlib.h>
 #include <stdlib.h>
+#include <unistd.h>
 
 
 #include "../../sr_module.h"
 #include "../../sr_module.h"
 #include "../../dprint.h"
 #include "../../dprint.h"
 #include "../../error.h"
 #include "../../error.h"
 #include "../../pvar.h"
 #include "../../pvar.h"
+#include "../../pt.h"
+#include "../../timer.h"
 #include "../../mem/mem.h"
 #include "../../mem/mem.h"
 #include "../../modules/tm/tm_load.h"
 #include "../../modules/tm/tm_load.h"
 #include "../../modules/tm/t_hooks.h"
 #include "../../modules/tm/t_hooks.h"
+#include "../../mod_fix.h"
+#include "../../rpc.h"
+#include "../../rpc_lookup.h"
+
 #include "../rr/api.h"
 #include "../rr/api.h"
 
 
 #include "from.h"
 #include "from.h"
 #include "auth.h"
 #include "auth.h"
 #include "uac_send.h"
 #include "uac_send.h"
+#include "uac_reg.h"
 
 
 
 
 MODULE_VERSION
 MODULE_VERSION
@@ -73,10 +81,12 @@ static int w_replace_from1(struct sip_msg* msg, char* str, char* str2);
 static int w_replace_from2(struct sip_msg* msg, char* str, char* str2);
 static int w_replace_from2(struct sip_msg* msg, char* str, char* str2);
 static int w_restore_from(struct sip_msg* msg,  char* foo, char* bar);
 static int w_restore_from(struct sip_msg* msg,  char* foo, char* bar);
 static int w_uac_auth(struct sip_msg* msg, char* str, char* str2);
 static int w_uac_auth(struct sip_msg* msg, char* str, char* str2);
+static int w_uac_reg_lookup(struct sip_msg* msg,  char* src, char* dst);
 static int fixup_replace_from1(void** param, int param_no);
 static int fixup_replace_from1(void** param, int param_no);
 static int fixup_replace_from2(void** param, int param_no);
 static int fixup_replace_from2(void** param, int param_no);
 static int mod_init(void);
 static int mod_init(void);
 static void mod_destroy(void);
 static void mod_destroy(void);
+static int child_init(int rank);
 
 
 
 
 static pv_export_t mod_pvs[] = {
 static pv_export_t mod_pvs[] = {
@@ -99,6 +109,8 @@ static cmd_export_t cmds[]={
 	{"uac_req_send",  (cmd_function)uac_req_send,         0,                  0, 0, 
 	{"uac_req_send",  (cmd_function)uac_req_send,         0,                  0, 0, 
 		REQUEST_ROUTE | FAILURE_ROUTE |
 		REQUEST_ROUTE | FAILURE_ROUTE |
 		ONREPLY_ROUTE | BRANCH_ROUTE | ERROR_ROUTE | LOCAL_ROUTE},
 		ONREPLY_ROUTE | BRANCH_ROUTE | ERROR_ROUTE | LOCAL_ROUTE},
+	{"uac_reg_lookup",  (cmd_function)w_uac_reg_lookup,  2, fixup_pvar_pvar,
+		fixup_free_pvar_pvar, ANY_ROUTE },
 
 
 	{0,0,0,0,0,0}
 	{0,0,0,0,0,0}
 };
 };
@@ -114,6 +126,8 @@ static param_export_t params[] = {
 	{"auth_username_avp", STR_PARAM,                &auth_username_avp     },
 	{"auth_username_avp", STR_PARAM,                &auth_username_avp     },
 	{"auth_realm_avp",    STR_PARAM,                &auth_realm_avp        },
 	{"auth_realm_avp",    STR_PARAM,                &auth_realm_avp        },
 	{"auth_password_avp", STR_PARAM,                &auth_password_avp     },
 	{"auth_password_avp", STR_PARAM,                &auth_password_avp     },
+	{"reg_db_url",        STR_PARAM,                &reg_db_url.s          },
+	{"reg_contact_addr",  STR_PARAM,                &reg_contact_addr.s    },
 	{0, 0, 0}
 	{0, 0, 0}
 };
 };
 
 
@@ -131,7 +145,7 @@ struct module_exports exports= {
 	mod_init,   /* module initialization function */
 	mod_init,   /* module initialization function */
 	0,
 	0,
 	mod_destroy,
 	mod_destroy,
-	0  /* per-child init function */
+	child_init  /* per-child init function */
 };
 };
 
 
 
 
@@ -218,6 +232,32 @@ static int mod_init(void)
 		}
 		}
 	}
 	}
 
 
+	if(reg_db_url.s!=NULL)
+	{
+		if(reg_contact_addr.s==NULL)
+		{
+			LM_ERR("contact address parameter not set\n");
+			goto error;
+		}
+		if(reg_htable_size>14)
+			reg_htable_size = 14;
+		if(reg_htable_size<2)
+			reg_htable_size = 2;
+
+		reg_htable_size = 1<<reg_htable_size;
+		if(uac_reg_init_rpc()!=0)
+		{
+			LM_ERR("failed to register RPC commands\n");
+			goto error;
+		}
+		if(uac_reg_init_ht(reg_htable_size)<0)
+		{
+			LM_ERR("failed to init reg htable\n");
+			goto error;
+		}
+		uac_reg_init_db();
+		register_procs(1);
+	}
 	init_from_replacer();
 	init_from_replacer();
 
 
 	uac_req_init();
 	uac_req_init();
@@ -227,6 +267,33 @@ error:
 	return -1;
 	return -1;
 }
 }
 
 
+static int child_init(int rank)
+{
+	int pid;
+	if (rank!=PROC_MAIN)
+		return 0;
+
+	if(reg_db_url.s==NULL)
+		return 0;
+
+	pid=fork_process(PROC_TIMER, "TIMER UAC REG", 1);
+	if (pid<0)
+	{
+		LM_ERR("failed to register timer routine as process\n");
+		return -1;
+	}
+	if (pid==0){
+		/* child */
+		uac_reg_load_db();
+		uac_reg_timer(0);
+		for(;;){
+			sleep(reg_timer_interval);
+			uac_reg_timer(get_ticks());
+		}
+	}
+	/* parent */
+	return 0;
+}
 
 
 static void mod_destroy(void)
 static void mod_destroy(void)
 {
 {
@@ -362,3 +429,25 @@ static int w_uac_auth(struct sip_msg* msg, char* str, char* str2)
 }
 }
 
 
 
 
+static int w_uac_reg_lookup(struct sip_msg* msg,  char* src, char* dst)
+{
+	pv_spec_t *spv;
+	pv_spec_t *dpv;
+	pv_value_t val;
+
+	spv = (pv_spec_t*)src;
+	dpv = (pv_spec_t*)dst;
+	if(pv_get_spec_value(msg, spv, &val) != 0)
+	{
+		LM_ERR("cannot get src uri value\n");
+		return -1;
+	}
+
+	if (!(val.flags & PV_VAL_STR))
+	{
+	    LM_ERR("src pv value is not string\n");
+	    return -1;
+	}
+	return uac_reg_lookup(msg, &val.rs, dpv, 0);
+}
+

+ 926 - 0
modules_k/uac/uac_reg.c

@@ -0,0 +1,926 @@
+/**
+ * $Id$
+ *
+ * Copyright (C) 2010 Daniel-Constantin Mierla (asipto.com)
+ *
+ * This file is part of kamailio, a free SIP server.
+ *
+ * openser 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
+ *
+ * openser 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 <time.h>
+
+#include "../../dprint.h"
+#include "../../timer.h"
+
+#include "../../mem/shm_mem.h"
+#include "../../lib/srdb1/db.h"
+#include "../../ut.h"
+#include "../../hashes.h"
+#include "../../parser/parse_uri.h"
+#include "../../parser/contact/parse_contact.h"
+#include "../../rpc.h"
+#include "../../rpc_lookup.h"
+
+#include "../../modules/tm/tm_load.h"
+
+#include "auth.h"
+#include "auth_hdr.h"
+#include "uac_reg.h"
+
+#define UAC_REG_DISABLED	(1<<0)
+#define UAC_REG_ONGOING		(1<<1)
+#define UAC_REG_ONLINE		(1<<2)
+
+#define MAX_UACH_SIZE 2048
+
+typedef struct _reg_uac
+{
+	unsigned int h_uuid;
+	unsigned int h_user;
+	str   l_uuid;
+	str   l_username;
+	str   l_domain;
+	str   r_username;
+	str   r_domain;
+	str   realm;
+	str   auth_username;
+	str   auth_password;
+	str   auth_proxy;
+	unsigned int flags;
+	unsigned int expires;
+	time_t timer_expires;
+} reg_uac_t;
+
+typedef struct _reg_item
+{
+	reg_uac_t *r;
+	struct _reg_item *next;
+} reg_item_t;
+
+
+typedef struct _reg_entry
+{
+	unsigned int isize;
+	unsigned int usize;
+	reg_item_t *byuser;
+	reg_item_t *byuuid;
+} reg_entry_t;
+
+typedef struct _reg_ht
+{
+	unsigned int htsize;
+	reg_entry_t *entries;
+} reg_ht_t;
+
+static reg_ht_t *_reg_htable = NULL;
+
+int reg_use_domain = 0;
+int reg_timer_interval = 90;
+int reg_htable_size = 4;
+int reg_fetch_rows = 1000;
+str reg_contact_addr = {0, 0};
+str reg_db_url = {0, 0};
+str reg_db_table = {"uacreg", 0};
+
+str l_uuid_column = {"l_uuid", 0};
+str l_username_column = {"l_username", 0};
+str l_domain_column = {"l_domain", 0};
+str r_username_column = {"r_username", 0};
+str r_domain_column = {"r_domain", 0};
+str realm_column = {"realm", 0};
+str auth_username_column = {"auth_username", 0};
+str auth_password_column = {"auth_password", 0};
+str auth_proxy_column = {"auth_proxy", 0};
+str expires_column = {"expires", 0};
+
+#if 0
+INSERT INTO version (table_name, table_version) values ('uacreg','1');
+CREATE TABLE uacreg (
+    id INT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL,
+    l_uuid VARCHAR(64) NOT NULL,
+    l_username VARCHAR(64) NOT NULL,
+    l_domain VARCHAR(128) DEFAULT '' NOT NULL,
+    r_username VARCHAR(64) NOT NULL,
+    r_domain VARCHAR(128) NOT NULL,
+    realm VARCHAR(64) NOT NULL,
+    auth_username VARCHAR(64) NOT NULL,
+    auth_password VARCHAR(64) NOT NULL,
+    auth_proxy VARCHAR(128) DEFAULT '' NOT NULL,
+    expires INT(10) UNSIGNED DEFAULT 0 NOT NULL,
+    CONSTRAINT l_uuid_idx UNIQUE (l_uuid)
+) ENGINE=MyISAM;
+#endif
+
+
+extern struct tm_binds uac_tmb;
+
+/**
+ *
+ */
+int uac_reg_init_db(void)
+{
+	reg_contact_addr.len = strlen(reg_contact_addr.s);
+	
+	reg_db_url.len = strlen(reg_db_url.s);
+	reg_db_table.len = strlen(reg_db_table.s);
+
+	l_uuid_column.len = strlen(l_uuid_column.s);
+	l_username_column.len = strlen(l_username_column.s);
+	l_domain_column.len = strlen(l_domain_column.s);
+	r_username_column.len = strlen(r_username_column.s);
+	r_domain_column.len = strlen(r_domain_column.s);
+	realm_column.len = strlen(realm_column.s);
+	auth_username_column.len = strlen(auth_username_column.s);
+	auth_password_column.len = strlen(auth_password_column.s);
+	auth_proxy_column.len = strlen(auth_proxy_column.s);
+	expires_column.len = strlen(expires_column.s);
+
+	return 0;
+}
+
+/**
+ *
+ */
+int uac_reg_init_ht(unsigned int sz)
+{
+	_reg_htable = (reg_ht_t*)shm_malloc(sizeof(reg_ht_t));
+	if(_reg_htable==NULL)
+	{
+		LM_ERR("no more shm\n");
+		return -1;
+	}
+	memset(_reg_htable, 0, sizeof(reg_ht_t));
+	_reg_htable->htsize = sz;
+
+	_reg_htable->entries =
+			(reg_entry_t*)shm_malloc(_reg_htable->htsize*sizeof(reg_entry_t));
+	if(_reg_htable->entries==NULL)
+	{
+		LM_ERR("no more shm.\n");
+		shm_free(_reg_htable);
+		return -1;
+	}
+	memset(_reg_htable->entries, 0, _reg_htable->htsize*sizeof(reg_entry_t));
+
+	return 0;
+}
+
+/**
+ *
+ */
+int uac_reg_free_ht(void)
+{
+	int i;
+	reg_item_t *it = NULL;
+	reg_item_t *it0 = NULL;
+
+	if(_reg_htable==NULL)
+	{
+		LM_DBG("no hash table\n");
+		return -1;
+	}
+	for(i=0; i<_reg_htable->htsize; i++)
+	{
+		/* free entries */
+		it = _reg_htable->entries[i].byuuid;
+		while(it)
+		{
+			it0 = it;
+			it = it->next;
+			shm_free(it0);
+		}
+		it = _reg_htable->entries[i].byuser;
+		while(it)
+		{
+			it0 = it;
+			it = it->next;
+			shm_free(it0->r);
+			shm_free(it0);
+		}
+	}
+	shm_free(_reg_htable->entries);
+	shm_free(_reg_htable);
+	_reg_htable = NULL;
+	return 0;
+}
+
+#define reg_compute_hash(_s)		get_hash1_raw((_s)->s,(_s)->len)
+#define reg_get_entry(_h,_size)		((_h)&((_size)-1))
+
+/**
+ *
+ */
+int reg_ht_add_byuuid(reg_uac_t *reg)
+{
+	unsigned int slot;
+	reg_item_t *ri = NULL;
+
+	ri = (reg_item_t*)shm_malloc(sizeof(reg_item_t));
+	if(ri==NULL)
+	{
+		LM_ERR("no more shm\n");
+		return -1;
+	}
+	memset(ri, 0, sizeof(reg_item_t));
+	slot = reg_get_entry(reg->h_uuid, _reg_htable->htsize);
+	ri->r = reg;
+	ri->next = _reg_htable->entries[slot].byuuid;
+	_reg_htable->entries[slot].byuuid = ri;
+	_reg_htable->entries[slot].isize++;
+	return 0;
+}
+
+/**
+ *
+ */
+int reg_ht_add_byuser(reg_uac_t *reg)
+{
+	unsigned int slot;
+	reg_item_t *ri = NULL;
+
+	ri = (reg_item_t*)shm_malloc(sizeof(reg_item_t));
+	if(ri==NULL)
+	{
+		LM_ERR("no more shm\n");
+		return -1;
+	}
+	memset(ri, 0, sizeof(reg_item_t));
+	slot = reg_get_entry(reg->h_user, _reg_htable->htsize);
+	ri->r = reg;
+	ri->next = _reg_htable->entries[slot].byuser;
+	_reg_htable->entries[slot].byuser = ri;
+	_reg_htable->entries[slot].usize++;
+	return 0;
+}
+
+#define reg_copy_shm(dst, src) do { \
+		if((src)->s!=NULL) { \
+			(dst)->s = p; \
+			strncpy((dst)->s, (src)->s, (src)->len); \
+			(dst)->len = (src)->len; \
+			(dst)->s[(dst)->len] = '\0'; \
+			p = p + (dst)->len + 1; \
+		} \
+	} while(0);
+
+/**
+ *
+ */
+int reg_ht_add(reg_uac_t *reg)
+{
+	int len;
+	reg_uac_t *nr = NULL;
+	char *p;
+
+	if(reg==NULL || _reg_htable==NULL)
+	{
+		LM_ERR("bad paramaers: %p/%p\n", reg, _reg_htable);
+		return -1;
+	}
+	len = reg->l_uuid.len + 1
+			+ reg->l_username.len + 1
+			+ reg->l_domain.len + 1
+			+ reg->r_username.len + 1
+			+ reg->r_domain.len + 1
+			+ reg->realm.len + 1
+			+ reg->auth_username.len + 1
+			+ reg->auth_password.len + 1
+			+ reg->auth_proxy.len + 1;
+	nr = (reg_uac_t*)shm_malloc(sizeof(reg_uac_t) + len);
+	if(nr==NULL)
+	{
+		LM_ERR("no more shm\n");
+		return -1;
+	}
+	memset(nr, 0, sizeof(reg_uac_t) + len);
+	nr->expires = reg->expires;
+	nr->h_uuid = reg_compute_hash(&reg->l_uuid);
+	nr->h_user = reg_compute_hash(&reg->l_username);
+	
+	p = (char*)nr + sizeof(reg_uac_t);
+
+	reg_copy_shm(&nr->l_uuid, &reg->l_uuid);
+	reg_copy_shm(&nr->l_username, &reg->l_username);
+	reg_copy_shm(&nr->l_domain, &reg->l_domain);
+	reg_copy_shm(&nr->r_username, &reg->r_username);
+	reg_copy_shm(&nr->r_domain, &reg->r_domain);
+	reg_copy_shm(&nr->realm, &reg->realm);
+	reg_copy_shm(&nr->auth_username, &reg->auth_username);
+	reg_copy_shm(&nr->auth_password, &reg->auth_password);
+	reg_copy_shm(&nr->auth_proxy, &reg->auth_proxy);
+
+	reg_ht_add_byuser(nr);
+	reg_ht_add_byuuid(nr);
+
+	return 0;
+}
+
+
+/**
+ *
+ */
+reg_uac_t *reg_ht_get_byuuid(str *uuid)
+{
+	unsigned int hash;
+	unsigned int slot;
+	reg_item_t *it = NULL;
+
+	hash = reg_compute_hash(uuid);
+	slot = reg_get_entry(hash, _reg_htable->htsize);
+	it = _reg_htable->entries[slot].byuuid;
+	while(it)
+	{
+		if((it->r->h_uuid==hash) && (it->r->l_uuid.len==uuid->len)
+				&& (strncmp(it->r->l_uuid.s, uuid->s, uuid->len)==0))
+		{
+			return it->r;
+		}
+		it = it->next;
+	}
+	return NULL;
+}
+
+/**
+ *
+ */
+reg_uac_t *reg_ht_get_byuser(str *user, str *domain)
+{
+	unsigned int hash;
+	unsigned int slot;
+	reg_item_t *it = NULL;
+
+	hash = reg_compute_hash(user);
+	slot = reg_get_entry(hash, _reg_htable->htsize);
+	it = _reg_htable->entries[slot].byuser;
+	while(it)
+	{
+		if((it->r->h_uuid==hash) && (it->r->l_username.len==user->len)
+				&& (strncmp(it->r->l_username.s, user->s, user->len)==0))
+		{
+			if(domain!=NULL && domain->s!=NULL)
+			{
+				if((it->r->l_domain.len==domain->len)
+				&& (strncmp(it->r->l_domain.s, domain->s, domain->len)==0))
+				{
+					return it->r;
+				}
+			}
+		}
+		it = it->next;
+	}
+	return NULL;
+}
+
+void uac_reg_tm_callback( struct cell *t, int type, struct tmcb_params *ps)
+{
+	char *uuid;
+	str suuid;
+	reg_uac_t *ri = NULL;
+	contact_t* c;
+	int expires;
+	struct sip_uri puri;
+	struct hdr_field *hdr;
+	HASHHEX response;
+	str *new_auth_hdr = NULL;
+	static struct authenticate_body auth;
+	struct uac_credential cred;
+	char  b_ruri[MAX_URI_SIZE];
+	str   s_ruri;
+	char  b_turi[MAX_URI_SIZE];
+	str   s_turi;
+	char  b_hdrs[MAX_UACH_SIZE];
+	str   s_hdrs;
+	uac_req_t uac_r;
+	str method = {"REGISTER", 8};
+	int ret;
+
+	if(ps->param==NULL || *ps->param==0)
+	{
+		LM_DBG("uuid not received\n");
+		return;
+	}
+	uuid = *((char**)ps->param);
+	LM_DBG("completed with status %d [uuid: %s]\n",
+		ps->code, uuid);
+	suuid.s = uuid;
+	suuid.len = strlen(suuid.s);
+	ri = reg_ht_get_byuuid(&suuid);
+
+	if(ri==NULL)
+	{
+		LM_DBG("no user with uuid %s\n", uuid);
+		goto done;
+	}
+
+	if(ps->code == 200)
+	{
+		if (parse_headers(ps->rpl, HDR_EOH_F, 0) == -1)
+		{
+			LM_ERR("failed to parse headers\n");
+			goto done;
+		}
+		if (ps->rpl->contact==NULL)
+		{
+			LM_ERR("no Contact found\n");
+			goto done;
+		}
+		if (parse_contact(ps->rpl->contact) < 0)
+		{
+			LM_ERR("failed to parse Contact HF\n");
+			goto done;
+		}
+		if (((contact_body_t*)ps->rpl->contact->parsed)->star)
+		{
+			LM_DBG("* Contact found\n");
+			goto done;
+		}
+
+		if (contact_iterator(&c, ps->rpl, 0) < 0)
+			goto done;
+		while(c)
+		{
+			if(parse_uri(c->uri.s, c->uri.len, &puri)!=0)
+			{
+				LM_ERR("failed to parse c-uri\n");
+				goto done;
+			}
+			if(suuid.len==puri.user.len
+					&& (strncmp(puri.user.s, suuid.s, suuid.len)==0))
+			{
+				/* calculate expires */
+				expires=0;
+				if(c->expires==NULL || c->expires->body.len<=0)
+				{
+					if(ps->rpl->expires!=NULL && ps->rpl->expires->body.len>0)
+						expires = atoi(ps->rpl->expires->body.s);
+				} else {
+					str2int(&c->expires->body, (unsigned int*)(&expires));
+				}
+				ri->timer_expires = ri->timer_expires + expires;
+				ri->flags &= ~UAC_REG_ONGOING;
+				ri->flags |= UAC_REG_ONLINE;
+				goto done;
+			}
+			if (contact_iterator(&c, ps->rpl, c) < 0)
+			{
+				LM_DBG("local contact not found\n");
+				goto done;
+			}
+		}
+	}
+
+	if(ps->code == 401)
+	{
+		hdr = get_autenticate_hdr(ps->rpl, 401);
+		if (hdr==0)
+		{
+			LM_ERR("failed to extract authenticate hdr\n");
+			goto done;
+		}
+
+		LM_DBG("auth header body [%.*s]\n",
+			hdr->body.len, hdr->body.s);
+
+		if (parse_authenticate_body(&hdr->body, &auth)<0)
+		{
+			LM_ERR("failed to parse auth hdr body\n");
+			goto done;
+		}
+		if(auth.realm.len!=ri->realm.len
+				|| strncmp(auth.realm.s, ri->realm.s, ri->realm.len)!=0)
+		{
+			LM_ERR("realms are different - ignire?!?!\n");
+		}
+		cred.realm = auth.realm;
+		cred.user = ri->auth_username; 
+		cred.passwd = ri->auth_password;
+ 		cred.next = NULL;
+
+		snprintf(b_ruri, MAX_URI_SIZE, "sip:%.*s",
+				ri->r_domain.len, ri->r_domain.s);
+		s_ruri.s = b_ruri; s_ruri.len = strlen(s_ruri.s);
+
+		do_uac_auth(&method, &s_ruri, &cred, &auth, response);
+		new_auth_hdr=build_authorization_hdr(401, &s_ruri, &cred,
+						&auth, response);
+		if (new_auth_hdr==0)
+		{
+			LM_ERR("failed to build authorization hdr\n");
+			goto done;
+		}
+		
+		snprintf(b_turi, MAX_URI_SIZE, "sip:%.*s@%.*s",
+				ri->r_username.len, ri->r_username.s,
+				ri->r_domain.len, ri->r_domain.s);
+		s_turi.s = b_turi; s_turi.len = strlen(s_turi.s);
+
+		snprintf(b_hdrs, MAX_UACH_SIZE,
+				"Contact: <sip:%.*s@%.*s>\r\n"
+				"Expires: %d\r\n"
+				"%.*s",
+				ri->l_uuid.len, ri->l_uuid.s,
+				reg_contact_addr.len, reg_contact_addr.s,
+				ri->expires,
+				new_auth_hdr->len, new_auth_hdr->s);
+		s_hdrs.s = b_hdrs; s_hdrs.len = strlen(s_hdrs.s);
+		pkg_free(new_auth_hdr->s);
+
+		memset(&uac_r, '\0', sizeof(uac_r));
+		uac_r.method = &method;
+		uac_r.headers = &s_hdrs;
+		uac_r.cb_flags = TMCB_LOCAL_COMPLETED;
+		/* Callback function */
+		uac_r.cb  = uac_reg_tm_callback;
+		/* Callback parameter */
+		uac_r.cbp = (void*)uuid;
+		ret = uac_tmb.t_request(&uac_r,  /* UAC Req */
+				&s_ruri, /* Request-URI */
+				&s_turi, /* To */
+				&s_turi, /* From */
+				(ri->auth_proxy.len)?&ri->auth_proxy:NULL /* outbound uri */
+			);
+
+		if(ret<0)
+			goto done;
+		return;
+	}
+
+done:
+	ri->flags &= ~UAC_REG_ONGOING;
+	shm_free(uuid);
+}
+
+int uac_reg_update(reg_uac_t *reg, time_t tn)
+{
+	char *uuid;
+	uac_req_t uac_r;
+	str method = {"REGISTER", 8};
+	int ret;
+	char  b_ruri[MAX_URI_SIZE];
+	str   s_ruri;
+	char  b_turi[MAX_URI_SIZE];
+	str   s_turi;
+	char  b_hdrs[MAX_UACH_SIZE];
+	str   s_hdrs;
+
+	if(uac_tmb.t_request==NULL)
+		return -1;
+	if(reg->expires==0)
+		return 1;
+	if(reg->flags&UAC_REG_ONGOING)
+		return 2;
+	if(reg->timer_expires > tn + reg_timer_interval + 3)
+		return 3;
+	reg->timer_expires = tn;
+	reg->flags |= UAC_REG_ONGOING;
+	reg->flags &= ~UAC_REG_ONLINE;
+	uuid = (char*)shm_malloc(reg->l_uuid.len+1);
+	if(uuid==NULL)
+	{
+		LM_ERR("no more shm\n");
+		return -1;
+	}
+	memcpy(uuid, reg->l_uuid.s, reg->l_uuid.len);
+	uuid[reg->l_uuid.len] = '\0';
+
+	snprintf(b_ruri, MAX_URI_SIZE, "sip:%.*s",
+			reg->r_domain.len, reg->r_domain.s);
+	s_ruri.s = b_ruri; s_ruri.len = strlen(s_ruri.s);
+
+	snprintf(b_turi, MAX_URI_SIZE, "sip:%.*s@%.*s",
+			reg->r_username.len, reg->r_username.s,
+			reg->r_domain.len, reg->r_domain.s);
+	s_turi.s = b_turi; s_turi.len = strlen(s_turi.s);
+
+	snprintf(b_hdrs, MAX_UACH_SIZE,
+			"Contact: <sip:%.*s@%.*s>\r\n"
+			"Expires: %d\r\n",
+			reg->l_uuid.len, reg->l_uuid.s,
+			reg_contact_addr.len, reg_contact_addr.s,
+			reg->expires);
+	s_hdrs.s = b_hdrs; s_hdrs.len = strlen(s_hdrs.s);
+
+	memset(&uac_r, '\0', sizeof(uac_r));
+	uac_r.method = &method;
+	uac_r.headers = &s_hdrs;
+	uac_r.cb_flags = TMCB_LOCAL_COMPLETED;
+	/* Callback function */
+	uac_r.cb  = uac_reg_tm_callback;
+	/* Callback parameter */
+	uac_r.cbp = (void*)uuid;
+	ret = uac_tmb.t_request(&uac_r,  /* UAC Req */
+			&s_ruri, /* Request-URI */
+			&s_turi, /* To */
+			&s_turi, /* From */
+			(reg->auth_proxy.len)?&reg->auth_proxy:NULL /* outbound uri */
+		);
+
+	if(ret<0)
+	{
+		shm_free(uuid);
+		return -1;
+	}
+	return 0;
+}
+
+/**
+ *
+ */
+void uac_reg_timer(unsigned int ticks)
+{
+	int i;
+	reg_item_t *it = NULL;
+	time_t tn;
+
+	tn = time(NULL);
+	for(i=0; i<_reg_htable->htsize; i++)
+	{
+		/* free entries */
+		it = _reg_htable->entries[i].byuuid;
+		while(it)
+		{
+			uac_reg_update(it->r, tn);
+			it = it->next;
+		}
+	}
+}
+
+#define reg_db_set_attr(attr, pos) do { \
+		if(!VAL_NULL(&RES_ROWS(db_res)[i].values[pos])) { \
+			reg.attr.s = \
+				(char*)(RES_ROWS(db_res)[i].values[pos].val.string_val); \
+			reg.attr.len = strlen(reg.attr.s); \
+		} \
+	} while(0);
+
+/**
+ *
+ */
+int uac_reg_load_db(void)
+{
+	db1_con_t *reg_db_con = NULL;
+	db_func_t reg_dbf;
+	reg_uac_t reg;
+	db_key_t db_cols[10] = {
+		&l_uuid_column,
+		&l_username_column,
+		&l_domain_column,
+		&r_username_column,
+		&r_domain_column,
+		&realm_column,
+		&auth_username_column,
+		&auth_password_column,
+		&auth_proxy_column,
+		&expires_column
+	};
+	db1_res_t* db_res = NULL;
+	int i, ret;
+
+	/* binding to db module */
+	if(reg_db_url.s==NULL)
+	{
+		LM_ERR("no db url\n");
+		return -1;
+	}
+
+	if(db_bind_mod(&reg_db_url, &reg_dbf))
+	{
+		LM_ERR("database module not found\n");
+		return -1;
+	}
+
+	if (!DB_CAPABILITY(reg_dbf, DB_CAP_ALL))
+	{
+		LM_ERR("database module does not "
+		    "implement all functions needed by the module\n");
+		return -1;
+	}
+
+	/* open a connection with the database */
+	reg_db_con = reg_dbf.init(&reg_db_url);
+	if(reg_db_con==NULL)
+	{
+		LM_ERR("failed to connect to the database\n");        
+		return -1;
+	}
+	if (reg_dbf.use_table(reg_db_con, &reg_db_table) < 0)
+	{
+		LM_ERR("failed to use_table\n");
+		return -1;
+	}
+
+	if (DB_CAPABILITY(reg_dbf, DB_CAP_FETCH)) {
+		if(reg_dbf.query(reg_db_con, 0, 0, 0, db_cols, 0, 10, 0, 0) < 0)
+		{
+			LM_ERR("Error while querying db\n");
+			return -1;
+		}
+		if(reg_dbf.fetch_result(reg_db_con, &db_res, reg_fetch_rows)<0)
+		{
+			LM_ERR("Error while fetching result\n");
+			if (db_res)
+				reg_dbf.free_result(reg_db_con, db_res);
+			goto error;
+		} else {
+			if(RES_ROW_N(db_res)==0)
+			{
+				goto done;
+			}
+		}
+	} else {
+		if((ret=reg_dbf.query(reg_db_con, NULL, NULL, NULL, db_cols,
+				0, 10, 0, &db_res))!=0
+			|| RES_ROW_N(db_res)<=0 )
+		{
+			reg_dbf.free_result(reg_db_con, db_res);
+			if( ret==0)
+			{
+				return 0;
+			} else {
+				goto error;
+			}
+		}
+	}
+
+	do {
+		for(i=0; i<RES_ROW_N(db_res); i++)
+		{
+			memset(&reg, 0, sizeof(reg_uac_t));;
+			/* check for NULL values ?!?! */
+			reg_db_set_attr(l_uuid, 0);
+			reg_db_set_attr(l_username, 1);
+			reg_db_set_attr(l_domain, 2);
+			reg_db_set_attr(r_username, 3);
+			reg_db_set_attr(r_domain, 4);
+			reg_db_set_attr(realm, 5);
+			reg_db_set_attr(auth_username, 6);
+			reg_db_set_attr(auth_password, 7);
+			reg_db_set_attr(auth_proxy, 8);
+			reg.expires
+				= (unsigned int)RES_ROWS(db_res)[i].values[9].val.int_val;
+			
+			if(reg_ht_add(&reg)<0)
+			{
+				LM_ERR("Error adding reg to htable\n");
+				goto error;
+			}
+	 	}
+		if (DB_CAPABILITY(reg_dbf, DB_CAP_FETCH)) {
+			if(reg_dbf.fetch_result(reg_db_con, &db_res, reg_fetch_rows)<0) {
+				LM_ERR("Error while fetching!\n");
+				if (db_res)
+					reg_dbf.free_result(reg_db_con, db_res);
+				goto error;
+			}
+		} else {
+			break;
+		}
+	}  while(RES_ROW_N(db_res)>0);
+	reg_dbf.free_result(reg_db_con, db_res);
+
+done:
+	return 0;
+
+error:
+	reg_dbf.free_result(reg_db_con, db_res);
+	return -1;
+}
+
+/**
+ *
+ */
+int  uac_reg_lookup(struct sip_msg *msg, str *src, pv_spec_t *dst, int mode)
+{
+	char  b_ruri[MAX_URI_SIZE];
+	str   s_ruri;
+	struct sip_uri puri;
+	reg_uac_t *reg = NULL;
+	pv_value_t val;
+
+	if(!pv_is_w(dst))
+	{
+		LM_ERR("dst is not w/\n");
+		return -1;
+	}
+	if(mode==0)
+	{
+		reg = reg_ht_get_byuuid(src);
+		if(reg==NULL)
+		{
+			LM_DBG("no uuid: %.*s\n", src->len, src->s);
+			return -1;
+		}
+		snprintf(b_ruri, MAX_URI_SIZE, "sip:%.*s@%.*s",
+			reg->l_username.len, reg->l_username.s,
+			reg->l_domain.len, reg->l_domain.s);
+		s_ruri.s = b_ruri; s_ruri.len = strlen(s_ruri.s);
+	} else {
+		if(parse_uri(src->s, src->len, &puri)!=0)
+		{
+			LM_ERR("failed to parse uri\n");
+			return -2;
+		}
+		reg = reg_ht_get_byuser(&puri.user, (reg_use_domain)?&puri.host:NULL);
+		if(reg==NULL)
+		{
+			LM_DBG("no user: %.*s\n", src->len, src->s);
+			return -1;
+		}
+		snprintf(b_ruri, MAX_URI_SIZE, "%.*s",
+			reg->l_uuid.len, reg->l_uuid.s);
+		s_ruri.s = b_ruri; s_ruri.len = strlen(s_ruri.s);
+	}
+	memset(&val, 0, sizeof(pv_value_t));
+	val.flags |= PV_VAL_STR;
+	val.rs = s_ruri;
+	if(pv_set_spec_value(msg, dst, 0, &val)!=0)
+		return -1;
+
+	return 1;
+}
+
+static const char* rpc_uac_reg_dump_doc[2] = {
+	"Dump the contents of user registrations table.",
+	0
+};
+
+static void rpc_uac_reg_dump(rpc_t* rpc, void* ctx)
+{
+	int i;
+	reg_item_t *reg = NULL;
+	void* th;
+	str none = {"none", 4};
+	time_t tn;
+
+	if(_reg_htable==NULL)
+	{
+		rpc->fault(ctx, 500, "Not enabled");
+		return;
+	}
+
+	tn = time(NULL);
+
+	for(i=0; i<_reg_htable->htsize; i++)
+	{
+		/* free entries */
+		reg = _reg_htable->entries[i].byuuid;
+		while(reg)
+		{
+			/* add entry node */
+			if (rpc->add(ctx, "{", &th) < 0)
+			{
+				rpc->fault(ctx, 500, "Internal error creating rpc");
+				return;
+			}
+			if(rpc->struct_add(th, "SSSSSSSSSdddd",
+					"l_uuid",        &reg->r->l_uuid,
+					"l_username",    &reg->r->l_username,
+					"l_domain",      &reg->r->l_domain,
+					"r_username",    &reg->r->r_username,
+					"r_domain",      &reg->r->r_domain,
+					"realm",         &reg->r->realm,
+					"auth_username", &reg->r->auth_username,
+					"auth_password", &reg->r->auth_password,
+					"auth_proxy",    (reg->r->auth_proxy.len)?
+										&reg->r->auth_proxy:&none,
+					"expires",       (int)reg->r->expires,
+					"flags",         (int)reg->r->flags,
+					"diff_expires",  (int)(reg->r->timer_expires - tn),
+					"timer_expires", (int)reg->r->timer_expires
+				)<0)
+			{
+				rpc->fault(ctx, 500, "Internal error adding item");
+				return;
+			}
+			reg = reg->next;
+		}
+	}
+}
+
+rpc_export_t uac_reg_rpc[] = {
+	{"uac.reg_dump", rpc_uac_reg_dump, rpc_uac_reg_dump_doc, 0},
+	{0, 0, 0, 0}
+};
+
+int uac_reg_init_rpc(void)
+{
+	if (rpc_register_array(uac_reg_rpc)!=0)
+	{
+		LM_ERR("failed to register RPC commands\n");
+		return -1;
+	}
+	return 0;
+}

+ 55 - 0
modules_k/uac/uac_reg.h

@@ -0,0 +1,55 @@
+/**
+ * $Id$
+ *
+ * Copyright (C) 2010 Daniel-Constantin Mierla (asipto.com)
+ *
+ * This file is part of kamailio, a free SIP server.
+ *
+ * openser 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
+ *
+ * openser 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 _UAC_REG_H_
+#define _UAC_REG_H_
+
+#include "../../pvar.h"
+
+extern int reg_timer_interval;
+extern int reg_htable_size;
+extern int reg_fetch_rows;
+extern str reg_contact_addr;
+extern str reg_db_url;
+extern str reg_db_table;
+
+extern str l_uuid_column;
+extern str l_username_column;
+extern str l_domain_column;
+extern str r_username_column;
+extern str r_domain_column;
+extern str realm_column;
+extern str auth_username_column;
+extern str auth_password_column;
+extern str auth_proxy_column;
+extern str expires_column;
+
+int uac_reg_init_db(void);
+int uac_reg_load_db(void);
+int uac_reg_init_ht(unsigned int sz);
+int uac_reg_free_ht(void);
+
+void uac_reg_timer(unsigned int ticks);
+int uac_reg_init_rpc(void);
+
+int  uac_reg_lookup(struct sip_msg *msg, str *src, pv_spec_t *dst, int mode);
+#endif