everything: shotgun attempt to put PROTO_WS and PROTO_WSS across core and in modules...
[sip-router] / modules_k / registrar / save.c
index 8d9cacf..4b3eda2 100644 (file)
@@ -4,15 +4,16 @@
  * Process REGISTER request and send reply
  *
  * Copyright (C) 2001-2003 FhG Fokus
+ * Copyright (C) 2006 Voice Sistem SRL
  *
- * This file is part of openser, a free SIP server.
+ * This file is part of Kamailio, a free SIP server.
  *
- * openser is free software; you can redistribute it and/or modify
+ * 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
  *
- * openser is distributed in the hope that it will be useful,
+ * 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.
  *            instead of UDP package (bogdan)
  * 2006-04-13 added tcp_persistent_flag for keeping the TCP connection as long
  *            as a TCP contact is registered (bogdan)
+ * 2006-11-22 save_noreply and save_memory merged into save() (bogdan)
+ * 2006-11-28 Added statistic support for the number of accepted/rejected 
+ *            registrations. (Jeffrey Magder - SOMA Networks) 
+ * 2007-02-24  sip_natping_flag moved into branch flags, so migrated to 
+ *             nathelper module (bogdan)
  */
+/*!
+ * \file
+ * \brief SIP registrar module - Process REGISTER request and send reply
+ * \ingroup registrar   
+ */  
 
 
 #include "../../str.h"
 #include "../../parser/parse_allow.h"
 #include "../../parser/parse_methods.h"
 #include "../../parser/msg_parser.h"
+#include "../../parser/parse_to.h"
 #include "../../parser/parse_uri.h"
 #include "../../dprint.h"
 #include "../../trim.h"
 #include "../../ut.h"
 #include "../../qvalue.h"
+#include "../../dset.h"
+#include "../../xavp.h"
+#include "../../mod_fix.h"
+#include "../../lib/srutils/sruid.h"
+#include "../../lib/kcore/cmpapi.h"
+#include "../../lib/kcore/statistics.h"
 #ifdef USE_TCP
 #include "../../tcp_server.h"
 #endif
 #include "regtime.h"
 #include "path.h"
 #include "save.h"
+#include "config.h"
 
 static int mem_only = 0;
-int_str rcv_avp;
 
-void remove_cont(urecord_t* _r, ucontact_t* _c)
-{
-       if (_c->prev) {
-               _c->prev->next = _c->next;
-               if (_c->next) {
-                       _c->next->prev = _c->prev;
-               }
-       } else {
-               _r->contacts = _c->next;
-               if (_c->next) {
-                       _c->next->prev = 0;
-               }
-       }
-}
-
-
-void move_on_top(urecord_t* _r, ucontact_t* _c)
-{
-       ucontact_t* prev;
-
-       if (!_r->contacts) return;
-       if (_c->prev == 0) return;
-
-       prev = _c->prev;
-
-       remove_cont(_r, _c);
-       
-       _c->next = _r->contacts;
-       _c->prev = 0;
-
-       _r->contacts->prev = _c;
-       _r->contacts = _c;
-}
+extern sruid_t _reg_sruid;
 
-
-/*
+/*! \brief
  * Process request that contained a star, in that case, 
  * we will remove all bindings with the given username 
  * from the usrloc and return 200 OK response
  */
-static inline int star(udomain_t* _d, str* _a)
+static inline int star(sip_msg_t *_m, udomain_t* _d, str* _a, str *_h)
 {
        urecord_t* r;
        ucontact_t* c;
        
-       ul.lock_udomain(_d);
+       ul.lock_udomain(_d, _a);
 
        if (!ul.get_urecord(_d, _a, &r)) {
                c = r->contacts;
@@ -117,10 +102,12 @@ static inline int star(udomain_t* _d, str* _a)
                        }
                        c = c->next;
                }
+       } else {
+               r = NULL;
        }
 
-       if (ul.delete_urecord(_d, _a, 0) < 0) {
-               LOG(L_ERR, "star(): Error while removing record from usrloc\n");
+       if (ul.delete_urecord(_d, _a, r) < 0) {
+               LM_ERR("failed to remove record from usrloc\n");
                
                     /* Delete failed, try to get corresponding
                      * record structure and send back all existing
@@ -128,17 +115,18 @@ static inline int star(udomain_t* _d, str* _a)
                      */
                rerrno = R_UL_DEL_R;
                if (!ul.get_urecord(_d, _a, &r)) {
-                       build_contact(r->contacts);
+                       build_contact(_m, r->contacts, _h);
+                       ul.release_urecord(r);
                }
-               ul.unlock_udomain(_d);
+               ul.unlock_udomain(_d, _a);
                return -1;
        }
-       ul.unlock_udomain(_d);
+       ul.unlock_udomain(_d, _a);
        return 0;
 }
 
 
-/*
+/*! \brief
  */
 static struct socket_info *get_sock_hdr(struct sip_msg *msg)
 {
@@ -148,15 +136,15 @@ static struct socket_info *get_sock_hdr(struct sip_msg *msg)
        str hosts;
        int port;
        int proto;
+       char c;
 
        if (parse_headers( msg, HDR_EOH_F, 0) == -1) {
-               LOG(L_ERR,"ERROR:registrar:get_sock_hdr: failed to parse message\n");
+               LM_ERR("failed to parse message\n");
                return 0;
        }
 
        for (hf=msg->headers; hf; hf=hf->next) {
-               if (hf->name.len==sock_hdr_name.len &&
-               strncasecmp(hf->name.s, sock_hdr_name.s, sock_hdr_name.len)==0 )
+               if (cmp_hdrname_str(&hf->name, &sock_hdr_name)==0)
                        break;
        }
 
@@ -168,62 +156,67 @@ static struct socket_info *get_sock_hdr(struct sip_msg *msg)
        if (socks.len==0)
                return 0;
 
-       if (parse_phostport( socks.s, socks.len, &hosts.s, &hosts.len, 
+       /*FIXME: This is a hack */
+       c = socks.s[socks.len];
+       socks.s[socks.len] = '\0';
+       if (parse_phostport( socks.s, &hosts.s, &hosts.len,
        &port, &proto)!=0) {
-               LOG(L_ERR,"ERROR:registrar:get_sock_hdr: bad socket <%.*s> in \n",
+               socks.s[socks.len] = c;
+               LM_ERR("bad socket <%.*s> in \n",
                        socks.len, socks.s);
                return 0;
        }
+       socks.s[socks.len] = c;
        sock = grep_sock_info(&hosts,(unsigned short)port,(unsigned short)proto);
        if (sock==0) {
-               LOG(L_WARN,"ERROR:registrar:get_sock_hdr: non-local socket <%.*s>\n",
-                       socks.len, socks.s);
+               LM_ERR("non-local socket <%.*s>\n",     socks.len, socks.s);
                return 0;
        }
 
-       DBG("DEBUG:registrar:get_sock_hdr: %d:<%.*s>:%d -> p=%p\n",
-               proto,socks.len,socks.s,port_no,sock );
+       LM_DBG("%d:<%.*s>:%d -> p=%p\n", proto,socks.len,socks.s,port_no,sock );
 
        return sock;
 }
 
 
 
-/*
+/*! \brief
  * Process request that contained no contact header
  * field, it means that we have to send back a response
  * containing a list of all existing bindings for the
  * given username (in To HF)
  */
-static inline int no_contacts(udomain_t* _d, str* _a)
+static inline int no_contacts(sip_msg_t *_m, udomain_t* _d, str* _a, str* _h)
 {
        urecord_t* r;
        int res;
        
-       ul.lock_udomain(_d);
+       ul.lock_udomain(_d, _a);
        res = ul.get_urecord(_d, _a, &r);
        if (res < 0) {
                rerrno = R_UL_GET_R;
-               LOG(L_ERR, "ERROR:registrar:no_contacts: failed to retrieve record "
-                       "from usrloc\n");
-               ul.unlock_udomain(_d);
+               LM_ERR("failed to retrieve record from usrloc\n");
+               ul.unlock_udomain(_d, _a);
                return -1;
        }
        
        if (res == 0) {  /* Contacts found */
-               build_contact(r->contacts);
+               build_contact(_m, r->contacts, _h);
+               ul.release_urecord(r);
+       } else {  /* No contacts found */
+               build_contact(_m, NULL, _h);
        }
-       ul.unlock_udomain(_d);
+       ul.unlock_udomain(_d, _a);
        return 0;
 }
 
 
 
-/*
+/*! \brief
  * Fills the common part (for all contacts) of the info structure
  */
 static inline ucontact_info_t* pack_ci( struct sip_msg* _m, contact_t* _c,
-                                                                                                       int _e, int _f1, int _f2)
+                                                                                       unsigned int _e, unsigned int _f)
 {
        static ucontact_info_t ci;
        static str no_ua = str_init("n/a");
@@ -244,7 +237,7 @@ static inline ucontact_info_t* pack_ci( struct sip_msg* _m, contact_t* _c,
                trim_trailing(&callid);
                if (callid.len > CALLID_MAX_SIZE) {
                        rerrno = R_CALLID_LEN;
-                       LOG(L_ERR, "ERROR:registrar:pack_ci: callid too long\n");
+                       LM_ERR("callid too long\n");
                        goto error;
                }
                ci.callid = &callid;
@@ -252,8 +245,7 @@ static inline ucontact_info_t* pack_ci( struct sip_msg* _m, contact_t* _c,
                /* Get CSeq number of the message */
                if (str2int(&get_cseq(_m)->number, (unsigned int*)&ci.cseq) < 0) {
                        rerrno = R_INV_CSEQ;
-                       LOG(L_ERR, "ERROR:registrar:pack_ci: failed to convert "
-                               "cseq number\n");
+                       LM_ERR("failed to convert cseq number\n");
                        goto error;
                }
 
@@ -282,16 +274,28 @@ static inline ucontact_info_t* pack_ci( struct sip_msg* _m, contact_t* _c,
                        }
                        if (path.len && path.s) {
                                ci.path = &path;
-                               /* save in msg too for reply */
-                               if (set_path_vector(_m, &path) < 0) {
-                                       rerrno = R_PARSE_PATH;
-                                       goto error;
+                               if (path_mode != PATH_MODE_OFF) {
+                                       /* save in msg too for reply */
+                                       if (set_path_vector(_m, &path) < 0) {
+                                               rerrno = R_PARSE_PATH;
+                                               goto error;
+                                       }
                                }
                        }
                }
 
                ci.last_modified = act_time;
 
+               /* set flags */
+               ci.flags  = _f;
+               getbflagsval(0, &ci.cflags);
+
+               /* get received */
+               if (path_received.len && path_received.s) {
+                       ci.cflags |= ul.nat_flag;
+                       ci.received = path_received;
+               }
+
                allow_parsed = 0; /* not parsed yet */
                received_found = 0; /* not found yet */
                m = _m; /* remember the message */
@@ -301,23 +305,18 @@ static inline ucontact_info_t* pack_ci( struct sip_msg* _m, contact_t* _c,
                /* Calculate q value of the contact */
                if (calc_contact_q(_c->q, &ci.q) < 0) {
                        rerrno = R_INV_Q;
-                       LOG(L_ERR, "ERROR:registrar:pack_ci: failed to calculate q\n");
+                       LM_ERR("failed to calculate q\n");
                        goto error;
                }
 
                /* set expire time */
                ci.expires = _e;
 
-               /* set flags */
-               ci.flags1 = _f1;
-               ci.flags2 = _f2;
-
                /* Get methods of contact */
                if (_c->methods) {
                        if (parse_methods(&(_c->methods->body), &ci.methods) < 0) {
                                rerrno = R_PARSE;
-                               LOG(L_ERR, "ERROR:usrloc:pack_ci: failed to parse "
-                                       "contact methods\n");
+                               LM_ERR("failed to parse contact methods\n");
                                goto error;
                        }
                } else {
@@ -334,32 +333,42 @@ static inline ucontact_info_t* pack_ci( struct sip_msg* _m, contact_t* _c,
                }
 
                /* get received */
-               if (path_received.len && path_received.s) {
-                       ci.flags1 |= FL_NAT;
-                       ci.flags2 &= ~FL_NAT;
-                       ci.received = path_received;
-               }
-               else if (_c->received) {
-                       ci.received = _c->received->body;
-               } else {
-                       if (received_found==0) {
-                               if (search_first_avp(0, rcv_avp, &val, 0) && val.s.s) {
-                                       if (val.s.len>RECEIVED_MAX_SIZE) {
-                                               rerrno = R_CONTACT_LEN;
-                                               LOG(L_ERR,"ERROR:registrar:pack_ci: received "
-                                                       "too long\n");
-                                               goto error;
+               if (ci.received.len==0) {
+                       if (_c->received) {
+                               ci.received = _c->received->body;
+                       } else {
+                               if (received_found==0) {
+                                       memset(&val, 0, sizeof(int_str));
+                                       if (rcv_avp_name.n!=0
+                                                               && search_first_avp(rcv_avp_type, rcv_avp_name, &val, 0)
+                                                               && val.s.len > 0) {
+                                               if (val.s.len>RECEIVED_MAX_SIZE) {
+                                                       rerrno = R_CONTACT_LEN;
+                                                       LM_ERR("received too long\n");
+                                                       goto error;
+                                               }
+                                               received = val.s;
+                                       } else {
+                                               received.s = 0;
+                                               received.len = 0;
                                        }
-                                       received = val.s;
-                               } else {
-                                       received.s = 0;
-                                       received.len = 0;
+                                       received_found = 1;
                                }
-                               received_found = 1;
+                               ci.received = received;
                        }
-                       ci.received = received;
                }
-
+               if(_c->instance!=NULL && _c->instance->body.len>0)
+                       ci.instance = _c->instance->body;
+               if(_c->reg_id!=NULL && _c->reg_id->body.len>0) {
+                       if(str2int(&_c->reg_id->body, &ci.reg_id)<0)
+                       {
+                               LM_ERR("invalid reg-id value\n");
+                               goto error;
+                       }
+               }
+               if(sruid_next(&_reg_sruid)<0)
+                       goto error;
+               ci.ruid = _reg_sruid.uid;
        }
 
        return &ci;
@@ -368,58 +377,89 @@ error:
 }
 
 
+int reg_get_crt_max_contacts(void)
+{
+       int n;
+       sr_xavp_t *ravp=NULL;
+       sr_xavp_t *vavp=NULL;
+       str vname = {"max_contacts", 12};
+
+       n = 0;
+
+       if(reg_xavp_cfg.s!=NULL)
+       {
+               ravp = xavp_get(&reg_xavp_cfg, NULL);
+               if(ravp!=NULL && ravp->val.type==SR_XTYPE_XAVP)
+               {
+                       vavp = xavp_get(&vname, ravp->val.v.xavp);
+                       if(vavp!=NULL && vavp->val.type==SR_XTYPE_INT)
+                       {
+                               n = vavp->val.v.i;
+                               LM_ERR("using max contacts value from xavp: %d\n", n);
+                       } else {
+                               ravp = NULL;
+                       }
+               } else {
+                       ravp = NULL;
+               }
+       }
+
+       if(ravp==NULL)
+       {
+               n = cfg_get(registrar, registrar_cfg, max_contacts);
+       }
+
+       return n;
+}
 
-/*
+/*! \brief
  * Message contained some contacts, but record with same address
  * of record was not found so we have to create a new record
  * and insert all contacts from the message that have expires
  * > 0
  */
-static inline int insert_contacts(struct sip_msg* _m, contact_t* _c,
-                                                                                                       udomain_t* _d, str* _a)
+static inline int insert_contacts(struct sip_msg* _m, udomain_t* _d, str* _a)
 {
        ucontact_info_t* ci;
-       urecord_t* r;
+       urecord_t* r = NULL;
        ucontact_t* c;
+       contact_t* _c;
        unsigned int flags;
-       int num;
-       int e;
+       int num, expires;
+       int maxc;
 #ifdef USE_TCP
-       int e_max;
-       int tcp_check;
+       int e_max, tcp_check;
        struct sip_uri uri;
 #endif
+       sip_uri_t *u;
 
-       /* is nated flag */
-       if (_m->flags&nat_flag)
-               flags = FL_NAT;
-       else
-               flags = FL_NONE;
-       /* nat type flag */
-       if (_m->flags&sip_natping_flag)
-               flags |= FL_NAT_SIPPING;
+       u = parse_to_uri(_m);
+       if(u==NULL)
+               goto error;
 
-       flags |= mem_only;
+       flags = mem_only;
 #ifdef USE_TCP
        if ( (_m->flags&tcp_persistent_flag) &&
-       (_m->rcv.proto==PROTO_TCP||_m->rcv.proto==PROTO_TLS)) {
+       (_m->rcv.proto==PROTO_TCP||_m->rcv.proto==PROTO_TLS||_m->rcv.proto==PROTO_WS||_m->rcv.proto==PROTO_WSS)) {
                e_max = 0;
                tcp_check = 1;
        } else {
                e_max = tcp_check = 0;
        }
 #endif
-
+       _c = get_first_contact(_m);
+       maxc = reg_get_crt_max_contacts();
        for( num=0,r=0,ci=0 ; _c ; _c = get_next_contact(_c) ) {
                /* calculate expires */
-               calc_contact_expires(_m, _c->expires, &e);
+               calc_contact_expires(_m, _c->expires, &expires);
                /* Skip contacts with zero expires */
-               if (e == 0)
+               if (expires == 0)
                        continue;
 
-               if (max_contacts && (num >= max_contacts)) {
-                       LOG(L_INFO,"INFO:registrar:insert_contacts: too many contacts "
-                               "(%d) for AOR <%.*s>\n", num, _a->len, _a->s);
+
+               if (maxc > 0 && num >= maxc) {
+                       LM_INFO("too many contacts (%d) for AOR <%.*s>\n", 
+                                       num, _a->len, _a->s);
                        rerrno = R_TOO_MANY;
                        goto error;
                }
@@ -428,32 +468,34 @@ static inline int insert_contacts(struct sip_msg* _m, contact_t* _c,
                if (r==0) {
                        if (ul.insert_urecord(_d, _a, &r) < 0) {
                                rerrno = R_UL_NEW_R;
-                               LOG(L_ERR, "ERROR:registrar:insert_contacts: failed to insert "
-                                       "new record structure\n");
+                               LM_ERR("failed to insert new record structure\n");
                                goto error;
                        }
                }
 
                /* pack the contact_info */
-               if ( (ci=pack_ci( (ci==0)?_m:0, _c, e, flags, 0))==0 ) {
-                       LOG(L_ERR, "ERROR:registrar:insert_contacts: failed to extract "
-                               "contact info\n");
+               if ( (ci=pack_ci( (ci==0)?_m:0, _c, expires, flags))==0 ) {
+                       LM_ERR("failed to extract contact info\n");
                        goto error;
                }
 
-               if ( r->contacts==0 ||
-               ul.get_ucontact(r, &_c->uri, ci->callid, ci->cseq+1, &c)!=0 ) {
+               /* hack to work with buggy clients having many contacts with same
+                * address in one REGISTER - increase CSeq to detect if there was
+                * one alredy added, then update */
+               ci->cseq++;
+               if ( r->contacts==0
+                               || ul.get_ucontact_by_instance(r, &_c->uri, ci, &c) != 0) {
+                       ci->cseq--;
                        if (ul.insert_ucontact( r, &_c->uri, ci, &c) < 0) {
                                rerrno = R_UL_INS_C;
-                               LOG(L_ERR, "ERROR:registrar:insert_contacts: failed to insert "
-                                       "contact\n");
+                               LM_ERR("failed to insert contact\n");
                                goto error;
                        }
                } else {
+                       ci->cseq--;
                        if (ul.update_ucontact( r, c, ci) < 0) {
                                rerrno = R_UL_UPD_C;
-                               LOG(L_ERR, "ERROR:registrar:insert_contacts: failed to update "
-                                       "contact\n");
+                               LM_ERR("failed to update contact\n");
                                goto error;
                        }
                }
@@ -461,15 +503,14 @@ static inline int insert_contacts(struct sip_msg* _m, contact_t* _c,
                if (tcp_check) {
                        /* parse contact uri to see if transport is TCP */
                        if (parse_uri( _c->uri.s, _c->uri.len, &uri)<0) {
-                               LOG(L_ERR,"ERROR:registrar:insert_contacts failed to parse "
-                                       "contact <%.*s>\n", _c->uri.len, _c->uri.s);
-                       } else if (uri.proto==PROTO_TCP || uri.proto==PROTO_TLS) {
+                               LM_ERR("failed to parse contact <%.*s>\n", 
+                                               _c->uri.len, _c->uri.s);
+                       } else if (uri.proto==PROTO_TCP || uri.proto==PROTO_TLS || uri.proto==PROTO_WS || uri.proto==PROTO_WSS) {
                                if (e_max) {
-                                       LOG(L_WARN,"WARNING:registrar:insert_contacts: multiple "
-                                               "TCP contacts on single REGISTER\n");
-                                       if (e>e_max) e_max = e;
+                                       LM_WARN("multiple TCP contacts on single REGISTER\n");
+                                       if (expires>e_max) e_max = expires;
                                } else {
-                                       e_max = e;
+                                       e_max = expires;
                                }
                        }
                }
@@ -478,14 +519,17 @@ static inline int insert_contacts(struct sip_msg* _m, contact_t* _c,
 
        if (r) {
                if (r->contacts)
-                       build_contact(r->contacts);
+                       build_contact(_m, r->contacts, &u->host);
                ul.release_urecord(r);
+       } else { /* No contacts found */
+               build_contact(_m, NULL, &u->host);
        }
 
 #ifdef USE_TCP
        if ( tcp_check && e_max>0 ) {
                e_max -= act_time;
-               force_tcp_conn_lifetime( &_m->rcv , e_max + 10 );
+               /*FIXME: Do we want this in the sr core?*/
+               /*force_tcp_conn_lifetime( &_m->rcv , e_max + 10 );*/
        }
 #endif
 
@@ -498,7 +542,7 @@ error:
 
 
 static int test_max_contacts(struct sip_msg* _m, urecord_t* _r, contact_t* _c,
-                                                                                                               ucontact_info_t *ci)
+                                                                               ucontact_info_t *ci, int mc)
 {
        int num;
        int e;
@@ -513,16 +557,15 @@ static int test_max_contacts(struct sip_msg* _m, urecord_t* _r, contact_t* _c,
                }
                ptr = ptr->next;
        }
-       DBG("DEBUG:registrar:test_max_contacts: %d valid contacts\n", num);
+       LM_DBG("%d valid contacts\n", num);
        
        for( ; _c ; _c = get_next_contact(_c) ) {
                /* calculate expires */
                calc_contact_expires(_m, _c->expires, &e);
-               
-               ret = ul.get_ucontact( _r, &_c->uri, ci->callid, ci->cseq, &cont);
+
+               ret = ul.get_ucontact_by_instance( _r, &_c->uri, ci, &cont);
                if (ret==-1) {
-                       LOG(L_ERR,"ERROR:registrar:update_contacts: invalid cseq for aor "
-                               "<%.*s>\n",_r->aor.len,_r->aor.s);
+                       LM_ERR("invalid cseq for aor <%.*s>\n",_r->aor.len,_r->aor.s);
                        rerrno = R_INV_CSEQ;
                        return -1;
                } else if (ret==-2) {
@@ -536,10 +579,9 @@ static int test_max_contacts(struct sip_msg* _m, urecord_t* _r, contact_t* _c,
                }
        }
        
-       DBG("DEBUG:registrar:test_max_contacts: %d contacts after commit\n", num);
-       if (num > max_contacts) {
-               LOG(L_INFO,"INFO:registrar:test_max_contacts: too many contacts "
-                               "for AOR <%.*s>\n", _r->aor.len, _r->aor.s);
+       LM_DBG("%d contacts after commit\n", num);
+       if (num > mc) {
+               LM_INFO("too many contacts for AOR <%.*s>\n", _r->aor.len, _r->aor.s);
                rerrno = R_TOO_MANY;
                return -1;
        }
@@ -548,7 +590,7 @@ static int test_max_contacts(struct sip_msg* _m, urecord_t* _r, contact_t* _c,
 }
 
 
-/*
+/*! \brief
  * Message contained some contacts and appropriate
  * record was found, so we have to walk through
  * all contacts and do the following:
@@ -560,42 +602,42 @@ static int test_max_contacts(struct sip_msg* _m, urecord_t* _r, contact_t* _c,
  *    == 0, delete contact
  */
 static inline int update_contacts(struct sip_msg* _m, urecord_t* _r,
-                                                                                                                               contact_t* _c)
+                                                                               int _mode)
 {
        ucontact_info_t *ci;
-       ucontact_t* c;
-       int e;
-       int set, reset;
+       ucontact_t *c, *ptr, *ptr0;
+       int expires, ret, updated;
        unsigned int flags;
-       int ret;
 #ifdef USE_TCP
-       int e_max;
-       int tcp_check;
+       int e_max, tcp_check;
        struct sip_uri uri;
 #endif
+       int rc;
+       contact_t* _c;
+       int maxc;
 
-       /* is nated flag */
-       if (_m->flags&nat_flag)
-               flags = FL_NAT;
-       else
-               flags = FL_NONE;
-       /* nat type flag */
-       if (_m->flags&sip_natping_flag)
-               flags |= FL_NAT_SIPPING;
+       /* mem flag */
+       flags = mem_only;
 
+       rc = 0;
        /* pack the contact_info */
-       if ( (ci=pack_ci( _m, 0, 0, 0, 0))==0 ) {
-               LOG(L_ERR, "ERROR:registrar:update_contacts: failed to "
-                       "initial pack contact info\n");
+       if ( (ci=pack_ci( _m, 0, 0, flags))==0 ) {
+               LM_ERR("failed to initial pack contact info\n");
                goto error;
        }
 
-       if (max_contacts && test_max_contacts(_m, _r, _c, ci) != 0 )
-               goto error;
+       if (!_mode) {
+               maxc = reg_get_crt_max_contacts();
+               if(maxc>0) {
+                       _c = get_first_contact(_m);
+                       if(test_max_contacts(_m, _r, _c, ci, maxc) != 0)
+                               goto error;
+               }
+       }
 
 #ifdef USE_TCP
        if ( (_m->flags&tcp_persistent_flag) &&
-       (_m->rcv.proto==PROTO_TCP||_m->rcv.proto==PROTO_TLS)) {
+       (_m->rcv.proto==PROTO_TCP||_m->rcv.proto==PROTO_TLS||_m->rcv.proto==PROTO_WS||_m->rcv.proto==PROTO_WSS)) {
                e_max = -1;
                tcp_check = 1;
        } else {
@@ -603,42 +645,56 @@ static inline int update_contacts(struct sip_msg* _m, urecord_t* _r,
        }
 #endif
 
+       _c = get_first_contact(_m);
+       updated=0;
        for( ; _c ; _c = get_next_contact(_c) ) {
                /* calculate expires */
-               calc_contact_expires(_m, _c->expires, &e);
+               calc_contact_expires(_m, _c->expires, &expires);
 
                /* search for the contact*/
-               ret = ul.get_ucontact( _r, &_c->uri, ci->callid, ci->cseq, &c);
+               ret = ul.get_ucontact_by_instance( _r, &_c->uri, ci, &c);
                if (ret==-1) {
-                       LOG(L_ERR,"ERROR:registrar:update_contacts: invalid cseq for aor "
-                               "<%.*s>\n",_r->aor.len,_r->aor.s);
+                       LM_ERR("invalid cseq for aor <%.*s>\n",_r->aor.len,_r->aor.s);
                        rerrno = R_INV_CSEQ;
                        goto error;
                } else if (ret==-2) {
+                       if(expires!=0 && _mode)
+                               break;
                        continue;
                }
 
                if ( ret > 0 ) {
                        /* Contact not found -> expired? */
-                       if (e==0)
+                       if (expires==0)
                                continue;
 
                        /* pack the contact_info */
-                       if ( (ci=pack_ci( 0, _c, e, flags, 0))==0 ) {
-                               LOG(L_ERR, "ERROR:registrar:update_contacts: failed to "
-                                       "extract contact info\n");
+                       if ( (ci=pack_ci( 0, _c, expires, 0))==0 ) {
+                               LM_ERR("failed to extract contact info\n");
                                goto error;
                        }
 
                        if (ul.insert_ucontact( _r, &_c->uri, ci, &c) < 0) {
                                rerrno = R_UL_INS_C;
-                               LOG(L_ERR, "ERROR:registrar:update_contacts: failed to insert "
-                                       "contact\n");
+                               LM_ERR("failed to insert contact\n");
                                goto error;
                        }
+                       rc = 1;
+                       if(_mode)
+                       {
+                               ptr=_r->contacts;
+                               while(ptr)
+                               {
+                                       ptr0 = ptr;
+                                       if(ptr!=c)
+                                               ul.delete_ucontact(_r, ptr);
+                                       ptr=ptr0->next;
+                               }
+                               updated=1;
+                       }
                } else {
                        /* Contact found */
-                       if (e == 0) {
+                       if (expires == 0) {
                                /* it's expired */
                                if (mem_only) {
                                        c->flags |= FL_MEM;
@@ -648,117 +704,136 @@ static inline int update_contacts(struct sip_msg* _m, urecord_t* _r,
 
                                if (ul.delete_ucontact(_r, c) < 0) {
                                        rerrno = R_UL_DEL_C;
-                                       LOG(L_ERR, "ERROR:registrar:update_contacts: failed "
-                                               "to delete contact\n");
+                                       LM_ERR("failed to delete contact\n");
                                        goto error;
                                }
+                               rc = 3;
                        } else {
                                /* do update */
-                               set = flags | mem_only;
-                               reset = ~(flags | mem_only) & (FL_NAT|FL_MEM|FL_NAT_SIPPING);
-
                                /* pack the contact specific info */
-                               if ( (ci=pack_ci( 0, _c, e, set, reset))==0 ) {
-                                       LOG(L_ERR, "ERROR:registrar:update_contacts: failed to "
-                                               "pack contact specific info\n");
+                               if ( (ci=pack_ci( 0, _c, expires, 0))==0 ) {
+                                       LM_ERR("failed to pack contact specific info\n");
                                        goto error;
                                }
 
+                               if(_mode)
+                               {
+                                       ptr=_r->contacts;
+                                       while(ptr)
+                                       {
+                                               ptr0 = ptr;
+                                               if(ptr!=c)
+                                                       ul.delete_ucontact(_r, ptr);
+                                               ptr=ptr0->next;
+                                       }
+                                       updated=1;
+                               }
                                if (ul.update_ucontact(_r, c, ci) < 0) {
                                        rerrno = R_UL_UPD_C;
-                                       LOG(L_ERR, "ERROR:registrar:update_contacts: failed to "
-                                               "update contact\n");
+                                       LM_ERR("failed to update contact\n");
                                        goto error;
                                }
-
-                               if (desc_time_order) {
-                                       move_on_top(_r, c);
-                               }
+                               rc = 2;
                        }
                }
 #ifdef USE_TCP
                if (tcp_check) {
                        /* parse contact uri to see if transport is TCP */
                        if (parse_uri( _c->uri.s, _c->uri.len, &uri)<0) {
-                               LOG(L_ERR,"ERROR:registrar:update_contacts failed to parse "
-                                       "contact <%.*s>\n", _c->uri.len, _c->uri.s);
-                       } else if (uri.proto==PROTO_TCP || uri.proto==PROTO_TLS) {
+                               LM_ERR("failed to parse contact <%.*s>\n", 
+                                               _c->uri.len, _c->uri.s);
+                       } else if (uri.proto==PROTO_TCP || uri.proto==PROTO_TLS || uri.proto==PROTO_WS || uri.proto==PROTO_WSS) {
                                if (e_max>0) {
-                                       LOG(L_WARN,"WARNING:registrar:update_contacts: multiple "
-                                               "TCP contacts on single REGISTER\n");
+                                       LM_WARN("multiple TCP contacts on single REGISTER\n");
                                }
-                               if (e>e_max) e_max = e;
+                               if (expires>e_max) e_max = expires;
                        }
                }
 #endif
-
-               if (e>e_max)
-                       e_max = e;
+               /* have one contact only -- break */
+               if(updated)
+                       break;
        }
 
 #ifdef USE_TCP
        if ( tcp_check && e_max>-1 ) {
                if (e_max) e_max -= act_time;
-               force_tcp_conn_lifetime( &_m->rcv , e_max + 10 );
+               /*FIXME: Do we want this in the sr core? */
+               /*force_tcp_conn_lifetime( &_m->rcv , e_max + 10 );*/
        }
 #endif
 
-       return 0;
+       return rc;
 error:
        return -1;
 }
 
 
-/* 
+/*! \brief
  * This function will process request that
  * contained some contact header fields
  */
-static inline int add_contacts(struct sip_msg* _m, contact_t* _c,
-                                                                                                       udomain_t* _d, str* _a)
+static inline int add_contacts(struct sip_msg* _m, udomain_t* _d,
+               str* _a, int _mode)
 {
        int res;
+       int ret;
        urecord_t* r;
+       sip_uri_t *u;
 
-       ul.lock_udomain(_d);
+       u = parse_to_uri(_m);
+       if(u==NULL)
+               return -2;
+
+       ret = 0;
+       ul.lock_udomain(_d, _a);
        res = ul.get_urecord(_d, _a, &r);
        if (res < 0) {
                rerrno = R_UL_GET_R;
-               LOG(L_ERR, "ERROR:registrar:add_contacts: failed to retrieve record "
-                       "from usrloc\n");
-               ul.unlock_udomain(_d);
+               LM_ERR("failed to retrieve record from usrloc\n");
+               ul.unlock_udomain(_d, _a);
                return -2;
        }
 
        if (res == 0) { /* Contacts found */
-               if (update_contacts(_m, r, _c) < 0) {
-                       build_contact(r->contacts);
+               if ((ret=update_contacts(_m, r, _mode)) < 0) {
+                       build_contact(_m, r->contacts, &u->host);
                        ul.release_urecord(r);
-                       ul.unlock_udomain(_d);
+                       ul.unlock_udomain(_d, _a);
                        return -3;
                }
-               build_contact(r->contacts);
+               build_contact(_m, r->contacts, &u->host);
                ul.release_urecord(r);
        } else {
-               if (insert_contacts(_m, _c, _d, _a) < 0) {
-                       ul.unlock_udomain(_d);
+               if (insert_contacts(_m, _d, _a) < 0) {
+                       ul.unlock_udomain(_d, _a);
                        return -4;
                }
+               ret = 1;
        }
-       ul.unlock_udomain(_d);
-       return 0;
+       ul.unlock_udomain(_d, _a);
+       return ret;
 }
 
 
-/*
+/*!\brief
  * Process REGISTER request and save it's contacts
  */
-static inline int save_real(struct sip_msg* _m, udomain_t* _t, int doreply)
+#define is_cflag_set(_name) (((unsigned int)_cflags)&(_name))
+int save(struct sip_msg* _m, udomain_t* _d, int _cflags, str *_uri)
 {
        contact_t* c;
-       int st;
+       int st, mode;
        str aor;
+       int ret;
+       sip_uri_t *u;
+
+       u = parse_to_uri(_m);
+       if(u==NULL)
+               goto error;
 
        rerrno = R_FINE;
+       ret = 1;
 
        if (parse_message(_m) < 0) {
                goto error;
@@ -771,56 +846,63 @@ static inline int save_real(struct sip_msg* _m, udomain_t* _t, int doreply)
        get_act_time();
        c = get_first_contact(_m);
 
-       if (extract_aor(&get_to(_m)->uri, &aor) < 0) {
-               LOG(L_ERR, "ERROR:registrar:save_real: failed to extract "
-                       "Address Of Record\n");
+       if (extract_aor((_uri)?_uri:&get_to(_m)->uri, &aor, NULL) < 0) {
+               LM_ERR("failed to extract Address Of Record\n");
                goto error;
        }
 
+       mem_only = is_cflag_set(REG_SAVE_MEM_FL)?FL_MEM:FL_NONE;
+
        if (c == 0) {
                if (st) {
-                       if (star(_t, &aor) < 0) goto error;
+                       if (star(_m, (udomain_t*)_d, &aor, &u->host) < 0) goto error;
+                       else ret=3;
                } else {
-                       if (no_contacts(_t, &aor) < 0) goto error;
+                       if (no_contacts(_m, (udomain_t*)_d, &aor, &u->host) < 0) goto error;
+                       else ret=4;
                }
        } else {
-               if (add_contacts(_m, c, _t, &aor) < 0) goto error;
+               mode = is_cflag_set(REG_SAVE_REPL_FL)?1:0;
+               if ((ret=add_contacts(_m, (udomain_t*)_d, &aor, mode)) < 0)
+                       goto error;
+               ret = (ret==0)?1:ret;
        }
 
-       if (doreply && (send_reply(_m) < 0)) return -1;
-       else return 1;
+       update_stat(accepted_registrations, 1);
 
+       /* Only send reply upon request, not upon reply */
+       if ((is_route_type(REQUEST_ROUTE)) && !is_cflag_set(REG_SAVE_NORPL_FL) && (reg_send_reply(_m) < 0))
+               return -1;
+
+       return ret;
 error:
-       if (doreply) send_reply(_m);
+       update_stat(rejected_registrations, 1);
+       if (is_route_type(REQUEST_ROUTE) && !is_cflag_set(REG_SAVE_NORPL_FL) )
+               reg_send_reply(_m);
+
        return 0;
 }
 
-
-/*
- * Process REGISTER request and save it's contacts
- */
-int save(struct sip_msg* _m, char* _t, char* _s)
+int unregister(struct sip_msg* _m, udomain_t* _d, str* _uri)
 {
-       mem_only = FL_NONE;
-       return save_real(_m, (udomain_t*)_t, 1);
-}
+       str aor = {0, 0};
+       sip_uri_t *u;
 
+       u = parse_to_uri(_m);
+       if(u==NULL)
+               return -2;
 
-/*
- * Process REGISTER request and save it's contacts, do not send any replies
- */
-int save_noreply(struct sip_msg* _m, char* _t, char* _s)
-{
-       mem_only = FL_NONE;
-       return save_real(_m, (udomain_t*)_t, 0);
-}
 
+       if (extract_aor(_uri, &aor, NULL) < 0) {
+               LM_ERR("failed to extract Address Of Record\n");
+               return -1;
+       }
 
-/*
- * Update memory cache only
- */
-int save_memory(struct sip_msg* _m, char* _t, char* _s)
-{
-       mem_only = FL_MEM;
-       return save_real(_m, (udomain_t*)_t, 1);
+       if (star(_m, _d, &aor, &u->host) < 0)
+       {
+               LM_ERR("error unregistering user [%.*s]\n", aor.len, aor.s);
+               return -1;
+       }
+       return 1;
 }
+