Ver código fonte

modules/registrar_scscf: first commit of registrar_scscf
- Registrar functionality for S-CSCF servers

Jason Penton 12 anos atrás
pai
commit
200082fd14
45 arquivos alterados com 10651 adições e 0 exclusões
  1. 293 0
      modules/registrar_scscf/CxDataType_Rel6.xsd
  2. 301 0
      modules/registrar_scscf/CxDataType_Rel7.xsd
  3. 20 0
      modules/registrar_scscf/Makefile
  4. 95 0
      modules/registrar_scscf/api.c
  5. 73 0
      modules/registrar_scscf/api.h
  6. 44 0
      modules/registrar_scscf/blurb
  7. 126 0
      modules/registrar_scscf/common.c
  8. 44 0
      modules/registrar_scscf/common.h
  9. 78 0
      modules/registrar_scscf/config.c
  10. 53 0
      modules/registrar_scscf/config.h
  11. 1047 0
      modules/registrar_scscf/cxdx_avp.c
  12. 286 0
      modules/registrar_scscf/cxdx_avp.h
  13. 37 0
      modules/registrar_scscf/cxdx_callbacks.c
  14. 31 0
      modules/registrar_scscf/cxdx_callbacks.h
  15. 344 0
      modules/registrar_scscf/cxdx_sar.c
  16. 94 0
      modules/registrar_scscf/cxdx_sar.h
  17. 248 0
      modules/registrar_scscf/lookup.c
  18. 54 0
      modules/registrar_scscf/lookup.h
  19. 115 0
      modules/registrar_scscf/path.c
  20. 43 0
      modules/registrar_scscf/path.h
  21. 689 0
      modules/registrar_scscf/reg_mod.c
  22. 117 0
      modules/registrar_scscf/reg_mod.h
  23. 1305 0
      modules/registrar_scscf/registrar_notify.c
  24. 142 0
      modules/registrar_scscf/registrar_notify.h
  25. 541 0
      modules/registrar_scscf/regpv.c
  26. 49 0
      modules/registrar_scscf/regpv.h
  27. 39 0
      modules/registrar_scscf/regtime.c
  28. 48 0
      modules/registrar_scscf/regtime.h
  29. 770 0
      modules/registrar_scscf/reply.c
  30. 84 0
      modules/registrar_scscf/reply.h
  31. 35 0
      modules/registrar_scscf/rerrno.c
  32. 71 0
      modules/registrar_scscf/rerrno.h
  33. 1270 0
      modules/registrar_scscf/save.c
  34. 70 0
      modules/registrar_scscf/save.h
  35. 66 0
      modules/registrar_scscf/screensharing.log
  36. 92 0
      modules/registrar_scscf/server_assignment.c
  37. 63 0
      modules/registrar_scscf/server_assignment.h
  38. 284 0
      modules/registrar_scscf/sip_msg.c
  39. 77 0
      modules/registrar_scscf/sip_msg.h
  40. 68 0
      modules/registrar_scscf/stats.c
  41. 56 0
      modules/registrar_scscf/stats.h
  42. 1059 0
      modules/registrar_scscf/userdata_parser.c
  43. 66 0
      modules/registrar_scscf/userdata_parser.h
  44. 109 0
      modules/registrar_scscf/usrloc_cb.c
  45. 55 0
      modules/registrar_scscf/usrloc_cb.h

+ 293 - 0
modules/registrar_scscf/CxDataType_Rel6.xsd

@@ -0,0 +1,293 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified">
+	<xs:simpleType name="tPriority" final="list restriction">
+		<xs:restriction base="xs:int">
+			<xs:minInclusive value="0"/>
+		</xs:restriction>
+	</xs:simpleType>
+	<xs:simpleType name="tProfilePartIndicator" final="list restriction">
+		<xs:restriction base="xs:unsignedByte">
+			<xs:maxInclusive value="1"/>
+			<xs:enumeration value="0">
+				<xs:annotation>
+					<xs:documentation>
+						<label xml:lang="en">REGISTERED</label>
+						<definition xml:lang="en">iFC is part of the registered profile</definition>
+					</xs:documentation>
+				</xs:annotation>
+			</xs:enumeration>
+			<xs:enumeration value="1">
+				<xs:annotation>
+					<xs:documentation>
+						<label xml:lang="en">UNREGISTERED</label>
+						<definition xml:lang="en">iFC is part of the unregistered profile</definition>
+					</xs:documentation>
+				</xs:annotation>
+			</xs:enumeration>
+		</xs:restriction>
+	</xs:simpleType>
+	<xs:simpleType name="tSharedIFCSetID" final="list restriction">
+		<xs:restriction base="xs:int">
+			<xs:minInclusive value="0"/>
+		</xs:restriction>
+	</xs:simpleType>
+	<xs:simpleType name="tGroupID" final="list restriction">
+		<xs:restriction base="xs:int">
+			<xs:minInclusive value="0"/>
+		</xs:restriction>
+	</xs:simpleType>
+	<xs:simpleType name="tRegistrationType" final="list restriction">
+		<xs:restriction base="xs:unsignedByte">
+			<xs:maxInclusive value="2"/>
+			<xs:enumeration value="0">
+				<xs:annotation>
+					<xs:documentation>
+						<label xml:lang="en">INITIAL_REGISTRATION</label>
+						<definition xml:lang="en">Matches to REGISTER messages that are related to initial registration</definition>
+					</xs:documentation>
+				</xs:annotation>
+			</xs:enumeration>
+			<xs:enumeration value="1">
+				<xs:annotation>
+					<xs:documentation>
+						<label xml:lang="en">RE-REGISTRATION</label>
+						<definition xml:lang="en">Matches to REGISTER messages that are related to re-registration</definition>
+					</xs:documentation>
+				</xs:annotation>
+			</xs:enumeration>
+			<xs:enumeration value="2">
+				<xs:annotation>
+					<xs:documentation>
+						<label xml:lang="en">DE-REGISTRATION</label>
+						<definition xml:lang="en">Matches to REGISTER messages that are related to de-registration</definition>
+					</xs:documentation>
+				</xs:annotation>
+			</xs:enumeration>
+		</xs:restriction>
+	</xs:simpleType>
+	<xs:simpleType name="tDefaultHandling" final="list restriction">
+		<xs:restriction base="xs:unsignedByte">
+			<xs:maxInclusive value="1"/>
+			<xs:enumeration value="0">
+				<xs:annotation>
+					<xs:documentation>
+						<label xml:lang="en">SESSION_CONTINUED</label>
+						<definition xml:lang="en">Session Continued</definition>
+					</xs:documentation>
+				</xs:annotation>
+			</xs:enumeration>
+			<xs:enumeration value="1">
+				<xs:annotation>
+					<xs:documentation>
+						<label xml:lang="en">SESSION_TERMINATED</label>
+						<definition xml:lang="en">Session Terminated</definition>
+					</xs:documentation>
+				</xs:annotation>
+			</xs:enumeration>
+		</xs:restriction>
+	</xs:simpleType>
+	<xs:simpleType name="tDirectionOfRequest" final="list restriction">
+		<xs:restriction base="xs:unsignedByte">
+			<xs:maxInclusive value="3"/>
+			<xs:enumeration value="0">
+				<xs:annotation>
+					<xs:documentation>
+						<label xml:lang="en">ORIGINATING_SESSION</label>
+						<definition xml:lang="en">Originating Session</definition>
+					</xs:documentation>
+				</xs:annotation>
+			</xs:enumeration>
+			<xs:enumeration value="1">
+				<xs:annotation>
+					<xs:documentation>
+						<label xml:lang="en">TERMINATING_REGISTERED</label>
+						<definition xml:lang="en">Terminating Session for registered user</definition>
+					</xs:documentation>
+				</xs:annotation>
+			</xs:enumeration>
+			<xs:enumeration value="2">
+				<xs:annotation>
+					<xs:documentation>
+						<label xml:lang="en">TERMINATING_UNREGISTERED</label>
+						<definition xml:lang="en">Terminating Session for unregistered user</definition>
+					</xs:documentation>
+				</xs:annotation>
+			</xs:enumeration>
+		</xs:restriction>
+	</xs:simpleType>
+	<xs:simpleType name="tPrivateID" final="list restriction">
+		<xs:restriction base="xs:anyURI"/>
+	</xs:simpleType>
+	<xs:simpleType name="tSIP_URL" final="list restriction">
+		<xs:restriction base="xs:anyURI"/>
+	</xs:simpleType>
+	<xs:simpleType name="tTEL_URL" final="list restriction">
+		<xs:restriction base="xs:anyURI"/>
+	</xs:simpleType>
+	<xs:simpleType name="tIdentity" final="list restriction">
+		<xs:union memberTypes="tSIP_URL tTEL_URL"/>
+	</xs:simpleType>
+	<xs:simpleType name="tIdentityType" final="list restriction">
+		<xs:restriction base="xs:unsignedByte">
+			<xs:minInclusive value="0"/>
+			<xs:maxInclusive value="2"/>
+			<xs:enumeration value="0">
+				<xs:annotation>
+					<xs:documentation>
+						<label xml:lang="en">PUBLIC_USER_IDENTITY</label>
+						<definition xml:lang="en">Identity is a Public User Identity.</definition>
+					</xs:documentation>
+				</xs:annotation>
+			</xs:enumeration>
+			<xs:enumeration value="1">
+				<xs:annotation>
+					<xs:documentation>
+						<label xml:lang="en">DISTINCT_PSI</label>
+						<definition xml:lang="en">Identity is a distinct Public Service Identity.</definition>
+					</xs:documentation>
+				</xs:annotation>
+			</xs:enumeration>
+			<xs:enumeration value="2">
+				<xs:annotation>
+					<xs:documentation>
+						<label xml:lang="en">WILDCARDED_PSI</label>
+						<definition xml:lang="en">Identity matches a wildcarded Public Service Identity.</definition>
+					</xs:documentation>
+				</xs:annotation>
+			</xs:enumeration>
+		</xs:restriction>
+	</xs:simpleType>
+	<xs:complexType name="tPublicIdentityExtension">
+		<xs:sequence>
+			<xs:element name="IdentityType" type="tIdentityType" minOccurs="0"/>
+			<xs:element name="WildcardedPSI" type="xs:anyURI" minOccurs="0"/>
+			<xs:element name="Extension" type="tExtension" minOccurs="0"/>
+		</xs:sequence>
+	</xs:complexType>
+	<xs:simpleType name="tServiceInfo" final="list restriction">
+		<xs:restriction base="xs:string">
+			<xs:minLength value="0"/>
+		</xs:restriction>
+	</xs:simpleType>
+	<xs:simpleType name="tString" final="list restriction">
+		<xs:restriction base="xs:string">
+			<xs:minLength value="0"/>
+		</xs:restriction>
+	</xs:simpleType>
+	<xs:simpleType name="tBool">
+		<xs:restriction base="xs:boolean"/>
+	</xs:simpleType>
+	<xs:simpleType name="tSubscribedMediaProfileId" final="list restriction">
+		<xs:restriction base="xs:int">
+			<xs:minInclusive value="0"/>
+		</xs:restriction>
+	</xs:simpleType>
+	<xs:complexType name="tExtension">
+		<xs:sequence>
+			<xs:any processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
+		</xs:sequence>
+	</xs:complexType>
+	<xs:complexType name="tServiceProfileExtension">
+		<xs:sequence>
+			<xs:element name="SharedIFCSetID" type="tSharedIFCSetID" minOccurs="0" maxOccurs="unbounded"/>
+			<xs:element name="Extension" type="tExtension" minOccurs="0"/>
+		</xs:sequence>
+	</xs:complexType>
+	<xs:complexType name="tSePoTriExtension">
+		<xs:sequence>
+			<xs:element name="RegistrationType" type="tRegistrationType" minOccurs="0" maxOccurs="2"/>
+			<xs:element name="Extension" type="tExtension" minOccurs="0"/>
+		</xs:sequence>
+	</xs:complexType>
+	<xs:complexType name="tIMSSubscription">
+		<xs:sequence>
+			<xs:element name="PrivateID" type="tPrivateID"/>
+			<xs:element name="ServiceProfile" type="tServiceProfile" maxOccurs="unbounded"/>
+			<xs:element name="Extension" type="tExtension" minOccurs="0"/>
+			<xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
+		</xs:sequence>
+	</xs:complexType>
+	<xs:complexType name="tServiceProfile">
+		<xs:sequence>
+			<xs:element name="PublicIdentity" type="tPublicIdentity" maxOccurs="unbounded"/>
+			<xs:element name="CoreNetworkServicesAuthorization" type="tCoreNetworkServicesAuthorization" minOccurs="0" />
+			<xs:element name="InitialFilterCriteria" type="tInitialFilterCriteria" minOccurs="0" maxOccurs="unbounded"/>
+			<xs:element name="Extension" type="tServiceProfileExtension" minOccurs="0"/>
+			<xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
+		</xs:sequence>
+	</xs:complexType>
+	<xs:complexType name="tCoreNetworkServicesAuthorization">
+		<xs:sequence>
+			<xs:element name="SubscribedMediaProfileId" type="tSubscribedMediaProfileId" minOccurs="0"/>
+			<xs:element name="Extension" type="tExtension" minOccurs="0"/>
+			<xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
+		</xs:sequence>
+	</xs:complexType>
+	<xs:complexType name="tInitialFilterCriteria">
+		<xs:sequence>
+			<xs:element name="Priority" type="tPriority"/>
+			<xs:element name="TriggerPoint" type="tTrigger" minOccurs="0"/>
+			<xs:element name="ApplicationServer" type="tApplicationServer"/>
+			<xs:element name="ProfilePartIndicator" type="tProfilePartIndicator" minOccurs="0"/>
+			<xs:element name="Extension" type="tExtension" minOccurs="0"/>
+			<xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
+		</xs:sequence>
+	</xs:complexType>
+	<xs:complexType name="tTrigger">
+		<xs:sequence>
+			<xs:element name="ConditionTypeCNF" type="tBool"/>
+			<xs:element name="SPT" type="tSePoTri" maxOccurs="unbounded"/>
+			<xs:element name="Extension" type="tExtension" minOccurs="0"/>
+			<xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
+		</xs:sequence>
+	</xs:complexType>
+	<xs:complexType name="tSePoTri">
+		<xs:sequence>
+			<xs:element name="ConditionNegated" type="tBool" default="0" minOccurs="0"/>
+			<xs:element name="Group" type="tGroupID" maxOccurs="unbounded"/>
+			<xs:choice>
+				<xs:element name="RequestURI" type="tString"/>
+				<xs:element name="Method" type="tString"/>
+				<xs:element name="SIPHeader" type="tHeader"/>
+				<xs:element name="SessionCase" type="tDirectionOfRequest"/>
+				<xs:element name="SessionDescription" type="tSessionDescription"/>
+			</xs:choice>
+			<xs:element name="Extension" type="tSePoTriExtension" minOccurs="0"/>
+			<xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
+		</xs:sequence>
+	</xs:complexType>
+	<xs:complexType name="tHeader">
+		<xs:sequence>
+			<xs:element name="Header" type="tString"/>
+			<xs:element name="Content" type="tString" minOccurs="0"/>
+			<xs:element name="Extension" type="tExtension" minOccurs="0"/>
+			<xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
+		</xs:sequence>
+	</xs:complexType>
+	<xs:complexType name="tSessionDescription">
+		<xs:sequence>
+			<xs:element name="Line" type="tString"/>
+			<xs:element name="Content" type="tString" minOccurs="0"/>
+			<xs:element name="Extension" type="tExtension" minOccurs="0"/>
+			<xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
+		</xs:sequence>
+	</xs:complexType>
+	<xs:complexType name="tApplicationServer">
+		<xs:sequence>
+			<xs:element name="ServerName" type="tSIP_URL"/>
+			<xs:element name="DefaultHandling" type="tDefaultHandling" minOccurs="0"/>
+			<xs:element name="ServiceInfo" type="tServiceInfo" minOccurs="0"/>
+			<xs:element name="Extension" type="tExtension" minOccurs="0"/>
+			<xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
+		</xs:sequence>
+	</xs:complexType>
+	<xs:complexType name="tPublicIdentity">
+		<xs:sequence>
+			<xs:element name="BarringIndication" type="tBool" default="0" minOccurs="0"/>
+			<xs:element name="Identity" type="tIdentity"/>
+			<xs:element name="Extension" type="tPublicIdentityExtension" minOccurs="0"/>
+			<xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
+		</xs:sequence>
+	</xs:complexType>
+	<xs:element name="IMSSubscription" type="tIMSSubscription"/>
+</xs:schema>

+ 301 - 0
modules/registrar_scscf/CxDataType_Rel7.xsd

@@ -0,0 +1,301 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified">
+	<xs:simpleType name="tPriority" final="list restriction">
+		<xs:restriction base="xs:int">
+			<xs:minInclusive value="0"/>
+		</xs:restriction>
+	</xs:simpleType>
+	<xs:simpleType name="tProfilePartIndicator" final="list restriction">
+		<xs:restriction base="xs:unsignedByte">
+			<xs:maxInclusive value="1"/>
+			<xs:enumeration value="0">
+				<xs:annotation>
+					<xs:documentation>
+						<label xml:lang="en">REGISTERED</label>
+						<definition xml:lang="en">iFC is part of the registered profile</definition>
+					</xs:documentation>
+				</xs:annotation>
+			</xs:enumeration>
+			<xs:enumeration value="1">
+				<xs:annotation>
+					<xs:documentation>
+						<label xml:lang="en">UNREGISTERED</label>
+						<definition xml:lang="en">iFC is part of the unregistered profile</definition>
+					</xs:documentation>
+				</xs:annotation>
+			</xs:enumeration>
+		</xs:restriction>
+	</xs:simpleType>
+	<xs:simpleType name="tSharedIFCSetID" final="list restriction">
+		<xs:restriction base="xs:int">
+			<xs:minInclusive value="0"/>
+		</xs:restriction>
+	</xs:simpleType>
+	<xs:simpleType name="tGroupID" final="list restriction">
+		<xs:restriction base="xs:int">
+			<xs:minInclusive value="0"/>
+		</xs:restriction>
+	</xs:simpleType>
+	<xs:simpleType name="tRegistrationType" final="list restriction">
+		<xs:restriction base="xs:unsignedByte">
+			<xs:maxInclusive value="2"/>
+			<xs:enumeration value="0">
+				<xs:annotation>
+					<xs:documentation>
+						<label xml:lang="en">INITIAL_REGISTRATION</label>
+						<definition xml:lang="en">Matches to REGISTER messages that are related to initial registration</definition>
+					</xs:documentation>
+				</xs:annotation>
+			</xs:enumeration>
+			<xs:enumeration value="1">
+				<xs:annotation>
+					<xs:documentation>
+						<label xml:lang="en">RE-REGISTRATION</label>
+						<definition xml:lang="en">Matches to REGISTER messages that are related to re-registration</definition>
+					</xs:documentation>
+				</xs:annotation>
+			</xs:enumeration>
+			<xs:enumeration value="2">
+				<xs:annotation>
+					<xs:documentation>
+						<label xml:lang="en">DE-REGISTRATION</label>
+						<definition xml:lang="en">Matches to REGISTER messages that are related to de-registration</definition>
+					</xs:documentation>
+				</xs:annotation>
+			</xs:enumeration>
+		</xs:restriction>
+	</xs:simpleType>
+	<xs:simpleType name="tDefaultHandling" final="list restriction">
+		<xs:restriction base="xs:unsignedByte">
+			<xs:maxInclusive value="1"/>
+			<xs:enumeration value="0">
+				<xs:annotation>
+					<xs:documentation>
+						<label xml:lang="en">SESSION_CONTINUED</label>
+						<definition xml:lang="en">Session Continued</definition>
+					</xs:documentation>
+				</xs:annotation>
+			</xs:enumeration>
+			<xs:enumeration value="1">
+				<xs:annotation>
+					<xs:documentation>
+						<label xml:lang="en">SESSION_TERMINATED</label>
+						<definition xml:lang="en">Session Terminated</definition>
+					</xs:documentation>
+				</xs:annotation>
+			</xs:enumeration>
+		</xs:restriction>
+	</xs:simpleType>
+	<xs:simpleType name="tDirectionOfRequest" final="list restriction">
+		<xs:restriction base="xs:unsignedByte">
+			<xs:maxInclusive value="3"/>
+			<xs:enumeration value="0">
+				<xs:annotation>
+					<xs:documentation>
+						<label xml:lang="en">ORIGINATING_SESSION</label>
+						<definition xml:lang="en">Originating Session</definition>
+					</xs:documentation>
+				</xs:annotation>
+			</xs:enumeration>
+			<xs:enumeration value="1">
+				<xs:annotation>
+					<xs:documentation>
+						<label xml:lang="en">TERMINATING_REGISTERED</label>
+						<definition xml:lang="en">Terminating Session for registered user</definition>
+					</xs:documentation>
+				</xs:annotation>
+			</xs:enumeration>
+			<xs:enumeration value="2">
+				<xs:annotation>
+					<xs:documentation>
+						<label xml:lang="en">TERMINATING_UNREGISTERED</label>
+						<definition xml:lang="en">Terminating Session for unregistered user</definition>
+					</xs:documentation>
+				</xs:annotation>
+			</xs:enumeration>
+			<xs:enumeration value="3">
+				<xs:annotation>
+					<xs:documentation>
+						<label xml:lang="en">ORIGINATING_UNREGISTERED</label>
+						<definition xml:lang="en">Originating Session for an unregistered user</definition>
+					</xs:documentation>
+				</xs:annotation>
+			</xs:enumeration>
+		</xs:restriction>
+	</xs:simpleType>
+	<xs:simpleType name="tPrivateID" final="list restriction">
+		<xs:restriction base="xs:anyURI"/>
+	</xs:simpleType>
+	<xs:simpleType name="tSIP_URL" final="list restriction">
+		<xs:restriction base="xs:anyURI"/>
+	</xs:simpleType>
+	<xs:simpleType name="tTEL_URL" final="list restriction">
+		<xs:restriction base="xs:anyURI"/>
+	</xs:simpleType>
+	<xs:simpleType name="tIdentity" final="list restriction">
+		<xs:union memberTypes="tSIP_URL tTEL_URL"/>
+	</xs:simpleType>
+	<xs:simpleType name="tIdentityType" final="list restriction">
+		<xs:restriction base="xs:unsignedByte">
+			<xs:minInclusive value="0"/>
+			<xs:maxInclusive value="2"/>
+			<xs:enumeration value="0">
+				<xs:annotation>
+					<xs:documentation>
+						<label xml:lang="en">PUBLIC_USER_IDENTITY</label>
+						<definition xml:lang="en">Identity is a Public User Identity.</definition>
+					</xs:documentation>
+				</xs:annotation>
+			</xs:enumeration>
+			<xs:enumeration value="1">
+				<xs:annotation>
+					<xs:documentation>
+						<label xml:lang="en">DISTINCT_PSI</label>
+						<definition xml:lang="en">Identity is a distinct Public Service Identity.</definition>
+					</xs:documentation>
+				</xs:annotation>
+			</xs:enumeration>
+			<xs:enumeration value="2">
+				<xs:annotation>
+					<xs:documentation>
+						<label xml:lang="en">WILDCARDED_PSI</label>
+						<definition xml:lang="en">Identity matches a wildcarded Public Service Identity.</definition>
+					</xs:documentation>
+				</xs:annotation>
+			</xs:enumeration>
+		</xs:restriction>
+	</xs:simpleType>
+	<xs:complexType name="tPublicIdentityExtension">
+		<xs:sequence>
+			<xs:element name="IdentityType" type="tIdentityType" minOccurs="0"/>
+			<xs:element name="WildcardedPSI" type="xs:anyURI" minOccurs="0"/>
+			<xs:element name="Extension" type="tExtension" minOccurs="0"/>
+		</xs:sequence>
+	</xs:complexType>
+	<xs:simpleType name="tServiceInfo" final="list restriction">
+		<xs:restriction base="xs:string">
+			<xs:minLength value="0"/>
+		</xs:restriction>
+	</xs:simpleType>
+	<xs:simpleType name="tString" final="list restriction">
+		<xs:restriction base="xs:string">
+			<xs:minLength value="0"/>
+		</xs:restriction>
+	</xs:simpleType>
+	<xs:simpleType name="tBool">
+		<xs:restriction base="xs:boolean"/>
+	</xs:simpleType>
+	<xs:simpleType name="tSubscribedMediaProfileId" final="list restriction">
+		<xs:restriction base="xs:int">
+			<xs:minInclusive value="0"/>
+		</xs:restriction>
+	</xs:simpleType>
+	<xs:complexType name="tExtension">
+		<xs:sequence>
+			<xs:any processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
+		</xs:sequence>
+	</xs:complexType>
+	<xs:complexType name="tServiceProfileExtension">
+		<xs:sequence>
+			<xs:element name="SharedIFCSetID" type="tSharedIFCSetID" minOccurs="0" maxOccurs="unbounded"/>
+			<xs:element name="Extension" type="tExtension" minOccurs="0"/>
+		</xs:sequence>
+	</xs:complexType>
+	<xs:complexType name="tSePoTriExtension">
+		<xs:sequence>
+			<xs:element name="RegistrationType" type="tRegistrationType" minOccurs="0" maxOccurs="2"/>
+			<xs:element name="Extension" type="tExtension" minOccurs="0"/>
+		</xs:sequence>
+	</xs:complexType>
+	<xs:complexType name="tIMSSubscription">
+		<xs:sequence>
+			<xs:element name="PrivateID" type="tPrivateID"/>
+			<xs:element name="ServiceProfile" type="tServiceProfile" maxOccurs="unbounded"/>
+			<xs:element name="Extension" type="tExtension" minOccurs="0"/>
+			<xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
+		</xs:sequence>
+	</xs:complexType>
+	<xs:complexType name="tServiceProfile">
+		<xs:sequence>
+			<xs:element name="PublicIdentity" type="tPublicIdentity" maxOccurs="unbounded"/>
+			<xs:element name="CoreNetworkServicesAuthorization" type="tCoreNetworkServicesAuthorization" minOccurs="0"/>
+			<xs:element name="InitialFilterCriteria" type="tInitialFilterCriteria" minOccurs="0" maxOccurs="unbounded"/>
+			<xs:element name="Extension" type="tServiceProfileExtension" minOccurs="0"/>
+			<xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
+		</xs:sequence>
+	</xs:complexType>
+	<xs:complexType name="tCoreNetworkServicesAuthorization">
+		<xs:sequence>
+			<xs:element name="SubscribedMediaProfileId" type="tSubscribedMediaProfileId" minOccurs="0"/>
+			<xs:element name="Extension" type="tExtension" minOccurs="0"/>
+			<xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
+		</xs:sequence>
+	</xs:complexType>
+	<xs:complexType name="tInitialFilterCriteria">
+		<xs:sequence>
+			<xs:element name="Priority" type="tPriority"/>
+			<xs:element name="TriggerPoint" type="tTrigger" minOccurs="0"/>
+			<xs:element name="ApplicationServer" type="tApplicationServer"/>
+			<xs:element name="ProfilePartIndicator" type="tProfilePartIndicator" minOccurs="0"/>
+			<xs:element name="Extension" type="tExtension" minOccurs="0"/>
+			<xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
+		</xs:sequence>
+	</xs:complexType>
+	<xs:complexType name="tTrigger">
+		<xs:sequence>
+			<xs:element name="ConditionTypeCNF" type="tBool"/>
+			<xs:element name="SPT" type="tSePoTri" maxOccurs="unbounded"/>
+			<xs:element name="Extension" type="tExtension" minOccurs="0"/>
+			<xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
+		</xs:sequence>
+	</xs:complexType>
+	<xs:complexType name="tSePoTri">
+		<xs:sequence>
+			<xs:element name="ConditionNegated" type="tBool" default="0" minOccurs="0"/>
+			<xs:element name="Group" type="tGroupID" maxOccurs="unbounded"/>
+			<xs:choice>
+				<xs:element name="RequestURI" type="tString"/>
+				<xs:element name="Method" type="tString"/>
+				<xs:element name="SIPHeader" type="tHeader"/>
+				<xs:element name="SessionCase" type="tDirectionOfRequest"/>
+				<xs:element name="SessionDescription" type="tSessionDescription"/>
+			</xs:choice>
+			<xs:element name="Extension" type="tSePoTriExtension" minOccurs="0"/>
+			<xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
+		</xs:sequence>
+	</xs:complexType>
+	<xs:complexType name="tHeader">
+		<xs:sequence>
+			<xs:element name="Header" type="tString"/>
+			<xs:element name="Content" type="tString" minOccurs="0"/>
+			<xs:element name="Extension" type="tExtension" minOccurs="0"/>
+			<xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
+		</xs:sequence>
+	</xs:complexType>
+	<xs:complexType name="tSessionDescription">
+		<xs:sequence>
+			<xs:element name="Line" type="tString"/>
+			<xs:element name="Content" type="tString" minOccurs="0"/>
+			<xs:element name="Extension" type="tExtension" minOccurs="0"/>
+			<xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
+		</xs:sequence>
+	</xs:complexType>
+	<xs:complexType name="tApplicationServer">
+		<xs:sequence>
+			<xs:element name="ServerName" type="tSIP_URL"/>
+			<xs:element name="DefaultHandling" type="tDefaultHandling" minOccurs="0"/>
+			<xs:element name="ServiceInfo" type="tServiceInfo" minOccurs="0"/>
+			<xs:element name="Extension" type="tExtension" minOccurs="0"/>
+			<xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
+		</xs:sequence>
+	</xs:complexType>
+	<xs:complexType name="tPublicIdentity">
+		<xs:sequence>
+			<xs:element name="BarringIndication" type="tBool" default="0" minOccurs="0"/>
+			<xs:element name="Identity" type="tIdentity"/>
+			<xs:element name="Extension" type="tPublicIdentityExtension" minOccurs="0"/>
+			<xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
+		</xs:sequence>
+	</xs:complexType>
+	<xs:element name="IMSSubscription" type="tIMSSubscription"/>
+</xs:schema>

+ 20 - 0
modules/registrar_scscf/Makefile

@@ -0,0 +1,20 @@
+# $Id$
+#
+# registrar module makefile
+#
+# 
+# WARNING: do not run this directly, it should be run by the master Makefile
+
+include ../../Makefile.defs
+auto_gen=
+NAME=registrar_scscf.so
+LIBS=
+
+DEFS+=-DOPENSER_MOD_INTERFACE
+DEFS += -I/usr/include/libxml2 
+LIBS += -L$(LOCALBASE)/lib -lxml2 -lrt
+
+SERLIBPATH=../../lib
+SER_LIBS+=$(SERLIBPATH)/kcore/kcore
+SER_LIBS+=$(SERLIBPATH)/ims/kamailio_ims
+include ../../Makefile.modules

+ 95 - 0
modules/registrar_scscf/api.c

@@ -0,0 +1,95 @@
+/*
+ * $Id$
+ *
+ * Functions that process REGISTER message 
+ * and store data in usrloc
+ *
+ * Copyright (C) 2010 Daniel-Constantin Mierla (asipto.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 "../../dprint.h"
+
+#include "reg_mod.h"
+#include "lookup.h"
+#include "save.h"
+#include "api.h"
+
+/**
+ *
+ */
+//int regapi_save(struct sip_msg *msg, char *table)
+//{
+//	udomain_t* d;
+//
+//	if(ul.get_udomain(table, &d)<0)
+//	{
+//		LM_ERR("usrloc domain [%s] not found\n", table);
+//		return -1;
+//	}
+//	return save(msg, d);
+//}
+//
+///**
+// *
+// */
+//int regapi_lookup(struct sip_msg *msg, char *table)
+//{
+//	udomain_t* d;
+//
+//	if(ul.get_udomain(table, &d)<0)
+//	{
+//		LM_ERR("usrloc domain [%s] not found\n", table);
+//		return -1;
+//	}
+//	return lookup(msg, d);
+//}
+//
+///**
+// *
+// */
+//int regapi_registered(struct sip_msg *msg, char *table)
+//{
+//	udomain_t* d;
+//
+//	if(ul.get_udomain(table, &d)<0)
+//	{
+//		LM_ERR("usrloc domain [%s] not found\n", table);
+//		return -1;
+//	}
+//	return term_impu_registered(msg, (char*)d, NULL);
+//}
+//
+///**
+// *
+// */
+//int bind_registrar(registrar_api_t* api)
+//{
+//	if (!api) {
+//		ERR("Invalid parameter value\n");
+//		return -1;
+//	}
+//	api->save   = regapi_save;
+//	api->lookup = regapi_lookup;
+//	api->registered = regapi_registered;
+//
+//	return 0;
+//}

+ 73 - 0
modules/registrar_scscf/api.h

@@ -0,0 +1,73 @@
+/*
+ * $Id$
+ *
+ * Functions that process REGISTER message 
+ * and store data in usrloc
+ *
+ * Copyright (C) 2010 Daniel-Constantin Mierla (asipto.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 _REGISTRAR_API_H_
+#define _REGISTRAR_API_H_
+
+#include "../../sr_module.h"
+#include "../../parser/msg_parser.h"
+
+typedef int (*regapi_save_f)(struct sip_msg *msg, char *table);
+int regapi_save(struct sip_msg *msg, char *table);
+
+typedef int (*regapi_lookup_f)(struct sip_msg *msg, char *table);
+int regapi_lookup(struct sip_msg *msg, char *table);
+
+/**
+ * @brief REGISTRAR API structure
+ */
+typedef struct registrar_api {
+	regapi_save_f    save;
+	regapi_lookup_f  lookup;
+	regapi_lookup_f  registered;
+} registrar_api_t;
+
+typedef int (*bind_registrar_f)(registrar_api_t* api);
+int bind_registrar(registrar_api_t* api);
+
+/**
+ * @brief Load the REGISTRAR API
+ */
+static inline int registrar_load_api(registrar_api_t *api)
+{
+	bind_registrar_f bindregistrar;
+
+	bindregistrar = (bind_registrar_f)find_export("bind_registrar", 0, 0);
+	if(bindregistrar == 0) {
+		LM_ERR("cannot find bind_registrar\n");
+		return -1;
+	}
+	if (bindregistrar(api) < 0)
+	{
+		LM_ERR("cannot bind registrar api\n");
+		return -1;
+	}
+	return 0;
+}
+
+
+#endif

+ 44 - 0
modules/registrar_scscf/blurb

@@ -0,0 +1,44 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * 
+ * The initial version of this code was written by Dragos Vingarzan
+ * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
+ * Fruanhofer Institute. It was and still is maintained in a separate
+ * branch of the original SER. We are therefore migrating it to
+ * Kamailio/SR and look forward to maintaining it from here on out.
+ * 2011/2012 Smile Communications, Pty. Ltd.
+ * ported/maintained/improved by 
+ * Jason Penton (jason(dot)penton(at)smilecoms.com and
+ * Richard Good (richard(dot)good(at)smilecoms.com) as part of an 
+ * effort to add full IMS support to Kamailio/SR using a new and
+ * improved architecture
+ * 
+ * NB: Alot of this code was originally part of OpenIMSCore,
+ * FhG Fokus. 
+ * Copyright (C) 2004-2006 FhG Fokus
+ * Thanks for great work! This is an effort to 
+ * break apart the various CSCF functions into logically separate
+ * components. We hope this will drive wider use. We also feel
+ * that in this way the architecture is more complete and thereby easier
+ * to manage in the Kamailio/SR environment
+ *
+ * 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
+ * 
+ */

+ 126 - 0
modules/registrar_scscf/common.c

@@ -0,0 +1,126 @@
+/*
+ * $Id$
+ *
+ * Common stuff
+ *
+ * Copyright (C) 2001-2003 FhG Fokus
+ *
+ * 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
+ *	
+ * History
+ * ------
+ * 2003-02-14 un-escaping added (janakj)
+ * 2006-09-19 AOR may be provided via an AVP instead of being fetched
+ *            from URI (bogdan)
+*/
+
+/*!
+ * \file
+ * \brief SIP registrar module
+ * \ingroup registrar
+ */
+
+#include <string.h> 
+#include "../../dprint.h"
+#include "../../parser/parse_uri.h"
+#include "rerrno.h"
+#include "reg_mod.h"
+#include "common.h"
+#include "config.h"
+
+#define MAX_AOR_LEN 256
+
+/*! \brief
+ * Extract Address of Record
+ */
+int extract_aor(str* _uri, str* _a) {
+	static char aor_buf[MAX_AOR_LEN];
+	str tmp;
+	struct sip_uri puri;
+	int user_len;
+	int_str avp_val;
+	struct usr_avp *avp;
+	str *uri;
+	str realm_prefix;
+
+	memset(aor_buf, 0, MAX_AOR_LEN);
+	if (aor_avp_name.n != 0) {
+		avp = search_first_avp(aor_avp_type, aor_avp_name, &avp_val, 0);
+		if (avp && is_avp_str_val(avp)) {
+			uri = &avp_val.s;
+		} else {
+			uri = _uri;
+		}
+	} else {
+		uri = _uri;
+	}
+
+	if (parse_uri(uri->s, uri->len, &puri) < 0) {
+		rerrno = R_AOR_PARSE;
+		LM_ERR("failed to parse Address of Record\n");
+		return -1;
+	}
+
+	if ((puri.user.len + puri.host.len + 1 + 4) > MAX_AOR_LEN
+			|| puri.user.len > USERNAME_MAX_SIZE
+			|| puri.host.len > DOMAIN_MAX_SIZE) {
+		rerrno = R_AOR_LEN;
+		LM_ERR("Address Of Record too long\n");
+		return -2;
+	}
+
+	_a->s = aor_buf;
+	_a->len = puri.user.len + 4;
+	strncpy(_a->s, "sip:", 4);
+
+	str tmps;
+	tmps.s = _a->s + 4;
+	tmps.len = puri.user.len;
+
+	if (un_escape(&puri.user, &tmps) < 0) {
+		rerrno = R_UNESCAPE;
+		LM_ERR("failed to unescape username\n");
+		return -3;
+	}
+
+	user_len = tmps.len + 4;//_a->len;
+
+	if (user_len>4)
+		aor_buf[_a->len++] = '@';
+	/* strip prefix (if defined) */
+	realm_prefix.s = cfg_get(registrar, registrar_cfg, realm_pref);
+	realm_prefix.len = strlen(realm_prefix.s);
+	if (realm_prefix.len && realm_prefix.len < puri.host.len
+			&& (memcmp(realm_prefix.s, puri.host.s, realm_prefix.len) == 0)) {
+		memcpy(aor_buf + _a->len, puri.host.s + realm_prefix.len,
+				puri.host.len - realm_prefix.len);
+		_a->len += puri.host.len - realm_prefix.len;
+	} else {
+		memcpy(aor_buf + _a->len, puri.host.s, puri.host.len);
+		_a->len += puri.host.len;
+	}
+
+	if (cfg_get(registrar, registrar_cfg, case_sensitive) && user_len) {
+		tmp.s = _a->s + user_len + 1;
+		tmp.len = _a->s + _a->len - tmp.s;
+		strlower(&tmp);
+	} else {
+		strlower(_a);
+	}
+
+	return 0;
+}

+ 44 - 0
modules/registrar_scscf/common.h

@@ -0,0 +1,44 @@
+/*
+ * $Id$
+ *
+ * Common stuff
+ *
+ * Copyright (C) 2001-2003 FhG Fokus
+ *
+ * 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
+ */
+
+/*!
+ * \file
+ * \brief SIP registrar module - common stuff
+ * \ingroup registrar   
+ */  
+
+
+#ifndef COMMON_H
+#define COMMON_H
+
+#include "../../str.h"
+
+
+/*! \brief
+ * Extract Address Of Record
+ */
+int extract_aor(str* _uri, str* _a);
+
+
+#endif /* COMMON_H */

+ 78 - 0
modules/registrar_scscf/config.c

@@ -0,0 +1,78 @@
+/*
+ * $Id$
+ * This file is part of SIP-router, a free SIP server.
+ *
+ * SIP-router 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
+ *
+ * SIP-router 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
+ *
+ */
+
+/*!
+ * \file 
+ * \brief Registrar :: Configuration
+ * \ingroup Registrar
+ */
+
+
+#include "../../cfg/cfg.h"
+#include "../../parser/msg_parser.h" /* method types */
+
+#include "config.h"
+
+struct cfg_group_registrar	default_registrar_cfg = {
+		3600, 	/* default_expires */
+		0,	/* default_expires_range */
+		60,	/* min_expires */
+		0,	/* max_expires */
+		3600,	/* emergency contact default expires */
+		60,	/* emergency contact min expires */
+		0,	/* emergency contact max expires */
+		0,	/* max_contacts */
+		0,	/* retry_after */
+		0,	/* case_sensitive */
+		Q_UNSPECIFIED,	/* default_q */
+		1,	/* append_branches */
+		""	/* realm_pref */
+	    };
+
+void	*registrar_cfg = &default_registrar_cfg;
+
+cfg_def_t	registrar_cfg_def[] = {
+	{"default_expires",	CFG_VAR_INT | CFG_CB_ONLY_ONCE,	0, 0, 0, default_expires_stats_update,
+		"Contains number of second to expire if no expire hf or contact expire present" },
+	{"default_expires_range",	CFG_VAR_INT | CFG_CB_ONLY_ONCE,	0, 100, 0, default_expires_range_update,
+		"Percent from default_expires that will be used in generating the range for the expire interval"},
+	{"min_expires",		CFG_VAR_INT | CFG_CB_ONLY_ONCE,	0, 0, 0, 0,
+		"The minimum expires value of a Contact. Value 0 disables the checking. "},
+	{"max_expires",		CFG_VAR_INT | CFG_CB_ONLY_ONCE,	0, 0, 0, max_expires_stats_update,
+		"The maximum expires value of a Contact. Value 0 disables the checking. "},
+	{"em_default_expires",	CFG_VAR_INT | CFG_CB_ONLY_ONCE,	0, 0, 0, 0,
+		"The default emergency expires value of a Contact."},
+	{"em_max_expires",	CFG_VAR_INT | CFG_CB_ONLY_ONCE,	0, 0, 0, 0,
+		"The maximum emergency expires value of a Contact. Value 0 disables the checking. "},
+	{"em_min_expires",	CFG_VAR_INT | CFG_CB_ONLY_ONCE,	0, 0, 0, 0,
+		"The maximum emergency expires value of a Contact. Value 0 disables the checking. "},
+	{"max_contacts",	CFG_VAR_INT | CFG_ATOMIC, 	0, 0, 0, 0,
+		"The maximum number of Contacts for an AOR. Value 0 disables the checking. "},
+	{"retry_after",		CFG_VAR_INT | CFG_ATOMIC, 	0, 0, 0, 0,
+		"If you want to add the Retry-After header field in 5xx replies, set this parameter to a value grater than zero"},
+	{"case_sensitive",	CFG_VAR_INT | CFG_ATOMIC,	0, 0, 0, 0,
+		"If set to 1 then AOR comparison will be case sensitive. Recommended and default is 0, case insensitive"},
+	{"default_q",		CFG_VAR_INT | CFG_ATOMIC,	-1, 1000, 0, 0,
+		"The parameter represents default q value for new contacts."}, /* Q_UNSPECIFIED is -1 */
+	{"append_branches",	CFG_VAR_INT ,			0, 0, 0, 0,
+		"If set to 1(default), lookup will put all contacts found in msg structure"},
+	{"realm_pref",		CFG_VAR_STRING ,			0, 0, 0, 0, "Realm prefix to be removed. Default is \"\""},
+	{0, 0, 0, 0, 0, 0}
+};

+ 53 - 0
modules/registrar_scscf/config.h

@@ -0,0 +1,53 @@
+/*
+ * $Id$
+ *
+ * SIP-router 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
+ */
+
+/*!
+ * \file 
+ * \brief Registrar :: Configuration
+ * \ingroup registrar
+ */
+
+
+#ifndef _REGISTRAR_CONFIG_H
+#define _REGISTRAR_CONFIG_H
+
+#include "../../qvalue.h"
+
+#include "../../cfg/cfg.h"
+#include "../../str.h"
+
+struct cfg_group_registrar {
+	unsigned int	default_expires;
+	unsigned int	default_expires_range;
+	unsigned int	min_expires;
+	unsigned int	max_expires;
+	unsigned int	em_default_expires;
+	unsigned int	em_max_expires;
+	unsigned int	em_min_expires;
+	unsigned int	max_contacts;
+	unsigned int	retry_after;
+	unsigned int	case_sensitive;
+	qvalue_t	default_q;
+	unsigned int	append_branches;
+	char* 		realm_pref;
+};
+
+extern struct cfg_group_registrar	default_registrar_cfg;
+extern void	*registrar_cfg;
+extern cfg_def_t	registrar_cfg_def[];
+
+extern void default_expires_stats_update(str*, str*);
+extern void default_expires_range_update(str*, str*);
+extern void max_expires_stats_update(str*, str*);
+
+#endif

+ 1047 - 0
modules/registrar_scscf/cxdx_avp.c

@@ -0,0 +1,1047 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * 
+ * The initial version of this code was written by Dragos Vingarzan
+ * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
+ * Fruanhofer Institute. It was and still is maintained in a separate
+ * branch of the original SER. We are therefore migrating it to
+ * Kamailio/SR and look forward to maintaining it from here on out.
+ * 2011/2012 Smile Communications, Pty. Ltd.
+ * ported/maintained/improved by 
+ * Jason Penton (jason(dot)penton(at)smilecoms.com and
+ * Richard Good (richard(dot)good(at)smilecoms.com) as part of an 
+ * effort to add full IMS support to Kamailio/SR using a new and
+ * improved architecture
+ * 
+ * NB: Alot of this code was originally part of OpenIMSCore,
+ * FhG Fokus. 
+ * Copyright (C) 2004-2006 FhG Fokus
+ * Thanks for great work! This is an effort to 
+ * break apart the various CSCF functions into logically separate
+ * components. We hope this will drive wider use. We also feel
+ * that in this way the architecture is more complete and thereby easier
+ * to manage in the Kamailio/SR environment
+ *
+ * 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 "../cdp/cdp_load.h"
+#include "../../modules/tm/tm_load.h"
+#include "cxdx_avp.h"
+
+
+
+static str s_empty = {0, 0};
+
+/**
+ * Create and add an AVP to a Diameter message.
+ * @param m - Diameter message to add to 
+ * @param d - the payload data
+ * @param len - length of the payload data
+ * @param avp_code - the code of the AVP
+ * @param flags - flags for the AVP
+ * @param vendorid - the value of the vendor id or 0 if none
+ * @param data_do - what to do with the data when done
+ * @param func - the name of the calling function, for debugging purposes
+ * @returns 1 on success or 0 on failure
+ */
+static inline int cxdx_add_avp(AAAMessage *m,char *d,int len,int avp_code,
+	int flags,int vendorid,int data_do,const char *func)
+{
+	AAA_AVP *avp;
+	if (vendorid!=0) flags |= AAA_AVP_FLAG_VENDOR_SPECIFIC;
+	avp = cdpb.AAACreateAVP(avp_code,flags,vendorid,d,len,data_do);
+	if (!avp) {
+		LM_ERR("%s: Failed creating avp\n",func);
+		return 0;
+	}
+	if (cdpb.AAAAddAVPToMessage(m,avp,m->avpList.tail)!=AAA_ERR_SUCCESS) {
+		LM_ERR("%s: Failed adding avp to message\n",func);
+		cdpb.AAAFreeAVP(&avp);
+		return 0;
+	}
+	return 1;
+}
+
+/**
+ * Create and add an AVP to a list of AVPs.
+ * @param list - the AVP list to add to 
+ * @param d - the payload data
+ * @param len - length of the payload data
+ * @param avp_code - the code of the AVP
+ * @param flags - flags for the AVP
+ * @param vendorid - the value of the vendor id or 0 if none
+ * @param data_do - what to do with the data when done
+ * @param func - the name of the calling function, for debugging purposes
+ * @returns 1 on success or 0 on failure
+ */
+static inline int cxdx_add_avp_list(AAA_AVP_LIST *list,char *d,int len,int avp_code,
+	int flags,int vendorid,int data_do,const char *func)
+{
+	AAA_AVP *avp;
+	if (vendorid!=0) flags |= AAA_AVP_FLAG_VENDOR_SPECIFIC;
+	avp = cdpb.AAACreateAVP(avp_code,flags,vendorid,d,len,data_do);
+	if (!avp) {
+		LM_ERR("%s: Failed creating avp\n",func);
+		return 0;
+	}
+	if (list->tail) {
+		avp->prev=list->tail;
+		avp->next=0;	
+		list->tail->next = avp;
+		list->tail=avp;
+	} else {
+		list->head = avp;
+		list->tail = avp;
+		avp->next=0;
+		avp->prev=0;
+	}
+	
+	return 1;
+}
+
+/**
+ * Returns the value of a certain AVP from a Diameter message.
+ * @param m - Diameter message to look into
+ * @param avp_code - the code to search for
+ * @param vendorid - the value of the vendor id to look for or 0 if none
+ * @param func - the name of the calling function, for debugging purposes
+ * @returns the str with the payload on success or an empty string on failure
+ */
+static inline str cxdx_get_avp(AAAMessage *msg,int avp_code,int vendor_id,
+							const char *func)
+{
+	AAA_AVP *avp;
+	str r={0,0};
+	
+	avp = cdpb.AAAFindMatchingAVP(msg,0,avp_code,vendor_id,0);
+	if (avp==0){
+		LM_INFO("%s: Failed finding avp\n",func);
+		return r;
+	}
+	else 
+		return avp->data;
+}
+
+/**
+ * Creates and adds a Destination-Realm AVP.
+ * @param msg - the Diameter message to add to.
+ * @param data - the value for the AVP payload
+ * @returns 1 on success or 0 on error
+ */
+inline int cxdx_add_destination_realm(AAAMessage *msg,str data)
+{
+	return 
+	cxdx_add_avp(msg,data.s,data.len,
+		AVP_Destination_Realm,
+		AAA_AVP_FLAG_MANDATORY,
+		0,
+		AVP_DUPLICATE_DATA,
+		__FUNCTION__);
+}
+
+
+/**
+ * Creates and adds a Vendor-Specifig-Application-ID AVP.
+ * @param msg - the Diameter message to add to.
+ * @param vendor_id - the value of the vendor_id,
+ * @param auth_id - the authorization application id
+ * @param acct_id - the accounting application id
+ * @returns 1 on success or 0 on error
+ */
+inline int cxdx_add_vendor_specific_appid(AAAMessage *msg,unsigned int vendor_id,
+	unsigned int auth_id,unsigned int acct_id)
+{
+	AAA_AVP_LIST list;
+	str group;
+	char x[4];
+
+	list.head=0;list.tail=0;
+		
+	set_4bytes(x,vendor_id);
+	cxdx_add_avp_list(&list,
+		x,4,
+		AVP_Vendor_Id,
+		AAA_AVP_FLAG_MANDATORY,
+		0,
+		AVP_DUPLICATE_DATA,
+		__FUNCTION__);
+
+	if (auth_id) {
+		set_4bytes(x,auth_id);
+		cxdx_add_avp_list(&list,
+			x,4,
+			AVP_Auth_Application_Id,
+			AAA_AVP_FLAG_MANDATORY,
+			0,
+			AVP_DUPLICATE_DATA,
+			__FUNCTION__);
+	}
+	if (acct_id) {
+		set_4bytes(x,acct_id);
+		cxdx_add_avp_list(&list,
+			x,4,
+			AVP_Acct_Application_Id,
+			AAA_AVP_FLAG_MANDATORY,
+			0,
+			AVP_DUPLICATE_DATA,
+			__FUNCTION__);
+	}	
+	
+	group = cdpb.AAAGroupAVPS(list);
+	
+	cdpb.AAAFreeAVPList(&list);
+	
+	return 
+	cxdx_add_avp(msg,group.s,group.len,
+		AVP_Vendor_Specific_Application_Id,
+		AAA_AVP_FLAG_MANDATORY,
+		0,
+		AVP_FREE_DATA,
+		__FUNCTION__);
+}
+
+/**
+ * Creates and adds a Auth-Session-State AVP.
+ * @param msg - the Diameter message to add to.
+ * @param data - the value for the AVP payload
+ * @returns 1 on success or 0 on error
+ */
+inline int cxdx_add_auth_session_state(AAAMessage *msg,unsigned int data)
+{
+	char x[4];
+	set_4bytes(x,data);
+	return 
+	cxdx_add_avp(msg,x,4,
+		AVP_Auth_Session_State,
+		AAA_AVP_FLAG_MANDATORY,
+		0,
+		AVP_DUPLICATE_DATA,
+		__FUNCTION__);
+}
+
+/**
+ * Creates and adds a User-Name AVP.
+ * @param msg - the Diameter message to add to.
+ * @param data - the value for the AVP payload
+ * @returns 1 on success or 0 on error
+ */
+inline int cxdx_add_user_name(AAAMessage *msg,str data)
+{
+	return 
+	cxdx_add_avp(msg,data.s,data.len,
+		AVP_User_Name,
+		AAA_AVP_FLAG_MANDATORY,
+		0,
+		AVP_DUPLICATE_DATA,
+		__FUNCTION__);
+}
+
+/**
+ * Creates and adds a Public Identity AVP.
+ * @param msg - the Diameter message to add to.
+ * @param data - the value for the AVP payload
+ * @returns 1 on success or 0 on error
+ */
+inline int cxdx_add_public_identity(AAAMessage *msg,str data)
+{
+	return 
+	cxdx_add_avp(msg,data.s,data.len,
+		AVP_IMS_Public_Identity,
+		AAA_AVP_FLAG_MANDATORY|AAA_AVP_FLAG_VENDOR_SPECIFIC,
+		IMS_vendor_id_3GPP,
+		AVP_DUPLICATE_DATA,
+		__FUNCTION__);
+}
+
+/**
+ * Creates and adds a Visited-Network-ID AVP.
+ * @param msg - the Diameter message to add to.
+ * @param data - the value for the AVP payload
+ * @returns 1 on success or 0 on error
+ */
+inline int cxdx_add_visited_network_id(AAAMessage *msg,str data)
+{
+	return 
+	cxdx_add_avp(msg,data.s,data.len,
+		AVP_IMS_Visited_Network_Identifier,
+		AAA_AVP_FLAG_MANDATORY|AAA_AVP_FLAG_VENDOR_SPECIFIC,
+		IMS_vendor_id_3GPP,
+		AVP_DUPLICATE_DATA,
+		__FUNCTION__);
+}
+
+/**
+ * Creates and adds a UAR-Flags AVP.
+ * @param msg - the Diameter message to add to.
+ * @param data - the value for the AVP payload
+ * @returns 1 on success or 0 on error
+ */
+inline int cxdx_add_UAR_flags(AAAMessage *msg, unsigned int sos_reg)
+{
+
+	char x[4];
+	/* optional AVP*/
+	if(!sos_reg)
+		return 1;
+	
+	set_4bytes(x, AVP_IMS_UAR_Flags_Emergency_Registration);
+	return 
+	cxdx_add_avp(msg,x,4,
+		AVP_IMS_UAR_Flags,
+		AAA_AVP_FLAG_VENDOR_SPECIFIC,
+		IMS_vendor_id_3GPP,
+		AVP_DUPLICATE_DATA,
+		__FUNCTION__);
+
+}
+/**
+ * Creates and adds a Authorization-Type AVP.
+ * @param msg - the Diameter message to add to.
+ * @param data - the value for the AVP payload
+ * @returns 1 on success or 0 on error
+ */
+inline int cxdx_add_authorization_type(AAAMessage *msg,unsigned int data)
+{
+	char x[4];
+	set_4bytes(x,data);
+	return 
+	cxdx_add_avp(msg,x,4,
+		AVP_IMS_User_Authorization_Type,
+		AAA_AVP_FLAG_MANDATORY|AAA_AVP_FLAG_VENDOR_SPECIFIC,
+		IMS_vendor_id_3GPP,
+		AVP_DUPLICATE_DATA,
+		__FUNCTION__);
+}
+
+/**
+ * Returns the Result-Code AVP from a Diameter message.
+ * @param msg - the Diameter message
+ * @returns the AVP payload on success or an empty string on error
+ */
+inline int cxdx_get_result_code(AAAMessage *msg, int *data)
+{
+	str s;
+	s = cxdx_get_avp(msg,
+		AVP_Result_Code,
+		0,
+		__FUNCTION__);
+	if (!s.s) return 0;
+	*data = get_4bytes(s.s);
+	return 1;
+}
+
+/**
+ * Returns the Experimental-Result-Code AVP from a Diameter message.
+ * @param msg - the Diameter message
+ * @returns the AVP payload on success or an empty string on error
+ */
+inline int cxdx_get_experimental_result_code(AAAMessage *msg, int *data)
+{
+	AAA_AVP_LIST list;
+	AAA_AVP *avp;
+	str grp;
+	grp = cxdx_get_avp(msg,
+		AVP_IMS_Experimental_Result,
+		0,
+		__FUNCTION__);
+	if (!grp.s) return 0;
+
+	list = cdpb.AAAUngroupAVPS(grp);
+	
+	avp = cdpb.AAAFindMatchingAVPList(list,0,AVP_IMS_Experimental_Result_Code,0,0);
+	if (!avp||!avp->data.s) {
+		cdpb.AAAFreeAVPList(&list);
+		return 0;
+	}
+
+	*data = get_4bytes(avp->data.s);
+	cdpb.AAAFreeAVPList(&list);
+
+	return 1;
+}
+
+/**
+ * Returns the Server-Name AVP from a Diameter message.
+ * @param msg - the Diameter message
+ * @returns the AVP payload on success or an empty string on error
+ */
+inline str cxdx_get_server_name(AAAMessage *msg)
+{	
+	return cxdx_get_avp(msg,
+		AVP_IMS_Server_Name,
+		IMS_vendor_id_3GPP,
+		__FUNCTION__);
+}
+
+/**
+ * Returns the Capabilities from the grouped AVP from a Diameter message.
+ * @param msg - the Diameter message
+ * @param m - array to be filled with the retrieved mandatory capabilities
+ * @param m_cnt - size of the array above to be filled
+ * @param o - array to be filled with the retrieved optional capabilities
+ * @param o_cnt - size of the array above to be filled
+ * @returns 1 on success 0 on fail
+ */
+inline int cxdx_get_capabilities(AAAMessage *msg,int **m,int *m_cnt,int **o,int *o_cnt,
+	str **p,int *p_cnt)
+{
+	AAA_AVP_LIST list;
+	AAA_AVP *avp;
+	str grp;
+	grp = cxdx_get_avp(msg,
+		AVP_IMS_Server_Capabilities,
+		IMS_vendor_id_3GPP,
+		__FUNCTION__);
+	if (!grp.s) return 0;
+
+	list = cdpb.AAAUngroupAVPS(grp);
+	
+	avp = list.head;
+	*m_cnt=0;
+	*o_cnt=0;
+	*p_cnt=0;
+	while(avp){
+		if (avp->code == AVP_IMS_Mandatory_Capability) (*m_cnt)++;
+		if (avp->code == AVP_IMS_Optional_Capability) (*o_cnt)++;		
+		if (avp->code == AVP_IMS_Server_Name) (*p_cnt)++;
+		avp = avp->next;
+	}
+	avp = list.head;
+	*m=shm_malloc(sizeof(int)*(*m_cnt));
+	if (!*m){
+		LM_ERR("cannot allocated %lx bytes of shm.\n",
+			sizeof(int)*(*m_cnt));
+		goto error;
+	}
+	*o=shm_malloc(sizeof(int)*(*o_cnt));
+	if (!*o){
+		LM_ERR("cannot allocated %lx bytes of shm.\n",
+			sizeof(int)*(*o_cnt));
+		goto error;
+	}
+	*p=shm_malloc(sizeof(str)*(*p_cnt));
+	if (!*p){
+		LM_ERR("cannot allocated %lx bytes of shm.\n",
+			sizeof(str)*(*p_cnt));
+		goto error;
+	}
+	
+	*m_cnt=0;
+	*o_cnt=0;
+	*p_cnt=0;
+	while(avp){
+		if (avp->code == AVP_IMS_Mandatory_Capability) 
+			(*m)[(*m_cnt)++]=get_4bytes(avp->data.s);
+		if (avp->code == AVP_IMS_Optional_Capability)		
+			(*o)[(*o_cnt)++]=get_4bytes(avp->data.s);
+		if (avp->code == AVP_IMS_Server_Name)		
+			(*p)[(*p_cnt)++]=avp->data;
+		avp = avp->next;
+	}
+	cdpb.AAAFreeAVPList(&list);
+	return 1;
+	
+error:
+	cdpb.AAAFreeAVPList(&list);
+	if (*m) shm_free(*m);	
+	if (*o) shm_free(*o);	
+	if (*p) shm_free(*p);
+	*m_cnt=0;
+	*o_cnt=0;
+	*p_cnt=0;
+	return 0;
+}
+
+/**
+ * Transactional SIP response - tries to create a transaction if none found.
+ * @param msg - message to reply to
+ * @param code - the Status-code for the response
+ * @param text - the Reason-Phrase for the response
+ * @returns the tmb.t_repy() result
+ */
+int cscf_reply_transactional(struct sip_msg *msg, int code, char *text)
+{
+	unsigned int hash,label;
+	if (tmb.t_get_trans_ident(msg,&hash,&label)<0){	
+	
+		if (tmb.t_newtran(msg)<0) 
+			LM_ERR("Failed creating SIP transaction\n");
+	}
+	return tmb.t_reply(msg,code,text);
+}
+
+/**
+ * Creates and adds a SIP-Number-Auth-Items AVP.
+ * @param msg - the Diameter message to add to.
+ * @param data - the value for the AVP payload
+ * @returns 1 on success or 0 on error
+ */
+inline int cxdx_add_sip_number_auth_items(AAAMessage *msg,unsigned int data)
+{
+	char x[4];
+	set_4bytes(x,data);
+	return 
+	cxdx_add_avp(msg,x,4,
+		AVP_IMS_SIP_Number_Auth_Items,
+		AAA_AVP_FLAG_MANDATORY|AAA_AVP_FLAG_VENDOR_SPECIFIC,
+		IMS_vendor_id_3GPP,
+		AVP_DUPLICATE_DATA,
+		__FUNCTION__);
+}
+
+/**
+ * Creates and adds a SIP-Auth-Data-Item AVP.
+ * @param msg - the Diameter message to add to.
+ * @param auth_scheme - the value for the authorization scheme AVP
+ * @param auth - the value for the authorization AVP
+ * @returns 1 on success or 0 on error
+ */
+inline int cxdx_add_sip_auth_data_item_request(AAAMessage *msg, str auth_scheme, str auth, str username, str realm,str method, str server_name)
+{
+	AAA_AVP_LIST list;
+	str group;
+	str etsi_authorization = {0, 0};
+	list.head=0;list.tail=0;
+		
+	if (auth_scheme.len){
+		cxdx_add_avp_list(&list,
+			auth_scheme.s,auth_scheme.len,
+			AVP_IMS_SIP_Authentication_Scheme,
+			AAA_AVP_FLAG_MANDATORY|AAA_AVP_FLAG_VENDOR_SPECIFIC,
+			IMS_vendor_id_3GPP,
+			AVP_DONT_FREE_DATA,
+			__FUNCTION__);
+	}	
+	if (auth.len){
+		cxdx_add_avp_list(&list,
+			auth.s,auth.len,
+			AVP_IMS_SIP_Authorization,
+			AAA_AVP_FLAG_MANDATORY|AAA_AVP_FLAG_VENDOR_SPECIFIC,
+			IMS_vendor_id_3GPP,
+			AVP_DONT_FREE_DATA,
+			__FUNCTION__);
+	}
+
+	if (server_name.len) 
+	{
+		etsi_authorization = cxdx_ETSI_sip_authorization(username, realm, s_empty, server_name, s_empty, s_empty, method, s_empty);
+	
+		if (etsi_authorization.len){
+			cxdx_add_avp_list(&list,
+				etsi_authorization.s,etsi_authorization.len,
+				AVP_ETSI_SIP_Authorization,
+				AAA_AVP_FLAG_MANDATORY|AAA_AVP_FLAG_VENDOR_SPECIFIC,
+				IMS_vendor_id_ETSI,
+				AVP_FREE_DATA,
+				__FUNCTION__);
+		}	
+	}
+
+	if (!list.head) return 1;
+	group = cdpb.AAAGroupAVPS(list);
+	
+	cdpb.AAAFreeAVPList(&list);
+	
+	return 
+	cxdx_add_avp(msg,group.s,group.len,
+		AVP_IMS_SIP_Auth_Data_Item,
+		AAA_AVP_FLAG_MANDATORY|AAA_AVP_FLAG_VENDOR_SPECIFIC,
+		IMS_vendor_id_3GPP,
+		AVP_FREE_DATA,
+		__FUNCTION__);
+}
+
+/**
+ * Creates and adds a Server-Name AVP.
+ * @param msg - the Diameter message to add to.
+ * @param data - the value for the AVP payload
+ * @returns 1 on success or 0 on error
+ */
+inline int cxdx_add_server_name(AAAMessage *msg,str data)
+{
+	return 
+	cxdx_add_avp(msg,data.s,data.len,
+		AVP_IMS_Server_Name,
+		AAA_AVP_FLAG_MANDATORY|AAA_AVP_FLAG_VENDOR_SPECIFIC,
+		IMS_vendor_id_3GPP,
+		AVP_DUPLICATE_DATA,
+		__FUNCTION__);
+}
+
+/**
+ * Returns the SIP-Number-Auth-Items AVP from a Diameter message.
+ * @param msg - the Diameter message
+ * @returns the number or 0 on error
+ */
+inline int cxdx_get_sip_number_auth_items(AAAMessage *msg, int *data)
+{
+	str s;
+	s = cxdx_get_avp(msg,
+		AVP_IMS_SIP_Number_Auth_Items,
+		IMS_vendor_id_3GPP,
+		__FUNCTION__);
+	if (!s.s) return 0;
+	*data = get_4bytes(s.s);
+	return 1;
+}
+
+/**
+ * Returns the Auth-Data-Item from a Diameter answer message.
+ * @param msg - the Diameter message
+ * @param auth_date - the string to fill with the authorization data
+ * @param item_number - the int to fill with the item number
+ * @param auth_scheme - the string to fill with the authentication scheme data
+ * @param authenticate - the string to fill with the authenticate data
+ * @param authorization - the string to fill with the authorization data
+ * @param ck - the string to fill with the cipher key
+ * @param ik - the string to fill with the integrity key
+ * @returns the AVP payload on success or an empty string on error
+ */
+int cxdx_get_auth_data_item_answer(AAAMessage *msg, AAA_AVP **auth_data,
+	int *item_number,str *auth_scheme,str *authenticate,str *authorization,
+	str *ck,str *ik,
+	str *ip, 
+	str *ha1, str *response_auth, str *digest_realm,
+	str *line_identifier)
+{
+	AAA_AVP_LIST list;
+	AAA_AVP_LIST list2;
+	AAA_AVP *avp;
+	AAA_AVP *avp2;
+	str grp;
+	ha1->s = 0; ha1->len = 0;
+	*auth_data = cdpb.AAAFindMatchingAVP(msg,*auth_data,AVP_IMS_SIP_Auth_Data_Item,
+		IMS_vendor_id_3GPP,0);
+	if (!*auth_data) return 0;
+		
+	grp = (*auth_data)->data;
+	if (!grp.len) return 0;
+
+	list = cdpb.AAAUngroupAVPS(grp);
+
+	avp = cdpb.AAAFindMatchingAVPList(list,0,AVP_IMS_SIP_Item_Number,
+		IMS_vendor_id_3GPP,0);
+	if (!avp||!avp->data.len==4) *item_number=0;
+	else *item_number = get_4bytes(avp->data.s);
+	
+	avp = cdpb.AAAFindMatchingAVPList(list,0,AVP_IMS_SIP_Authentication_Scheme,
+		IMS_vendor_id_3GPP,0);
+	if (!avp||!avp->data.s) {auth_scheme->s=0;auth_scheme->len=0;}
+	else *auth_scheme = avp->data;
+
+	/* Early-IMS */
+	ip->s=0;ip->len=0;
+	avp = cdpb.AAAFindMatchingAVPList(list,0,AVP_Framed_IP_Address,0,0);
+	if (avp && avp->data.s){
+		if (avp->data.len!=4){
+			LM_ERR("Invalid length of AVP Framed IP Address (should be 4 for AVP_Framed_IP_Address) >%d.\n",
+				avp->data.len);
+		}
+		ip->len = 4;
+		ip->s = avp->data.s;
+	} else { 
+		avp = cdpb.AAAFindMatchingAVPList(list,0,AVP_Framed_IPv6_Prefix,0,0);
+		if (avp && avp->data.s){
+			if (avp->data.len==0){
+				LM_ERR("Invalid length of AVP Framed IPv6 Prefix (should be >0 for AVP_Framed_IPv6_Prefix) >%d.\n",
+					avp->data.len);
+			}
+			ip->len = avp->data.len;
+			ip->s = avp->data.s;	
+		}	
+	}
+
+	/* Digest */
+
+	avp = cdpb.AAAFindMatchingAVPList(list,0,AVP_CableLabs_SIP_Digest_Authenticate,IMS_vendor_id_CableLabs,0);
+	if (avp  && avp->data.s) 
+	{
+		list2 = cdpb.AAAUngroupAVPS(avp->data);
+		
+		avp2 = cdpb.AAAFindMatchingAVPList(list2,0,AVP_CableLabs_Digest_HA1,IMS_vendor_id_CableLabs,0);
+		if (!avp2||!avp2->data.s) {
+			ha1->s = 0; ha1->len = 0;
+			cdpb.AAAFreeAVPList(&list2);
+			return 0;
+		}
+		*ha1 = avp2->data;
+		cdpb.AAAFreeAVPList(&list2);
+	}
+	
+	
+	/* SIP Digest */
+
+	avp = cdpb.AAAFindMatchingAVPList(list,0,AVP_IMS_SIP_Digest_Authenticate,IMS_vendor_id_3GPP,0);
+	if (avp  && avp->data.s) 
+	{
+		list2 = cdpb.AAAUngroupAVPS(avp->data);
+		
+		avp2 = cdpb.AAAFindMatchingAVPList(list2,0,AVP_IMS_Digest_HA1,0,0);
+		if (!avp2||!avp2->data.s) {
+			ha1->s = 0; ha1->len = 0;
+			cdpb.AAAFreeAVPList(&list2);
+			return 0;
+		}
+		*ha1 = avp2->data;
+		cdpb.AAAFreeAVPList(&list2);
+	}
+	
+	
+	/* AKA, MD5 */
+	avp = cdpb.AAAFindMatchingAVPList(list,0,AVP_IMS_SIP_Authenticate,
+		IMS_vendor_id_3GPP,0);
+	if (!avp||!avp->data.s) {authenticate->s=0;authenticate->len=0;}
+	else *authenticate = avp->data;
+		
+	avp = cdpb.AAAFindMatchingAVPList(list,0,AVP_IMS_SIP_Authorization,
+		IMS_vendor_id_3GPP,0);
+	if (!avp||!avp->data.s) {authorization->s=0;authorization->len=0;}
+	else *authorization = avp->data;	
+
+	avp = cdpb.AAAFindMatchingAVPList(list,0,AVP_IMS_Confidentiality_Key,
+		IMS_vendor_id_3GPP,0);
+	if (!avp||!avp->data.s) {ck->s=0;ck->len=0;}
+	else *ck = avp->data;
+
+	avp = cdpb.AAAFindMatchingAVPList(list,0,AVP_IMS_Integrity_Key,
+		IMS_vendor_id_3GPP,0);
+	if (!avp||!avp->data.s) {ik->s=0;ik->len=0;}
+	else *ik = avp->data;
+
+	/* ETSI HTTP Digest */
+
+	avp = cdpb.AAAFindMatchingAVPList(list,0,AVP_ETSI_SIP_Authenticate,IMS_vendor_id_ETSI,0);
+	if (avp  && avp->data.s) 
+	{
+		list2 = cdpb.AAAUngroupAVPS(avp->data);
+		
+		avp2 = cdpb.AAAFindMatchingAVPList(list2,0,AVP_ETSI_Digest_Realm, IMS_vendor_id_ETSI,0);
+		if (!avp2||!avp2->data.s) {
+			digest_realm->s=0;digest_realm->len=0;
+			cdpb.AAAFreeAVPList(&list2);
+			return 0;
+		}
+		*digest_realm = avp2->data;
+
+		avp2 = cdpb.AAAFindMatchingAVPList(list2,0,AVP_ETSI_Digest_Nonce, IMS_vendor_id_ETSI,0);
+		if (!avp2||!avp2->data.s) {
+			authenticate->s=0;authenticate->len=0;
+			cdpb.AAAFreeAVPList(&list2);
+			return 0;
+		}
+		*authenticate = avp2->data;
+		
+		avp2 = cdpb.AAAFindMatchingAVPList(list2,0,AVP_ETSI_Digest_HA1, IMS_vendor_id_ETSI,0);
+		if (!avp2||!avp2->data.s) {
+			ha1->s = 0; ha1->len = 0;
+			cdpb.AAAFreeAVPList(&list2);
+			return 0;
+		}
+		*ha1 = avp2->data;
+		
+		cdpb.AAAFreeAVPList(&list2);
+	}
+
+	avp = cdpb.AAAFindMatchingAVPList(list,0,AVP_ETSI_SIP_Authentication_Info,IMS_vendor_id_ETSI,0);
+	if (avp  && avp->data.s) 
+	{
+		list2 = cdpb.AAAUngroupAVPS(avp->data);
+		
+		avp2 = cdpb.AAAFindMatchingAVPList(list2,0,AVP_ETSI_Digest_Response_Auth, IMS_vendor_id_ETSI,0);
+		if (!avp2||!avp2->data.s) {
+			response_auth->s=0;response_auth->len=0;
+			cdpb.AAAFreeAVPList(&list2);
+			return 0;
+		}
+		*response_auth = avp2->data;
+		cdpb.AAAFreeAVPList(&list2);
+	}
+	else
+	{
+		response_auth->s=0;response_auth->len=0;
+	}
+	
+	/* NASS Bundled */
+	avp = cdpb.AAAFindMatchingAVPList(list,0,AVP_ETSI_Line_Identifier, IMS_vendor_id_ETSI,0);
+	if (!avp||!avp->data.s) {line_identifier->s=0;line_identifier->len=0;}
+	else *line_identifier = avp->data;
+	
+	cdpb.AAAFreeAVPList(&list);
+	return 1;
+}
+
+/**
+ * Creates and adds a ETSI_sip_authorization AVP.
+ * @param username - UserName
+ * @param realm - Realm
+ * @param nonce - Nonce
+ * @param URI - URI
+ * @param response - Response
+ * @param algoritm - Algorithm
+ * @param method - Method
+ * @param hash - Enitity-Body-Hash
+ * @returns grouped str on success
+ */
+str cxdx_ETSI_sip_authorization(str username, str realm, str nonce, str URI, str response, str algorithm, str method, str hash)
+{
+	AAA_AVP_LIST list;
+	str group = {0, 0};
+	list.head=0;list.tail=0;
+		
+	if (username.len){
+		cxdx_add_avp_list(&list,
+			username.s,username.len,
+			AVP_ETSI_Digest_Username,
+			AAA_AVP_FLAG_MANDATORY|AAA_AVP_FLAG_VENDOR_SPECIFIC,
+			IMS_vendor_id_ETSI,
+			AVP_DONT_FREE_DATA,
+			__FUNCTION__);
+	}	
+
+	if (realm.len){
+		cxdx_add_avp_list(&list,
+			realm.s,realm.len,
+			AVP_ETSI_Digest_Realm,
+			AAA_AVP_FLAG_MANDATORY|AAA_AVP_FLAG_VENDOR_SPECIFIC,
+			IMS_vendor_id_ETSI,
+			AVP_DONT_FREE_DATA,
+			__FUNCTION__);
+	}	
+	
+	if (nonce.len){
+		cxdx_add_avp_list(&list,
+			nonce.s,nonce.len,
+			AVP_ETSI_Digest_Nonce,
+			AAA_AVP_FLAG_MANDATORY|AAA_AVP_FLAG_VENDOR_SPECIFIC,
+			IMS_vendor_id_ETSI,
+			AVP_DONT_FREE_DATA,
+			__FUNCTION__);
+	}
+
+	if (URI.len){
+		cxdx_add_avp_list(&list,
+			URI.s,URI.len,
+			AVP_ETSI_Digest_URI,
+			AAA_AVP_FLAG_MANDATORY|AAA_AVP_FLAG_VENDOR_SPECIFIC,
+			IMS_vendor_id_ETSI,
+			AVP_DONT_FREE_DATA,
+			__FUNCTION__);
+	}
+
+	if (response.len){
+		cxdx_add_avp_list(&list,
+			response.s,response.len,
+			AVP_ETSI_Digest_Response,
+			AAA_AVP_FLAG_MANDATORY|AAA_AVP_FLAG_VENDOR_SPECIFIC,
+			IMS_vendor_id_ETSI,
+			AVP_DONT_FREE_DATA,
+			__FUNCTION__);
+	}
+
+	if (algorithm.len){
+		cxdx_add_avp_list(&list,
+			algorithm.s,algorithm.len,
+			AVP_ETSI_Digest_Algorithm,
+			AAA_AVP_FLAG_MANDATORY|AAA_AVP_FLAG_VENDOR_SPECIFIC,
+			IMS_vendor_id_ETSI,
+			AVP_DONT_FREE_DATA,
+			__FUNCTION__);
+	}
+
+	if (method.len){
+		cxdx_add_avp_list(&list,
+			method.s,method.len,
+			AVP_ETSI_Digest_Method,
+			AAA_AVP_FLAG_MANDATORY|AAA_AVP_FLAG_VENDOR_SPECIFIC,
+			IMS_vendor_id_ETSI,
+			AVP_DONT_FREE_DATA,
+			__FUNCTION__);
+	}
+
+	if (hash.len){
+		cxdx_add_avp_list(&list,
+			hash.s,hash.len,
+			AVP_ETSI_Digest_Entity_Body_Hash,
+			AAA_AVP_FLAG_MANDATORY|AAA_AVP_FLAG_VENDOR_SPECIFIC,
+			IMS_vendor_id_ETSI,
+			AVP_DONT_FREE_DATA,
+			__FUNCTION__);
+	}
+
+	if (!list.head) return group;
+	group = cdpb.AAAGroupAVPS(list);
+	
+	cdpb.AAAFreeAVPList(&list);
+	
+	return group;
+}
+
+/**
+ * Returns the User-Data from a Diameter message.
+ * @param msg - the Diameter message
+ * @returns the AVP payload on success or an empty string on error
+ */
+
+inline str cxdx_get_user_data(AAAMessage *msg)
+{	
+	return cxdx_get_avp(msg,
+		AVP_IMS_User_Data_Cx,
+		IMS_vendor_id_3GPP,
+		__FUNCTION__);
+}
+
+/**
+ * Returns the Charging-Information from a Diameter message.
+ * @param msg - the Diameter message
+ * @returns the AVP payload on success or an empty string on error
+ */
+inline int cxdx_get_charging_info(AAAMessage *msg,str *ccf1,str *ccf2,str *ecf1,str *ecf2)
+{		
+	AAA_AVP_LIST list;
+	AAA_AVP *avp;
+	str grp;
+	grp = cxdx_get_avp(msg,
+		AVP_IMS_Charging_Information,
+		IMS_vendor_id_3GPP,
+		__FUNCTION__);
+	if (!grp.s) return 0;
+
+	list = cdpb.AAAUngroupAVPS(grp);
+	
+	if (ccf1){
+		avp = cdpb.AAAFindMatchingAVPList(list,0,AVP_IMS_Primary_Charging_Collection_Function_Name,
+			IMS_vendor_id_3GPP,0);
+		if (avp) *ccf1 = avp->data;
+	}		
+	if (ccf2){
+		avp = cdpb.AAAFindMatchingAVPList(list,0,AVP_IMS_Secondary_Charging_Collection_Function_Name,
+			IMS_vendor_id_3GPP,0);
+		if (avp) *ccf2 = avp->data;
+	}		
+	if (ecf1){
+		avp = cdpb.AAAFindMatchingAVPList(list,0,AVP_IMS_Primary_Event_Charging_Function_Name,
+			IMS_vendor_id_3GPP,0);
+		if (avp) *ecf1 = avp->data;
+	}		
+	if (ecf2){
+		avp = cdpb.AAAFindMatchingAVPList(list,0,AVP_IMS_Secondary_Event_Charging_Function_Name,
+			IMS_vendor_id_3GPP,0);
+		if (avp) *ecf2 = avp->data;
+	}		
+		
+	cdpb.AAAFreeAVPList(&list);
+	return 1;		
+		
+}
+
+/**
+ * Creates and adds a Server-Assignment-Type AVP.
+ * @param msg - the Diameter message to add to.
+ * @param data - the value for the AVP payload
+ * @returns 1 on success or 0 on error
+ */
+inline int cxdx_add_server_assignment_type(AAAMessage *msg,unsigned int data)
+{
+	char x[4];
+	set_4bytes(x,data);
+	return 
+	cxdx_add_avp(msg,x,4,
+		AVP_IMS_Server_Assignment_Type,
+		AAA_AVP_FLAG_MANDATORY|AAA_AVP_FLAG_VENDOR_SPECIFIC,
+		IMS_vendor_id_3GPP,
+		AVP_DUPLICATE_DATA,
+		__FUNCTION__);
+}
+
+/**
+ * Creates and adds Userdata-Available AVP.
+ * @param msg - the Diameter message to add to.
+ * @param data - the value for the AVP payload
+ * @returns 1 on success or 0 on error
+ */
+inline int cxdx_add_userdata_available(AAAMessage *msg,unsigned int data)
+{
+	char x[4];
+	set_4bytes(x,data);
+	return 
+	cxdx_add_avp(msg,x,4,
+		AVP_IMS_User_Data_Already_Available,
+		AAA_AVP_FLAG_MANDATORY|AAA_AVP_FLAG_VENDOR_SPECIFIC,
+		IMS_vendor_id_3GPP,
+		AVP_DUPLICATE_DATA,
+		__FUNCTION__);
+}
+
+/**
+ * Finds out the next Public-Identity AVP from a Diameter message.
+ * @param msg - the Diameter message
+ * @param pos - position to resume search or NULL if to start from the first AVP 
+ * @param avp_code - the code of the AVP to look for
+ * @param vendor_id - the vendor id of the AVP to look for
+ * @param func - the name of the calling function for debugging purposes
+ * @returns the AVP payload on success or an empty string on error
+ */
+inline AAA_AVP* cxdx_get_next_public_identity(AAAMessage *msg,AAA_AVP* pos,int avp_code,int vendor_id,const char *func)
+{		
+	AAA_AVP *avp;
+	
+	avp = cdpb.AAAFindMatchingAVP(msg,pos,avp_code,vendor_id,0);
+	if (avp==0){
+		LM_DBG("INFO:%s: Failed finding avp\n",func);
+		return avp;
+	}
+	else 
+		return avp;
+}
+
+/**
+ * Returns the User-Name AVP from a Diameter message.
+ * @param msg - the Diameter message
+ * @returns the AVP payload on success or an empty string on error
+ */
+inline str cxdx_get_user_name(AAAMessage *msg)
+{
+	return cxdx_get_avp(msg,
+		AVP_User_Name,
+		0,
+		__FUNCTION__);
+}
+
+/**
+ * Creates and adds a Result-Code AVP.
+ * @param msg - the Diameter message to add to.
+ * @param data - the value for the AVP payload
+ * @returns 1 on success or 0 on error
+ */
+inline int cxdx_add_result_code(AAAMessage *msg,unsigned int data)
+{
+	char x[4];
+	set_4bytes(x,data);
+	return 
+	cxdx_add_avp(msg,x,4,
+		AVP_Result_Code,
+		AAA_AVP_FLAG_MANDATORY,
+		0,
+		AVP_DUPLICATE_DATA,
+		__FUNCTION__);
+}

+ 286 - 0
modules/registrar_scscf/cxdx_avp.h

@@ -0,0 +1,286 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * 
+ * The initial version of this code was written by Dragos Vingarzan
+ * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
+ * Fruanhofer Institute. It was and still is maintained in a separate
+ * branch of the original SER. We are therefore migrating it to
+ * Kamailio/SR and look forward to maintaining it from here on out.
+ * 2011/2012 Smile Communications, Pty. Ltd.
+ * ported/maintained/improved by 
+ * Jason Penton (jason(dot)penton(at)smilecoms.com and
+ * Richard Good (richard(dot)good(at)smilecoms.com) as part of an 
+ * effort to add full IMS support to Kamailio/SR using a new and
+ * improved architecture
+ * 
+ * NB: Alot of this code was originally part of OpenIMSCore,
+ * FhG Fokus. 
+ * Copyright (C) 2004-2006 FhG Fokus
+ * Thanks for great work! This is an effort to 
+ * break apart the various CSCF functions into logically separate
+ * components. We hope this will drive wider use. We also feel
+ * that in this way the architecture is more complete and thereby easier
+ * to manage in the Kamailio/SR environment
+ *
+ * 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 IS_CSCF_CXDX_AVP_H
+#define IS_CSCF_CXDX_AVP_H
+
+extern struct cdp_binds cdpb;            /**< Structure with pointers to cdp funcs 		*/
+extern struct tm_binds tmb;
+
+struct AAAMessage;
+struct AAA_AVP;
+struct sip_msg;
+
+/**
+ * Creates and adds a Destination-Realm AVP.
+ * @param msg - the Diameter message to add to.
+ * @param data - the value for the AVP payload
+ * @returns 1 on success or 0 on error
+ */
+inline int cxdx_add_destination_realm(AAAMessage *msg, str data);
+
+/**
+ * Creates and adds a Vendor-Specifig-Application-ID AVP.
+ * @param msg - the Diameter message to add to.
+ * @param vendor_id - the value of the vendor_id,
+ * @param auth_id - the authorization application id
+ * @param acct_id - the accounting application id
+ * @returns 1 on success or 0 on error
+ */
+inline int cxdx_add_vendor_specific_appid(AAAMessage *msg,unsigned int vendor_id,unsigned int auth_id,unsigned int acct_id);
+
+/**
+ * Creates and adds a Auth-Session-State AVP.
+ * @param msg - the Diameter message to add to.
+ * @param data - the value for the AVP payload
+ * @returns 1 on success or 0 on error
+ */
+inline int cxdx_add_auth_session_state(AAAMessage *msg,unsigned int data);
+
+/**
+ * Creates and adds a User-Name AVP.
+ * @param msg - the Diameter message to add to.
+ * @param data - the value for the AVP payload
+ * @returns 1 on success or 0 on error
+ */
+inline int cxdx_add_user_name(AAAMessage *msg,str data);
+
+/**
+ * Creates and adds a Public Identity AVP.
+ * @param msg - the Diameter message to add to.
+ * @param data - the value for the AVP payload
+ * @returns 1 on success or 0 on error
+ */
+inline int cxdx_add_public_identity(AAAMessage *msg,str data);
+
+/**
+ * Creates and adds a Visited-Network-ID AVP.
+ * @param msg - the Diameter message to add to.
+ * @param data - the value for the AVP payload
+ * @returns 1 on success or 0 on error
+ */
+inline int cxdx_add_visited_network_id(AAAMessage *msg,str data);
+
+/**
+ * Creates and adds a UAR-Flags AVP.
+ * @param msg - the Diameter message to add to.
+ * @param data - the value for the AVP payload
+ * @returns 1 on success or 0 on error
+ */
+inline int cxdx_add_UAR_flags(AAAMessage *msg, unsigned int sos_reg);
+
+/**
+ * Creates and adds a Authorization-Type AVP.
+ * @param msg - the Diameter message to add to.
+ * @param data - the value for the AVP payload
+ * @returns 1 on success or 0 on error
+ */
+inline int cxdx_add_authorization_type(AAAMessage *msg,unsigned int data);
+
+/**
+ * Returns the Result-Code AVP from a Diameter message.
+ * @param msg - the Diameter message
+ * @returns the AVP payload on success or an empty string on error
+ */
+inline int cxdx_get_result_code(AAAMessage *msg, int *data);
+
+/**
+ * Returns the Experimental-Result-Code AVP from a Diameter message.
+ * @param msg - the Diameter message
+ * @returns the AVP payload on success or an empty string on error
+ */
+inline int cxdx_get_experimental_result_code(AAAMessage *msg, int *data);
+
+/**
+ * Returns the Server-Name AVP from a Diameter message.
+ * @param msg - the Diameter message
+ * @returns the AVP payload on success or an empty string on error
+ */
+inline str cxdx_get_server_name(AAAMessage *msg);
+
+/**
+ * Returns the Capabilities from the grouped AVP from a Diameter message.
+ * @param msg - the Diameter message
+ * @param m - array to be filled with the retrieved mandatory capabilities
+ * @param m_cnt - size of the array above to be filled
+ * @param o - array to be filled with the retrieved optional capabilities
+ * @param o_cnt - size of the array above to be filled
+ * @returns 1 on success 0 on fail
+ */
+inline int cxdx_get_capabilities(AAAMessage *msg,int **m,int *m_cnt,int **o,int *o_cnt,	str **p,int *p_cnt);
+
+/**
+ * Creates and adds a SIP-Number-Auth-Items AVP.
+ * @param msg - the Diameter message to add to.
+ * @param data - the value for the AVP payload
+ * @returns 1 on success or 0 on error
+ */
+inline int cxdx_add_sip_number_auth_items(AAAMessage *msg,unsigned int data);
+
+/**
+ * Creates and adds a SIP-Auth-Data-Item AVP.
+ * @param msg - the Diameter message to add to.
+ * @param auth_scheme - the value for the authorization scheme AVP
+ * @param auth - the value for the authorization AVP
+ * @returns 1 on success or 0 on error
+ */
+inline int cxdx_add_sip_auth_data_item_request(AAAMessage *msg, str auth_scheme, str auth, str username, str realm,str method, str server_name);
+
+/**
+ * Creates and adds a Server-Name AVP.
+ * @param msg - the Diameter message to add to.
+ * @param data - the value for the AVP payload
+ * @returns 1 on success or 0 on error
+ */
+inline int cxdx_add_server_name(AAAMessage *msg,str data);
+
+/**
+ * Returns the SIP-Number-Auth-Items AVP from a Diameter message.
+ * @param msg - the Diameter message
+ * @returns the number or 0 on error
+ */
+inline int cxdx_get_sip_number_auth_items(AAAMessage *msg, int *data);
+
+/**
+ * Returns the Auth-Data-Item from a Diameter answer message.
+ * @param msg - the Diameter message
+ * @param auth_date - the string to fill with the authorization data
+ * @param item_number - the int to fill with the item number
+ * @param auth_scheme - the string to fill with the authentication scheme data
+ * @param authenticate - the string to fill with the authenticate data
+ * @param authorization - the string to fill with the authorization data
+ * @param ck - the string to fill with the cipher key
+ * @param ik - the string to fill with the integrity key
+ * @returns the AVP payload on success or an empty string on error
+ */
+int cxdx_get_auth_data_item_answer(AAAMessage *msg, AAA_AVP **auth_data,
+	int *item_number,str *auth_scheme,str *authenticate,str *authorization,
+	str *ck,str *ik,
+	str *ip, 
+	str *ha1, str *response_auth, str *digest_realm,
+	str *line_identifier);
+
+/**
+ * Creates and adds a ETSI_sip_authorization AVP.
+ * @param username - UserName
+ * @param realm - Realm
+ * @param nonce - Nonce
+ * @param URI - URI
+ * @param response - Response
+ * @param algoritm - Algorithm
+ * @param method - Method
+ * @param hash - Enitity-Body-Hash
+ * @returns grouped str on success
+ */
+str cxdx_ETSI_sip_authorization(str username, str realm, str nonce, str URI, str response, str algorithm, str method, str hash);
+
+/**
+ * Returns the User-Data from a Diameter message.
+ * @param msg - the Diameter message
+ * @returns the AVP payload on success or an empty string on error
+ */
+
+inline str cxdx_get_user_data(AAAMessage *msg);
+
+/**
+ * Returns the Charging-Information from a Diameter message.
+ * @param msg - the Diameter message
+ * @returns the AVP payload on success or an empty string on error
+ */
+inline int cxdx_get_charging_info(AAAMessage *msg,str *ccf1,str *ccf2,str *ecf1,str *ecf2);
+
+/**
+ * Creates and adds a Server-Assignment-Type AVP.
+ * @param msg - the Diameter message to add to.
+ * @param data - the value for the AVP payload
+ * @returns 1 on success or 0 on error
+ */
+inline int cxdx_add_server_assignment_type(AAAMessage *msg,unsigned int data);
+
+/**
+ * Creates and adds Userdata-Available AVP.
+ * @param msg - the Diameter message to add to.
+ * @param data - the value for the AVP payload
+ * @returns 1 on success or 0 on error
+ */
+inline int cxdx_add_userdata_available(AAAMessage *msg,unsigned int data);
+
+/**
+ * Finds out the next Public-Identity AVP from a Diameter message.
+ * @param msg - the Diameter message
+ * @param pos - position to resume search or NULL if to start from the first AVP 
+ * @param avp_code - the code of the AVP to look for
+ * @param vendor_id - the vendor id of the AVP to look for
+ * @param func - the name of the calling function for debugging purposes
+ * @returns the AVP payload on success or an empty string on error
+ */
+inline AAA_AVP* cxdx_get_next_public_identity(AAAMessage *msg,AAA_AVP* pos,int avp_code,int vendor_id,const char *func);
+
+/**
+ * Returns the User-Name AVP from a Diameter message.
+ * @param msg - the Diameter message
+ * @returns the AVP payload on success or an empty string on error
+ */
+inline str cxdx_get_user_name(AAAMessage *msg);
+
+/**
+ * Creates and adds a Result-Code AVP.
+ * @param msg - the Diameter message to add to.
+ * @param data - the value for the AVP payload
+ * @returns 1 on success or 0 on error
+ */
+inline int cxdx_add_result_code(AAAMessage *msg,unsigned int data);
+
+/**
+ * Transactional SIP response - tries to create a transaction if none found.
+ * @param msg - message to reply to
+ * @param code - the Status-code for the response
+ * @param text - the Reason-Phrase for the response
+ * @returns the tmb.t_repy() result
+ */
+int cscf_reply_transactional(struct sip_msg *msg, int code, char *text);
+
+
+#endif /* IS_CSCF_CXDX_AVP_H */

+ 37 - 0
modules/registrar_scscf/cxdx_callbacks.c

@@ -0,0 +1,37 @@
+/**
+ * Callback functions for RTR/PPR from the HSS
+ *
+ * Copyright (c) 2012 Carsten Bock, ng-voice GmbH
+ *
+ * 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 "cxdx_callbacks.h"
+#include "../../str.h"
+#include "../../dprint.h"
+
+/*int PPR_RTR_Event(void *parsed_message, int type, void *param) {
+	if (type & CXDX_PPR_RECEIVED) {
+		LM_ERR("Received a PPR-Request\n");
+		return 1;
+	}
+	if (type & CXDX_RTR_RECEIVED) {
+		LM_ERR("Received a RTR-Request\n");
+		return 1;
+	}
+	return 0;
+}*/

+ 31 - 0
modules/registrar_scscf/cxdx_callbacks.h

@@ -0,0 +1,31 @@
+/** 
+ * Callback functions for RTR/PPR from the HSS
+ *
+ * Copyright (c) 2012 Carsten Bock, ng-voice GmbH
+ *
+ * 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 CXDX_CALLBACKS_H
+#define CXDX_CALLBACKS_H
+
+/**
+ * Check, if a user-agent follows the indicated service-routes
+ */
+int PPR_RTR_Event(void *parsed_message, int type, void *param);
+
+#endif /* CXDX_CALLBACKS_H */

+ 344 - 0
modules/registrar_scscf/cxdx_sar.c

@@ -0,0 +1,344 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * 
+ * The initial version of this code was written by Dragos Vingarzan
+ * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
+ * Fruanhofer Institute. It was and still is maintained in a separate
+ * branch of the original SER. We are therefore migrating it to
+ * Kamailio/SR and look forward to maintaining it from here on out.
+ * 2011/2012 Smile Communications, Pty. Ltd.
+ * ported/maintained/improved by 
+ * Jason Penton (jason(dot)penton(at)smilecoms.com and
+ * Richard Good (richard(dot)good(at)smilecoms.com) as part of an 
+ * effort to add full IMS support to Kamailio/SR using a new and
+ * improved architecture
+ * 
+ * NB: Alot of this code was originally part of OpenIMSCore,
+ * FhG Fokus. 
+ * Copyright (C) 2004-2006 FhG Fokus
+ * Thanks for great work! This is an effort to 
+ * break apart the various CSCF functions into logically separate
+ * components. We hope this will drive wider use. We also feel
+ * that in this way the architecture is more complete and thereby easier
+ * to manage in the Kamailio/SR environment
+ *
+ * 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 "stats.h"
+#include "../cdp/cdp_load.h"
+#include "../../modules/tm/tm_load.h"
+#include "../../modules/dialog2/dlg_load.h"
+#include "../usrloc_scscf/usrloc.h"
+#include "api.h"
+#include "cxdx_avp.h"
+
+#include "reply.h"
+#include "rerrno.h"
+#include "reg_mod.h"
+#include "cxdx_sar.h"
+#include "save.h"
+#include "userdata_parser.h"
+#include "../../lib/kcore/statistics.h"
+#include "../../data_lump_rpl.h"
+#include "sip_msg.h"
+#include "regtime.h"
+
+int create_return_code(int result) {
+    int rc;
+    int_str avp_val, avp_name;
+    avp_name.s.s = "saa_return_code";
+    avp_name.s.len = 15;
+
+    //build avp spec for saa_return_code
+    avp_val.n = result;
+
+    rc = add_avp(AVP_NAME_STR, avp_name, avp_val);
+
+    if (rc < 0)
+        LM_ERR("couldnt create AVP\n");
+    else
+        LM_INFO("created AVP successfully : [%.*s] - [%d]\n", avp_name.s.len, avp_name.s.s, result);
+
+    return 1;
+}
+
+void free_saved_transaction_data(saved_transaction_t* data) {
+    if (!data)
+        return;
+
+    if (data->public_identity.s && data->public_identity.len) {
+        shm_free(data->public_identity.s);
+        data->public_identity.len = 0;
+    }
+    free_contact_buf(data->contact_header);
+    shm_free(data);
+}
+
+void async_cdp_callback(int is_timeout, void *param, AAAMessage *saa, long elapsed_msecs) {
+    struct cell *t = 0;
+    int rc = -1, experimental_rc = -1;
+    int result = CSCF_RETURN_TRUE;
+    saved_transaction_t* data = 0;
+
+    str xml_data = {0, 0}, ccf1 = {0, 0}, ccf2 = {0, 0}, ecf1 = {0, 0}, ecf2 = {0, 0};
+    ims_subscription* s = 0;
+
+    if (!param) {
+        LM_DBG("No transaction data this must have been called from usrloc cb impu deleted - just log result code and then exit");
+        cxdx_get_result_code(saa, &rc);
+        cxdx_get_experimental_result_code(saa, &experimental_rc);
+
+        if (saa) cdpb.AAAFreeMessage(&saa);
+
+        if (!rc && !experimental_rc) {
+            LM_ERR("bad SAA result code\n");
+            return;
+        }
+        switch (rc) {
+            case -1:
+                LM_DBG("Received Diameter error\n");
+                return;
+
+            case AAA_UNABLE_TO_COMPLY:
+                LM_DBG("Unable to comply\n");
+                return;
+
+            case AAA_SUCCESS:
+                LM_DBG("received AAA success\n");
+                return;
+
+            default:
+                LM_ERR("Unknown error\n");
+                return;
+        }
+
+    } else {
+        LM_DBG("There is transaction data this must have been called from save or assign server unreg");
+        data = (saved_transaction_t*) param;
+        if (tmb.t_lookup_ident(&t, data->tindex, data->tlabel) < 0) {
+            LM_ERR("t_continue: transaction not found\n");
+            rerrno = R_SAR_FAILED;
+            goto error_no_send;
+        }
+
+        get_act_time();
+
+        if (is_timeout) {
+        	update_stat(stat_sar_timeouts, 1);
+            LM_ERR("Transaction timeout - did not get SAA\n");
+            rerrno = R_SAR_FAILED;
+            goto error;
+        }
+        if (!saa) {
+            LM_ERR("Error sending message via CDP\n");
+            rerrno = R_SAR_FAILED;
+            goto error;
+        }
+
+        update_stat(sar_replies_received, 1);
+        update_stat(sar_replies_response_time, elapsed_msecs);
+
+        /* check and see that all the required headers are available and can be parsed */
+        if (parse_message_for_register(t->uas.request) < 0) {
+            LM_ERR("Unable to parse register message correctly\n");
+            rerrno = R_SAR_FAILED;
+            goto error;
+        }
+
+        cxdx_get_result_code(saa, &rc);
+        cxdx_get_experimental_result_code(saa, &experimental_rc);
+        cxdx_get_charging_info(saa, &ccf1, &ccf2, &ecf1, &ecf2);
+
+        if (!rc && !experimental_rc) {
+            LM_ERR("bad SAA result code\n");
+            rerrno = R_SAR_FAILED;
+            goto error;
+        }
+
+        switch (rc) {
+            case -1:
+                LM_DBG("Received Diameter error\n");
+                rerrno = R_SAR_FAILED;
+                goto error;
+
+            case AAA_UNABLE_TO_COMPLY:
+                LM_DBG("Unable to comply\n");
+                rerrno = R_SAR_FAILED;
+                goto error;
+
+            case AAA_SUCCESS:
+                LM_DBG("received AAA success\n");
+                break;
+
+            default:
+                LM_ERR("Unknown error\n");
+                rerrno = R_SAR_FAILED;
+                goto error;
+        }
+        //success
+        //if this is from a save (not a server assign unreg) and expires is zero we don't update usrloc as this is a dereg and usrloc was updated previously
+        if (data->sar_assignment_type != AVP_IMS_SAR_UNREGISTERED_USER && data->expires == 0) {
+            LM_DBG("no need to update usrloc - already done for de-reg\n");
+            result = CSCF_RETURN_TRUE;
+            goto success;
+        }
+
+        xml_data = cxdx_get_user_data(saa);
+        /*If there is XML user data we must be able to parse it*/
+        if (xml_data.s && xml_data.len > 0) {
+            LM_DBG("Parsing user data string from SAA\n");
+            s = parse_user_data(xml_data);
+            if (!s) {
+                LM_ERR("Unable to parse user data XML string\n");
+                rerrno = R_SAR_FAILED;
+                goto error;
+            }
+            LM_DBG("Successfully parse user data XML\n");
+        } else {
+            if (data->require_user_data) {
+                LM_ERR("We require User data for this assignment/register and none was supplied\n");
+                rerrno = R_SAR_FAILED;
+                result = CSCF_RETURN_FALSE;
+                goto done;
+            }
+        }
+
+        if (data->sar_assignment_type == AVP_IMS_SAR_REGISTRATION || data->sar_assignment_type == AVP_IMS_SAR_RE_REGISTRATION) {
+            if (build_p_associated_uri(s) != 0) {
+                LM_ERR("Unable to build p_associated_uri\n");
+                rerrno = R_SAR_FAILED;
+                goto error;
+            }
+        }
+
+        //here we update the contacts and also build the new contact header for the 200 OK reply
+        if (update_contacts_new(t->uas.request, data->domain, &data->public_identity, data->sar_assignment_type, &s, &ccf1, &ccf2, &ecf1, &ecf2, &data->contact_header) <= 0) {
+            LM_ERR("Error processing REGISTER\n");
+            rerrno = R_SAR_FAILED;
+            goto error;
+        }
+        
+        LM_DBG("Updated contacts: %.*s\n", data->contact_header->data_len, data->contact_header->buf);
+        
+    }
+
+success:
+    update_stat(accepted_registrations, 1);
+
+done:
+    reg_send_reply_transactional(t->uas.request, data->contact_header, t);
+    LM_DBG("DBG:SAR Async CDP callback: ... Done resuming transaction\n");
+    set_avp_list(AVP_TRACK_FROM | AVP_CLASS_URI, &t->uri_avps_from);
+    set_avp_list(AVP_TRACK_TO | AVP_CLASS_URI, &t->uri_avps_to);
+    set_avp_list(AVP_TRACK_FROM | AVP_CLASS_USER, &t->user_avps_from);
+    set_avp_list(AVP_TRACK_TO | AVP_CLASS_USER, &t->user_avps_to);
+    set_avp_list(AVP_TRACK_FROM | AVP_CLASS_DOMAIN, &t->domain_avps_from);
+    set_avp_list(AVP_TRACK_TO | AVP_CLASS_DOMAIN, &t->domain_avps_to);
+
+    create_return_code(result);
+
+    //free memory
+    if (saa) cdpb.AAAFreeMessage(&saa);
+    if (t) {
+        del_nonshm_lump_rpl(&t->uas.request->reply_lump);
+        tmb.unref_cell(t);
+    }
+    //free path vector pkg memory
+    reset_path_vector(t->uas.request);
+
+    tmb.t_continue(data->tindex, data->tlabel, data->act);
+    free_saved_transaction_data(data);
+    return;
+
+error:
+    reg_send_reply_transactional(t->uas.request, data->contact_header, t);
+
+error_no_send: //if we don't have the transaction then we can't send a transaction response
+    update_stat(rejected_registrations, 1);
+    //free memory
+    if (saa) cdpb.AAAFreeMessage(&saa);
+    if (t) {
+        del_nonshm_lump_rpl(&t->uas.request->reply_lump);
+        tmb.unref_cell(t);
+    }
+    tmb.t_continue(data->tindex, data->tlabel, data->act);
+    free_saved_transaction_data(data);
+    return;
+}
+
+/**
+ * Create and send a Server-Assignment-Request and returns the Answer received for it.
+ * This function performs the Server Assignment operation.
+ * @param msg - the SIP message to send for
+ * @parma public_identity - the public identity of the user
+ * @param server_name - local name of the S-CSCF to save on the HSS
+ * @param assignment_type - type of the assignment
+ * @param data_available - if the data is already available
+ * @returns the SAA
+ */
+int cxdx_send_sar(struct sip_msg *msg, str public_identity, str private_identity,
+        str server_name, int assignment_type, int data_available, saved_transaction_t* transaction_data) {
+    AAAMessage *sar = 0;
+    AAASession *session = 0;
+    unsigned int hash = 0, label = 0;
+
+    session = cdpb.AAACreateSession(0);
+
+    sar = cdpb.AAACreateRequest(IMS_Cx, IMS_SAR, Flag_Proxyable, session);
+    if (session) {
+        cdpb.AAADropSession(session);
+        session = 0;
+    }
+    if (!sar) goto error1;
+
+    if (!cxdx_add_destination_realm(sar, cxdx_dest_realm)) goto error1;
+
+    if (!cxdx_add_vendor_specific_appid(sar, IMS_vendor_id_3GPP, IMS_Cx, 0 /*IMS_Cx*/)) goto error1;
+    if (!cxdx_add_auth_session_state(sar, 1)) goto error1;
+
+    if (!cxdx_add_public_identity(sar, public_identity)) goto error1;
+    if (!cxdx_add_server_name(sar, server_name)) goto error1;
+    if (private_identity.len)
+        if (!cxdx_add_user_name(sar, private_identity)) goto error1;
+    if (!cxdx_add_server_assignment_type(sar, assignment_type)) goto error1;
+    if (!cxdx_add_userdata_available(sar, data_available)) goto error1;
+
+    if (msg && tmb.t_get_trans_ident(msg, &hash, &label) < 0) {
+        // it's ok cause we can call this async with a message for ul callbacks!
+        LM_DBG("SIP message without transaction... must be a ul callback\n");
+        //return 0;
+    }
+
+    if (cxdx_forced_peer.len)
+        cdpb.AAASendMessageToPeer(sar, &cxdx_forced_peer, (void*) async_cdp_callback, (void*) transaction_data);
+    else
+        cdpb.AAASendMessage(sar, (void*) async_cdp_callback, (void*) transaction_data);
+
+    return 0;
+
+error1: //Only free SAR IFF it has not been passed to CDP
+    if (sar) cdpb.AAAFreeMessage(&sar);
+
+    return -1;
+
+
+}

+ 94 - 0
modules/registrar_scscf/cxdx_sar.h

@@ -0,0 +1,94 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * 
+ * The initial version of this code was written by Dragos Vingarzan
+ * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
+ * Fruanhofer Institute. It was and still is maintained in a separate
+ * branch of the original SER. We are therefore migrating it to
+ * Kamailio/SR and look forward to maintaining it from here on out.
+ * 2011/2012 Smile Communications, Pty. Ltd.
+ * ported/maintained/improved by 
+ * Jason Penton (jason(dot)penton(at)smilecoms.com and
+ * Richard Good (richard(dot)good(at)smilecoms.com) as part of an 
+ * effort to add full IMS support to Kamailio/SR using a new and
+ * improved architecture
+ * 
+ * NB: Alot of this code was originally part of OpenIMSCore,
+ * FhG Fokus. 
+ * Copyright (C) 2004-2006 FhG Fokus
+ * Thanks for great work! This is an effort to 
+ * break apart the various CSCF functions into logically separate
+ * components. We hope this will drive wider use. We also feel
+ * that in this way the architecture is more complete and thereby easier
+ * to manage in the Kamailio/SR environment
+ *
+ * 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 CXDX_SAR_H
+#define CXDX_SAR_H
+
+
+typedef struct sar_param {
+	int type;
+        udomain_t* param;
+	cfg_action_t *paction;
+} sar_param_t;
+
+
+
+extern struct cdp_binds cdpb;
+extern str cxdx_forced_peer; /**< FQDN of the Diameter peer to send requests to */
+extern str cxdx_dest_realm;
+
+typedef struct saved_transaction {
+	unsigned int tindex;
+	unsigned int tlabel;
+	unsigned int ticks;
+	cfg_action_t *act;
+        int expires; //used to see if this is a dereg as then we don't need to touch usrloc! > 0 if not dereg - 0 id de-reg
+        int require_user_data;
+        int sar_assignment_type;
+        str public_identity;
+        udomain_t* domain;
+        contact_for_header_t* contact_header;//used to send the 200 OK with contacts after async callback for dereg where we don't rebuild the contacts
+} saved_transaction_t;
+
+/**
+ * Create and send a Server-Assignment-Request and returns the Answer received for it.
+ * This function performs the Server Assignment operation.
+ * @param msg - the SIP message to send for
+ * @parma public_identity - the public identity of the user
+ * @param server_name - local name of the S-CSCF to save on the HSS
+ * @param realm - Realm of the user
+ * @param assignment_type - type of the assignment
+ * @param data_available - if the data is already available
+ * @returns the SAA
+ */
+int cxdx_send_sar(struct sip_msg *msg, str public_identity, str private_identity,
+					str server_name,int assignment_type, int data_available, saved_transaction_t* transaction_data);
+
+void free_saved_transaction_data(saved_transaction_t* data);
+int create_return_code(int result);
+ 
+
+#endif
+

+ 248 - 0
modules/registrar_scscf/lookup.c

@@ -0,0 +1,248 @@
+/*
+ * $Id$
+ *
+ * Lookup contacts in usrloc
+ *
+ * Copyright (C) 2001-2003 FhG Fokus
+ *
+ * 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
+ *
+ * History:
+ * ---------
+ * 2003-03-12 added support for zombie state (nils)
+ */
+/*!
+ * \file
+ * \brief SIP registrar module - lookup contacts in usrloc
+ * \ingroup registrar
+ */
+
+
+#include <string.h>
+#include "../../ut.h"
+#include "../../dset.h"
+#include "../../str.h"
+#include "../../config.h"
+#include "../../action.h"
+#include "../../parser/parse_rr.h"
+#include "../usrloc_scscf/usrloc.h"
+#include "../../lib/ims/ims_getters.h"
+#include "common.h"
+#include "regtime.h"
+#include "reg_mod.h"
+#include "lookup.h"
+#include "config.h"
+
+#define allowed_method(_msg, _c) \
+	( !method_filtering || ((_msg)->REQ_METHOD)&((_c)->methods) )
+
+
+/*! \brief
+ * Lookup contact in the database and rewrite Request-URI
+ * \return: -1 : not found
+ *          -2 : found but method not allowed
+ *          -3 : error
+ */
+int lookup(struct sip_msg* _m, udomain_t* _d)
+{
+	impurecord_t* r;
+	str aor, uri;
+	ucontact_t* ptr;
+	int res;
+	int ret;
+	str path_dst;
+	flag_t old_bflags;
+
+
+	if (_m->new_uri.s) uri = _m->new_uri;
+	else uri = _m->first_line.u.request.uri;
+
+	if (extract_aor(&uri, &aor) < 0) {
+		LM_ERR("failed to extract address of record\n");
+		return -3;
+	}
+
+	get_act_time();
+
+	ul.lock_udomain(_d, &aor);
+	res = ul.get_impurecord(_d, &aor, &r);
+	if (res > 0) {
+		LM_DBG("'%.*s' Not found in usrloc\n", aor.len, ZSW(aor.s));
+		ul.unlock_udomain(_d, &aor);
+		return -1;
+	}
+
+	ptr = r->contacts;
+	ret = -1;
+	/* look first for an un-expired and suported contact */
+	while ( (ptr) &&
+	!(VALID_CONTACT(ptr,act_time) && (ret=-2) && allowed_method(_m,ptr)))
+		ptr = ptr->next;
+	if (ptr==0) {
+		/* nothing found */
+		goto done;
+	}
+
+	ret = 1;
+	if (ptr) {
+		if (rewrite_uri(_m, &ptr->c) < 0) {
+			LM_ERR("unable to rewrite Request-URI\n");
+			ret = -3;
+			goto done;
+		}
+
+		/* reset next hop address */
+		reset_dst_uri(_m);
+
+		/* If a Path is present, use first path-uri in favour of
+		 * received-uri because in that case the last hop towards the uac
+		 * has to handle NAT. - agranig */
+		if (ptr->path.s && ptr->path.len) {
+			if (get_path_dst_uri(&ptr->path, &path_dst) < 0) {
+				LM_ERR("failed to get dst_uri for Path\n");
+				ret = -3;
+				goto done;
+			}
+			if (set_path_vector(_m, &ptr->path) < 0) {
+				LM_ERR("failed to set path vector\n");
+				ret = -3;
+				goto done;
+			}
+			if (set_dst_uri(_m, &path_dst) < 0) {
+				LM_ERR("failed to set dst_uri of Path\n");
+				ret = -3;
+				goto done;
+			}
+		} else if (ptr->received.s && ptr->received.len) {
+			if (set_dst_uri(_m, &ptr->received) < 0) {
+				ret = -3;
+				goto done;
+			}
+		}
+
+		set_ruri_q(ptr->q);
+
+		old_bflags = 0;
+		getbflagsval(0, &old_bflags);
+		setbflagsval(0, old_bflags|ptr->cflags);
+
+		if (ptr->sock)
+			set_force_socket(_m, ptr->sock);
+
+		ptr = ptr->next;
+	}
+
+	/* Append branches if enabled */
+	if (!cfg_get(registrar, registrar_cfg, append_branches)) goto done;
+
+	for( ; ptr ; ptr = ptr->next ) {
+		if (VALID_CONTACT(ptr, act_time) && allowed_method(_m, ptr)) {
+			path_dst.len = 0;
+			if(ptr->path.s && ptr->path.len
+			&& get_path_dst_uri(&ptr->path, &path_dst) < 0) {
+				LM_ERR("failed to get dst_uri for Path\n");
+				continue;
+			}
+
+			/* The same as for the first contact applies for branches
+			 * regarding path vs. received. */
+			if (km_append_branch(_m,&ptr->c,path_dst.len?&path_dst:&ptr->received,
+			&ptr->path, ptr->q, ptr->cflags, ptr->sock) == -1) {
+				LM_ERR("failed to append a branch\n");
+				/* Also give a chance to the next branches*/
+				continue;
+			}
+		}
+	}
+
+done:
+	//ul.release_impurecord(r);
+	ul.unlock_udomain(_d, &aor);
+	return ret;
+}
+
+/*! \brief the impu_registered() function
+ * Return true if the AOR in the To Header is registered
+ */
+int impu_registered(struct sip_msg* _m, char* _t, char* _s)
+{
+	impurecord_t* r;
+	int res, ret=-1;
+
+	str impu;
+	impu = cscf_get_public_identity(_m);
+
+	LM_DBG("Looking for IMPU <%.*s>\n", impu.len, impu.s);
+
+	ul.lock_udomain((udomain_t*)_t, &impu);
+	res = ul.get_impurecord((udomain_t*)_t, &impu, &r);
+
+	if (res < 0) {
+		ul.unlock_udomain((udomain_t*)_t, &impu);
+		LM_ERR("failed to query usrloc for IMPU <%.*s>\n", impu.len, impu.s);
+		return ret;
+	}
+
+	if (res == 0) {
+		if (r->reg_state == IMPU_REGISTERED ) ret = 1;
+		ul.unlock_udomain((udomain_t*) _t, &impu);
+		LM_DBG("'%.*s' found in usrloc\n", impu.len, ZSW(impu.s));
+		return ret;
+	}
+
+	ul.unlock_udomain((udomain_t*)_t, &impu);
+	LM_DBG("'%.*s' not found in usrloc\n", impu.len, ZSW(impu.s));
+	return ret;
+}
+
+/*! \brief the term_impu_registered() function
+ * Return true if the AOR in the Request-URI  for the terminating user is registered
+ */
+int term_impu_registered(struct sip_msg* _m, char* _t, char* _s)
+{
+	str uri, aor;
+	impurecord_t* r;
+	int res;
+
+	if (_m->new_uri.s) uri = _m->new_uri;
+	else uri = _m->first_line.u.request.uri;
+
+	if (extract_aor(&uri, &aor) < 0) {
+		LM_ERR("failed to extract address of record\n");
+		return -1;
+	}
+
+	ul.lock_udomain((udomain_t*)_t, &aor);
+	res = ul.get_impurecord((udomain_t*)_t, &aor, &r);
+
+	if (res < 0) {
+		ul.unlock_udomain((udomain_t*)_t, &aor);
+		LM_ERR("failed to query for terminating IMPU <%.*s>\n", aor.len, aor.s);
+		return -1;
+	}
+
+	if (res == 0) {
+		//ul.release_impurecord(r);
+		ul.unlock_udomain((udomain_t*) _t, &aor);
+		LM_DBG("'%.*s' found in usrloc\n", aor.len, ZSW(aor.s));
+		return 1;
+	}
+
+	ul.unlock_udomain((udomain_t*)_t, &aor);
+	LM_DBG("'%.*s' not found in usrloc\n", aor.len, ZSW(aor.s));
+	return -1;
+}

+ 54 - 0
modules/registrar_scscf/lookup.h

@@ -0,0 +1,54 @@
+/*
+ * $Id$
+ *
+ * Lookup contacts in usrloc
+ *
+ * Copyright (C) 2001-2003 FhG Fokus
+ *
+ * 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
+ */
+
+/*!
+ * \file
+ * \brief SIP registrar module - lookup contacts in usrloc
+ * \ingroup registrar   
+ */  
+
+
+#ifndef LOOKUP_H
+#define LOOKUP_H
+
+#include "../../parser/msg_parser.h"
+#include "../usrloc_scscf/usrloc.h"
+
+
+/*! \brief
+ * Lookup a contact in usrloc and rewrite R-URI if found
+ */
+int lookup(struct sip_msg* _m, udomain_t* _d);
+
+
+/*! \brief
+ * Return true if the AOR in the Request-URI is registered,
+ * it is similar to lookup but registered neither rewrites
+ * the Request-URI nor appends branches
+ */
+int impu_registered(struct sip_msg* _m, char* _t, char* _s);
+int term_impu_registered(struct sip_msg* _m, char* _t, char* _s);
+
+
+#endif /* LOOKUP_H */

+ 115 - 0
modules/registrar_scscf/path.c

@@ -0,0 +1,115 @@
+/*
+ * $Id$
+ *
+ * Helper functions for Path support.
+ *
+ * Copyright (C) 2006 Andreas Granig <[email protected]>
+ *
+ * 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
+ *
+ */
+/*!
+ * \file
+ * \brief SIP registrar module - Helper functions for Path support
+ * \ingroup registrar   
+ */  
+
+
+#include "../../data_lump.h"
+#include "../../parser/parse_rr.h"
+#include "../../parser/parse_uri.h"
+#include "path.h"
+#include "reg_mod.h"
+
+/*! \brief
+ * Combines all Path HF bodies into one string.
+ */
+int build_path_vector(struct sip_msg *_m, str *path, str *received)
+{
+	static char buf[MAX_PATH_SIZE];
+	char *p;
+	struct hdr_field *hdr;
+	struct sip_uri puri;
+
+	rr_t *route = 0;
+
+	path->len = 0;
+	path->s = 0;
+	received->s = 0;
+	received->len = 0;
+
+	if(parse_headers(_m, HDR_EOH_F, 0) < 0) {
+		LM_ERR("failed to parse the message\n");
+		goto error;
+	}
+
+	for( hdr=_m->path,p=buf ; hdr ; hdr = next_sibling_hdr(hdr)) {
+		/* check for max. Path length */
+		if( p-buf+hdr->body.len+1 >= MAX_PATH_SIZE) {
+			LM_ERR("Overall Path body exceeds max. length of %d\n",
+					MAX_PATH_SIZE);
+			goto error;
+		}
+		if(p!=buf)
+			*(p++) = ',';
+		memcpy( p, hdr->body.s, hdr->body.len);
+		p +=  hdr->body.len;
+	}
+
+	if (p!=buf) {
+		/* check if next hop is a loose router */
+		if (parse_rr_body( buf, p-buf, &route) < 0) {
+			LM_ERR("failed to parse Path body, no head found\n");
+			goto error;
+		}
+		if (parse_uri(route->nameaddr.uri.s,route->nameaddr.uri.len,&puri)<0){
+			LM_ERR("failed to parse the first Path URI\n");
+			goto error;
+		}
+		if (!puri.lr.s) {
+			LM_ERR("first Path URI is not a loose-router, not supported\n");
+			goto error;
+		}
+		if (path_use_params) {
+			param_hooks_t hooks;
+			param_t *params;
+
+			if (parse_params(&(puri.params),CLASS_CONTACT,&hooks,&params)!=0){
+				LM_ERR("failed to parse parameters of first hop\n");
+				goto error;
+			}
+			if (hooks.contact.received)
+				*received = hooks.contact.received->body;
+			/*for (;params; params = params->next) {
+				if (params->type == P_RECEIVED) {
+					*received = hooks.contact.received->body;
+					break;
+				}
+			}*/
+			free_params(params);
+		}
+		free_rr(&route);
+	}
+
+	path->s = buf;
+	path->len = p-buf;
+	return 0;
+error:
+	if(route) free_rr(&route);
+	return -1;
+}
+

+ 43 - 0
modules/registrar_scscf/path.h

@@ -0,0 +1,43 @@
+/*
+ * $Id$
+ *
+ * Helper functions for Path support.
+ *
+ * Copyright (C) 2006 Andreas Granig <[email protected]>
+ *
+ * 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
+ *
+ */
+/*!
+ * \file
+ * \brief SIP registrar module - helper functions for Path support
+ * \ingroup registrar   
+ */  
+
+#ifndef REG_PATH_H
+#define REG_PATH_H
+
+#include "../../parser/msg_parser.h"
+
+/*! \brief
+ * Extracts all Path header bodies into one string and
+ * checks if first hop is a loose router. It also extracts
+ * the received-param of the first hop if path_use_received is 1.
+ */
+int build_path_vector(struct sip_msg *_m, str *path, str *received);
+
+#endif /* REG_PATH_H */

+ 689 - 0
modules/registrar_scscf/reg_mod.c

@@ -0,0 +1,689 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * 
+ * The initial version of this code was written by Dragos Vingarzan
+ * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
+ * Fruanhofer Institute. It was and still is maintained in a separate
+ * branch of the original SER. We are therefore migrating it to
+ * Kamailio/SR and look forward to maintaining it from here on out.
+ * 2011/2012 Smile Communications, Pty. Ltd.
+ * ported/maintained/improved by 
+ * Jason Penton (jason(dot)penton(at)smilecoms.com and
+ * Richard Good (richard(dot)good(at)smilecoms.com) as part of an 
+ * effort to add full IMS support to Kamailio/SR using a new and
+ * improved architecture
+ * 
+ * NB: Alot of this code was originally part of OpenIMSCore,
+ * FhG Fokus. 
+ * Copyright (C) 2004-2006 FhG Fokus
+ * Thanks for great work! This is an effort to 
+ * break apart the various CSCF functions into logically separate
+ * components. We hope this will drive wider use. We also feel
+ * that in this way the architecture is more complete and thereby easier
+ * to manage in the Kamailio/SR environment
+ *
+ * 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 "../../data_lump.h"
+#include "../../ip_addr.h"
+#include "../../ut.h"
+#include "../../sr_module.h"
+#include "../../timer.h"
+#include "../../dprint.h"
+#include "../../error.h"
+#include "../../socket_info.h"
+#include "../../pvar.h"
+#include "../usrloc_scscf/usrloc.h"
+#include "../../lib/kcore/statistics.h"
+#include "../../modules/sl/sl.h"
+#include "../../mod_fix.h"
+
+#include "save.h"
+#include "api.h"
+#include "lookup.h"
+#include "regpv.h"
+#include "reply.h"
+#include "reg_mod.h"
+#include "config.h"
+#include "server_assignment.h"
+#include "usrloc_cb.h"
+#include "userdata_parser.h"
+#include "cxdx_sar.h"
+#include "registrar_notify.h"
+#include "../cdp_avp/mod_export.h"
+
+MODULE_VERSION
+
+int * callback_singleton; /**< Cx callback singleton 								*/
+
+
+struct tm_binds tmb;
+
+struct cdp_binds cdpb;
+cdp_avp_bind_t *cdp_avp;
+
+
+usrloc_api_t ul; /*!< Structure containing pointers to usrloc functions*/
+
+char *scscf_user_data_dtd = 0; /* Path to "CxDataType.dtd" */
+char *scscf_user_data_xsd = 0; /* Path to "CxDataType_Rel6.xsd" or "CxDataType_Rel7.xsd" */
+int scscf_support_wildcardPSI = 0;
+char *scscf_name = "sip:scscf2.ims.smilecoms.com:6060"; /* default scscf_name - actual should be set via parameter*/
+int store_data_on_dereg = 0; /**< should we store SAR data on de-registration  */
+
+/* parameters storage */
+char* cxdx_dest_realm_s = "ims.smilecoms.com";
+str cxdx_dest_realm;
+
+//Only used if we want to force the Rx peer
+//Usually this is configured at a stack level and the first request uses realm routing
+char* cxdx_forced_peer_s = "";
+str cxdx_forced_peer;
+
+str scscf_name_str;
+str scscf_serviceroute_uri_str; /* Service Route URI */
+
+/*! \brief Module init & destroy function */
+static int mod_init(void);
+static int child_init(int);
+static void mod_destroy(void);
+static int w_save(struct sip_msg* _m, char* _d, char* mode, char* _cflags);
+static int w_assign_server_unreg(struct sip_msg* _m, char* _d, char* _direction);
+static int w_lookup(struct sip_msg* _m, char* _d, char* _p2);
+
+/*! \brief Fixup functions */
+static int domain_fixup(void** param, int param_no);
+static int assign_save_fixup3(void** param, int param_no);
+//static int save_fixup2(void** param, int param_no);
+//static int assign_fixup2(void** param, int param_no);
+static int unreg_fixup(void** param, int param_no);
+static int fetchc_fixup(void** param, int param_no);
+/*! \brief Functions */
+static int add_sock_hdr(struct sip_msg* msg, char *str, char *foo);
+
+int tcp_persistent_flag = -1; /*!< if the TCP connection should be kept open */
+int method_filtering = 0; /*!< if the looked up contacts should be filtered based on supported methods */
+int path_enabled = 0; /*!< if the Path HF should be handled */
+int path_mode = PATH_MODE_STRICT; /*!< if the Path HF should be inserted in the reply.
+ 			*   - STRICT (2): always insert, error if no support indicated in request
+ 			*   - LAZY   (1): insert only if support indicated in request
+ 			*   - OFF    (0): never insert */
+
+int path_use_params = 0; /*!< if the received- and nat-parameters of last Path uri should be used
+ 						 * to determine if UAC is nat'ed */
+
+char *aor_avp_param = 0; /*!< if instead of extacting the AOR from the request, it should be
+ 						 * fetched via this AVP ID */
+unsigned short aor_avp_type = 0;
+int_str aor_avp_name;
+
+/* Populate this AVP if testing for specific registration instance. */
+char *reg_callid_avp_param = 0;
+unsigned short reg_callid_avp_type = 0;
+int_str reg_callid_avp_name;
+
+char* rcv_avp_param = 0;
+unsigned short rcv_avp_type = 0;
+int_str rcv_avp_name;
+
+int sock_flag = -1;
+str sock_hdr_name = {0, 0};
+
+int subscription_default_expires = 3600; /**< the default value for expires if none found*/
+int subscription_min_expires = 10; /**< minimum subscription expiration time 		*/
+int subscription_max_expires = 1000000; /**< maximum subscription expiration time 		*/
+
+
+extern reg_notification_list *notification_list; /**< list of notifications for reg to be sent			*/
+
+#define RCV_NAME "received"
+str rcv_param = str_init(RCV_NAME);
+
+stat_var *accepted_registrations;
+stat_var *rejected_registrations;
+stat_var *max_expires_stat;
+stat_var *max_contacts_stat;
+stat_var *default_expire_stat;
+stat_var *default_expire_range_stat;
+/** SL API structure */
+sl_api_t slb;
+
+/*! \brief
+ * Exported PV
+ */
+static pv_export_t mod_pvs[] = {
+    {
+        {"ulc", sizeof ("ulc") - 1}, PVT_OTHER, pv_get_ulc, pv_set_ulc,
+        pv_parse_ulc_name, pv_parse_index, 0, 0
+    },
+    {
+        {0, 0}, 0, 0, 0, 0, 0, 0, 0
+    }
+};
+
+
+/*! \brief
+ * Exported functions
+ */
+static cmd_export_t cmds[] = {
+    {"save", (cmd_function) w_save, 1, assign_save_fixup3, 0, REQUEST_ROUTE | ONREPLY_ROUTE},
+    {"lookup", (cmd_function) w_lookup, 1, domain_fixup, 0, REQUEST_ROUTE | FAILURE_ROUTE},
+    {"term_impu_registered", (cmd_function) term_impu_registered, 1, domain_fixup, 0, REQUEST_ROUTE | FAILURE_ROUTE},
+    {"impu_registered", (cmd_function) impu_registered, 1, domain_fixup, 0, REQUEST_ROUTE | FAILURE_ROUTE},
+    {"assign_server_unreg", (cmd_function) w_assign_server_unreg, 2, assign_save_fixup3, 0, REQUEST_ROUTE},
+    {"add_sock_hdr", (cmd_function) add_sock_hdr, 1, fixup_str_null, 0, REQUEST_ROUTE},
+    {"unregister", (cmd_function) unregister, 2, unreg_fixup, 0, REQUEST_ROUTE | FAILURE_ROUTE},
+    {"reg_fetch_contacts", (cmd_function) pv_fetch_contacts, 3, fetchc_fixup, 0, REQUEST_ROUTE | FAILURE_ROUTE},
+    {"reg_free_contacts", (cmd_function) pv_free_contacts, 1, fixup_str_null, 0, REQUEST_ROUTE | FAILURE_ROUTE},
+    {"can_subscribe_to_reg", (cmd_function) can_subscribe_to_reg, 1, domain_fixup, 0, REQUEST_ROUTE},
+    {"subscribe_to_reg", (cmd_function) subscribe_to_reg, 1, domain_fixup, 0, REQUEST_ROUTE},
+    //{"bind_registrar", (cmd_function) bind_registrar, 0, 0, 0, 0},  TODO put this back in !
+    {0, 0, 0, 0, 0, 0}
+};
+
+
+/*! \brief
+ * Exported parameters
+ */
+static param_export_t params[] = {
+    {"default_expires", INT_PARAM, &default_registrar_cfg.default_expires},
+    {"default_expires_range", INT_PARAM, &default_registrar_cfg.default_expires_range},
+    {"min_expires", INT_PARAM, &default_registrar_cfg.min_expires},
+    {"max_expires", INT_PARAM, &default_registrar_cfg.max_expires},
+    {"em_default_expires", INT_PARAM, &default_registrar_cfg.em_default_expires},
+    {"em_min_expires", INT_PARAM, &default_registrar_cfg.em_max_expires},
+    {"em_min_expires", INT_PARAM, &default_registrar_cfg.em_min_expires},
+
+    {"default_q", INT_PARAM, &default_registrar_cfg.default_q},
+    {"append_branches", INT_PARAM, &default_registrar_cfg.append_branches},
+    {"case_sensitive", INT_PARAM, &default_registrar_cfg.case_sensitive},
+    {"realm_prefix", STR_PARAM, &default_registrar_cfg.realm_pref},
+
+    {"received_param", STR_PARAM, &rcv_param},
+    {"received_avp", STR_PARAM, &rcv_avp_param},
+    {"aor_avp", STR_PARAM, &aor_avp_param},
+    {"reg_callid_avp", STR_PARAM, &reg_callid_avp_param},
+    {"max_contacts", INT_PARAM, &default_registrar_cfg.max_contacts},
+    {"retry_after", INT_PARAM, &default_registrar_cfg.retry_after},
+    {"sock_flag", INT_PARAM, &sock_flag},
+    {"sock_hdr_name", STR_PARAM, &sock_hdr_name.s},
+    {"method_filtering", INT_PARAM, &method_filtering},
+    {"use_path", INT_PARAM, &path_enabled},
+    {"path_mode", INT_PARAM, &path_mode},
+    {"path_use_received", INT_PARAM, &path_use_params},
+    {"user_data_dtd", STR_PARAM, &scscf_user_data_dtd},
+    {"user_data_xsd", STR_PARAM, &scscf_user_data_xsd},
+    {"support_wildcardPSI", INT_PARAM, &scscf_support_wildcardPSI},
+    {"scscf_name", STR_PARAM, &scscf_name}, //TODO: need to set this to default
+    {"store_profile_dereg", INT_PARAM, &store_data_on_dereg},
+    {"cxdx_forced_peer", STR_PARAM, &cxdx_forced_peer_s},
+    {"cxdx_dest_realm", STR_PARAM, &cxdx_dest_realm_s},
+
+    {"subscription_default_expires", INT_PARAM, &subscription_default_expires},
+    {"subscription_min_expires", INT_PARAM, &subscription_min_expires},
+    {"subscription_max_expires", INT_PARAM, &subscription_max_expires},
+
+    {0, 0, 0}
+};
+
+/*! \brief We expose internal variables via the statistic framework below.*/
+stat_export_t mod_stats[] = {
+    {"max_expires", STAT_NO_RESET, &max_expires_stat},
+    {"max_contacts", STAT_NO_RESET, &max_contacts_stat},
+    {"default_expire", STAT_NO_RESET, &default_expire_stat},
+    {"default_expires_range", STAT_NO_RESET, &default_expire_range_stat},
+    {"accepted_regs", 0, &accepted_registrations},
+    {"rejected_regs", 0, &rejected_registrations},
+    {"sar_avg_response_time", STAT_IS_FUNC, (stat_var**) get_avg_sar_response_time},
+    {"sar_timeouts", 0, (stat_var**) & stat_sar_timeouts},
+    {0, 0, 0}
+};
+
+/*! \brief
+ * Module exports structure
+ */
+struct module_exports exports = {
+    "registrar_scscf",
+    DEFAULT_DLFLAGS, /* dlopen flags */
+    cmds, /* Exported functions */
+    params, /* Exported parameters */
+    mod_stats, /* exported statistics */
+    0, /* exported MI functions */
+    mod_pvs, /* exported pseudo-variables */
+    0, /* extra processes */
+    mod_init, /* module initialization function */
+    0,
+    mod_destroy, /* destroy function */
+    child_init, /* Per-child init function */
+};
+
+static str orig_prefix = {"sip:orig@", 9};
+
+/*! \brief
+ * Initialize parent
+ */
+
+static int mod_init(void) {
+    pv_spec_t avp_spec;
+    str s;
+    bind_usrloc_t bind_usrloc;
+    qvalue_t dq;
+
+    /*build the required strings */
+    scscf_name_str.s = scscf_name;
+    scscf_name_str.len = strlen(scscf_name);
+
+    scscf_serviceroute_uri_str.s =
+            (char*) pkg_malloc(orig_prefix.len + scscf_name_str.len);
+
+    if (!scscf_serviceroute_uri_str.s) {
+        LM_ERR("Unable to allocate memory for service route uri\n");
+        return -1;
+    }
+
+    memcpy(scscf_serviceroute_uri_str.s, orig_prefix.s, orig_prefix.len);
+    scscf_serviceroute_uri_str.len = orig_prefix.len;
+    if (scscf_name_str.len > 4
+            && strncasecmp(scscf_name_str.s, "sip:", 4) == 0) {
+        memcpy(scscf_serviceroute_uri_str.s + scscf_serviceroute_uri_str.len,
+                scscf_name_str.s + 4, scscf_name_str.len - 4);
+        scscf_serviceroute_uri_str.len += scscf_name_str.len - 4;
+    } else {
+        memcpy(scscf_serviceroute_uri_str.s + scscf_serviceroute_uri_str.len,
+                scscf_name_str.s, scscf_name_str.len);
+        scscf_serviceroute_uri_str.len += scscf_name_str.len;
+    }
+
+    cxdx_forced_peer.s = cxdx_forced_peer_s;
+    cxdx_forced_peer.len = strlen(cxdx_forced_peer_s);
+
+    cxdx_dest_realm.s = cxdx_dest_realm_s;
+    cxdx_dest_realm.len = strlen(cxdx_dest_realm_s);
+
+    /* </build required strings> */
+
+#ifdef STATISTICS
+    /* register statistics */
+    if (register_module_stats(exports.name, mod_stats) != 0) {
+        LM_ERR("failed to register core statistics\n");
+        return -1;
+    }
+    if (!register_stats()) {
+        LM_ERR("Unable to register statistics\n");
+        return -1;
+    }
+#endif
+
+    /* bind the SL API */
+    if (sl_load_api(&slb) != 0) {
+        LM_ERR("cannot bind to SL API\n");
+        return -1;
+    }
+
+    /* load the TM API */
+    if (load_tm_api(&tmb) != 0) {
+        LM_ERR("can't load TM API\n");
+        return -1;
+    }
+
+    /* load the CDP API */
+    if (load_cdp_api(&cdpb) != 0) {
+        LM_ERR("can't load CDP API\n");
+        return -1;
+    }
+
+    cdp_avp = load_cdp_avp();
+    if (!cdp_avp) {
+        LM_ERR("can't load CDP_AVP API\n");
+        return -1;
+    }
+
+    rcv_param.len = strlen(rcv_param.s);
+
+    if (cfg_declare("registrar", registrar_cfg_def, &default_registrar_cfg,
+            cfg_sizeof(registrar), &registrar_cfg)) {
+        LM_ERR("Fail to declare the configuration\n");
+        return -1;
+    }
+
+    if (rcv_avp_param && *rcv_avp_param) {
+        s.s = rcv_avp_param;
+        s.len = strlen(s.s);
+        if (pv_parse_spec(&s, &avp_spec) == 0 || avp_spec.type != PVT_AVP) {
+            LM_ERR("malformed or non AVP %s AVP definition\n", rcv_avp_param);
+            return -1;
+        }
+
+        if (pv_get_avp_name(0, &avp_spec.pvp, &rcv_avp_name, &rcv_avp_type)
+                != 0) {
+            LM_ERR("[%s]- invalid AVP definition\n", rcv_avp_param);
+            return -1;
+        }
+    } else {
+        rcv_avp_name.n = 0;
+        rcv_avp_type = 0;
+    }
+    if (aor_avp_param && *aor_avp_param) {
+        s.s = aor_avp_param;
+        s.len = strlen(s.s);
+        if (pv_parse_spec(&s, &avp_spec) == 0 || avp_spec.type != PVT_AVP) {
+            LM_ERR("malformed or non AVP %s AVP definition\n", aor_avp_param);
+            return -1;
+        }
+
+        if (pv_get_avp_name(0, &avp_spec.pvp, &aor_avp_name, &aor_avp_type)
+                != 0) {
+            LM_ERR("[%s]- invalid AVP definition\n", aor_avp_param);
+            return -1;
+        }
+    } else {
+        aor_avp_name.n = 0;
+        aor_avp_type = 0;
+    }
+
+    if (reg_callid_avp_param && *reg_callid_avp_param) {
+        s.s = reg_callid_avp_param;
+        s.len = strlen(s.s);
+        if (pv_parse_spec(&s, &avp_spec) == 0 || avp_spec.type != PVT_AVP) {
+            LM_ERR("malformed or non AVP %s AVP definition\n", reg_callid_avp_param);
+            return -1;
+        }
+
+        if (pv_get_avp_name(0, &avp_spec.pvp, &reg_callid_avp_name,
+                &reg_callid_avp_type) != 0) {
+            LM_ERR("[%s]- invalid AVP definition\n", reg_callid_avp_param);
+            return -1;
+        }
+    } else {
+        reg_callid_avp_name.n = 0;
+        reg_callid_avp_type = 0;
+    }
+
+    bind_usrloc = (bind_usrloc_t) find_export("ul_bind_usrloc", 1, 0);
+    if (!bind_usrloc) {
+        LM_ERR("can't bind usrloc\n");
+        return -1;
+    }
+
+    /* Normalize default_q parameter */
+    dq = cfg_get(registrar, registrar_cfg, default_q);
+    if (dq != Q_UNSPECIFIED) {
+        if (dq > MAX_Q) {
+            LM_DBG("default_q = %d, lowering to MAX_Q: %d\n", dq, MAX_Q);
+            dq = MAX_Q;
+        } else if (dq < MIN_Q) {
+            LM_DBG("default_q = %d, raising to MIN_Q: %d\n", dq, MIN_Q);
+            dq = MIN_Q;
+        }
+    }
+    cfg_get(registrar, registrar_cfg, default_q) = dq;
+
+    if (bind_usrloc(&ul) < 0) {
+        return -1;
+    }
+
+    /*Register for callback of URECORD being deleted - so we can send a SAR*/
+
+    if (ul.register_ulcb == NULL) {
+        LM_ERR("Could not import ul_register_ulcb\n");
+        return -1;
+    }
+    
+    if (ul.register_ulcb(0, 0, UL_IMPU_INSERT, ul_impu_inserted, 0) < 0) {
+        LM_ERR("can not register callback for insert\n");
+        return -1;
+    }
+
+    if (sock_hdr_name.s) {
+        sock_hdr_name.len = strlen(sock_hdr_name.s);
+        if (sock_hdr_name.len == 0 || sock_flag == -1) {
+            LM_WARN("empty sock_hdr_name or sock_flag no set -> reseting\n");
+            pkg_free(
+                    sock_hdr_name.s);
+            sock_hdr_name.s = 0;
+            sock_hdr_name.len = 0;
+            sock_flag = -1;
+        }
+    } else if (sock_flag != -1) {
+        LM_WARN("sock_flag defined but no sock_hdr_name -> reseting flag\n");
+        sock_flag = -1;
+    }
+
+    /* fix the flags */
+    sock_flag = (sock_flag != -1) ? (1 << sock_flag) : 0;
+    tcp_persistent_flag =
+            (tcp_persistent_flag != -1) ? (1 << tcp_persistent_flag) : 0;
+
+    /* init the registrar notifications */
+    if (!notify_init()) return -1;
+
+    /* register the registrar notifications timer */
+    //Currently we do not use this - we send notifies immediately
+    //if (register_timer(notification_timer, notification_list, 5) < 0) return -1;
+
+    return 0;
+}
+
+static int child_init(int rank) {
+    if (rank == PROC_MAIN || rank == PROC_TCP_MAIN)
+        return 0;
+    if (rank == 1) {
+        /* init stats */
+        //TODO if parameters are modified via cfg framework do i change them?
+        update_stat(max_expires_stat, default_registrar_cfg.max_expires);
+        update_stat(max_contacts_stat, default_registrar_cfg.max_contacts);
+        update_stat(default_expire_stat, default_registrar_cfg.default_expires);
+    }
+    /* don't do anything for main process and TCP manager process */
+
+    /* don't do anything for main process and TCP manager process */
+    if (rank == PROC_MAIN || rank == PROC_TCP_MAIN)
+        return 0;
+
+    /* Init the user data parser */
+    if (!parser_init(scscf_user_data_dtd, scscf_user_data_xsd))
+        return -1;
+
+    return 0;
+}
+
+/*! \brief
+ * Wrapper to save(location)
+ */
+static int w_save(struct sip_msg* _m, char* _d, char* mode, char* _cflags) {
+    //return save(_m, (udomain_t*)_d);
+    return save(_m, _d);
+}
+
+static int w_assign_server_unreg(struct sip_msg* _m, char* _d, char* _direction) {
+    str direction;
+
+    direction.s = _direction;
+    direction.len = strlen(_direction);
+    //return assign_server_unreg(_m, (udomain_t*)_d, &direction);
+    return assign_server_unreg(_m, _d, &direction);
+
+}
+
+/*! \brief
+ * Wrapper to lookup(location)
+ */
+static int w_lookup(struct sip_msg* _m, char* _d, char* _p2) {
+    return lookup(_m, (udomain_t*) _d);
+}
+
+/*! \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;
+}
+
+/*! \brief
+ * Convert char* parameter to udomain_t* pointer
+ * Convert char* parameter to pv_elem_t* pointer
+ */
+static int unreg_fixup(void** param, int param_no) {
+    if (param_no == 1) {
+        return domain_fixup(param, 1);
+    } else if (param_no == 2) {
+        return fixup_spve_null(param, 1);
+    }
+    return 0;
+}
+
+/*
+ * Convert the char* parameters
+ */
+static int assign_save_fixup3(void** param, int param_no) {
+
+    if (strlen((char*) *param) <= 0) {
+        LM_ERR("empty parameter %d not allowed\n", param_no);
+        return -1;
+    }
+
+    if (param_no == 1) {
+        udomain_t* d;
+
+        if (ul.register_udomain((char*) *param, &d) < 0) {
+            LM_ERR("Erroring doing fixup on assign save");
+            return -1;
+        }
+
+        *param = (void*) d;
+
+        sar_param_t *ap;
+        ap = (sar_param_t*) pkg_malloc(sizeof (sar_param_t));
+        if (ap == NULL) {
+            LM_ERR("no more pkg\n");
+            return -1;
+        }
+        memset(ap, 0, sizeof (sar_param_t));
+        ap->paction = get_action_from_param(param, param_no);
+
+        ap->param = (udomain_t*) * param;
+
+        *param = (void*) ap;
+    }
+
+    return 0;
+}
+
+/*! \brief
+ * Convert char* parameter to udomain_t* pointer
+ * Convert char* parameter to pv_elem_t* pointer
+ * Convert char* parameter to str* pointer
+ */
+static int fetchc_fixup(void** param, int param_no) {
+    if (param_no == 1) {
+        return domain_fixup(param, 1);
+    } else if (param_no == 2) {
+        return fixup_spve_null(param, 1);
+    } else if (param_no == 3) {
+        return fixup_str_null(param, 1);
+    }
+    return 0;
+}
+
+static void mod_destroy(void) {
+    free_p_associated_uri_buf();
+    free_expired_contact_buf();
+}
+
+static int add_sock_hdr(struct sip_msg* msg, char *name, char *foo) {
+    struct socket_info* si;
+    struct lump* anchor;
+    str *hdr_name;
+    str hdr;
+    char *p;
+
+    hdr_name = (str*) name;
+    si = msg->rcv.bind_address;
+
+    if (parse_headers(msg, HDR_EOH_F, 0) == -1) {
+        LM_ERR("failed to parse message\n");
+        goto error;
+    }
+
+    anchor = anchor_lump(msg, msg->unparsed - msg->buf, 0, 0);
+    if (anchor == 0) {
+        LM_ERR("can't get anchor\n");
+        goto error;
+    }
+
+    hdr.len = hdr_name->len + 2 + si->sock_str.len + CRLF_LEN;
+    if ((hdr.s = (char*) pkg_malloc(hdr.len)) == 0) {
+        LM_ERR("no more pkg mem\n");
+        goto error;
+    }
+
+    p = hdr.s;
+    memcpy(p, hdr_name->s, hdr_name->len);
+    p += hdr_name->len;
+    *(p++) = ':';
+    *(p++) = ' ';
+
+    memcpy(p, si->sock_str.s, si->sock_str.len);
+    p += si->sock_str.len;
+
+    memcpy(p, CRLF, CRLF_LEN);
+    p += CRLF_LEN;
+
+    if (p - hdr.s != hdr.len) {
+        LM_CRIT("buffer overflow (%d!=%d)\n", (int) (long) (p - hdr.s), hdr.len);
+        goto error1;
+    }
+
+    if (insert_new_lump_before(anchor, hdr.s, hdr.len, 0) == 0) {
+        LM_ERR("can't insert lump\n");
+        goto error1;
+    }
+
+    return 1;
+error1:
+    pkg_free(hdr.s);
+error:
+    return -1;
+}
+
+void default_expires_stats_update(str* gname, str* name) {
+    update_stat(default_expire_stat, cfg_get(registrar, registrar_cfg, default_expires));
+}
+
+void max_expires_stats_update(str* gname, str* name) {
+    update_stat(max_expires_stat, cfg_get(registrar, registrar_cfg, max_expires));
+}
+
+void default_expires_range_update(str* gname, str* name) {
+    update_stat(default_expire_range_stat, cfg_get(registrar, registrar_cfg, default_expires_range));
+}

+ 117 - 0
modules/registrar_scscf/reg_mod.h

@@ -0,0 +1,117 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * 
+ * The initial version of this code was written by Dragos Vingarzan
+ * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
+ * Fruanhofer Institute. It was and still is maintained in a separate
+ * branch of the original SER. We are therefore migrating it to
+ * Kamailio/SR and look forward to maintaining it from here on out.
+ * 2011/2012 Smile Communications, Pty. Ltd.
+ * ported/maintained/improved by 
+ * Jason Penton (jason(dot)penton(at)smilecoms.com and
+ * Richard Good (richard(dot)good(at)smilecoms.com) as part of an 
+ * effort to add full IMS support to Kamailio/SR using a new and
+ * improved architecture
+ * 
+ * NB: Alot of this code was originally part of OpenIMSCore,
+ * FhG Fokus. 
+ * Copyright (C) 2004-2006 FhG Fokus
+ * Thanks for great work! This is an effort to 
+ * break apart the various CSCF functions into logically separate
+ * components. We hope this will drive wider use. We also feel
+ * that in this way the architecture is more complete and thereby easier
+ * to manage in the Kamailio/SR environment
+ *
+ * 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 REG_MOD_H
+#define REG_MOD_H
+
+#include "stats.h"
+#include "../../parser/msg_parser.h"
+#include "../../qvalue.h"
+#include "../../usr_avp.h"
+#include "../usrloc_scscf/usrloc.h"
+#include "../../modules/sl/sl.h"
+#include "../../modules/tm/tm_load.h"
+
+/* if DB support is used, this values must not exceed the
+ * storage capacity of the DB columns! See db/schema/entities.xml */
+#define CONTACT_MAX_SIZE       255
+#define RECEIVED_MAX_SIZE      255
+#define USERNAME_MAX_SIZE      64
+#define DOMAIN_MAX_SIZE        128
+#define CALLID_MAX_SIZE        255
+#define UA_MAX_SIZE            255
+
+#define PATH_MODE_STRICT	2
+#define PATH_MODE_LAZY		1
+#define PATH_MODE_OFF		0
+
+#define REG_SAVE_MEM_FL     (1<<0)
+#define REG_SAVE_NORPL_FL   (1<<1)
+#define REG_SAVE_REPL_FL    (1<<2)
+#define REG_SAVE_ALL_FL     ((1<<3)-1)
+
+#define MOD_NAME "registrar_scscf"
+/** Return and break the execution of routng script */
+#define CSCF_RETURN_BREAK	0
+/** Return true in the routing script */
+#define CSCF_RETURN_TRUE	1
+/** Return false in the routing script */
+#define CSCF_RETURN_FALSE -1
+/** Return error in the routing script */
+#define CSCF_RETURN_ERROR -2
+
+
+extern int nat_flag;
+extern int tcp_persistent_flag;
+extern int received_avp;
+
+extern unsigned short aor_avp_type;
+extern int_str aor_avp_name;
+extern unsigned short rcv_avp_type;
+extern int_str rcv_avp_name;
+extern unsigned short reg_callid_avp_type;
+extern int_str reg_callid_avp_name;
+
+extern str rcv_param;
+extern int method_filtering;
+extern int path_enabled;
+extern int path_mode;
+extern int path_use_params;
+
+extern str sock_hdr_name;
+extern int sock_flag;
+
+extern usrloc_api_t ul;/*!< Structure containing pointers to usrloc functions*/
+
+extern sl_api_t slb;
+
+extern stat_var *accepted_registrations;
+extern stat_var *rejected_registrations;
+extern stat_var *default_expire_stat;
+extern stat_var *max_expires_stat;
+
+
+#endif /* REG_MOD_H */

+ 1305 - 0
modules/registrar_scscf/registrar_notify.c

@@ -0,0 +1,1305 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * 
+ * The initial version of this code was written by Dragos Vingarzan
+ * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
+ * Fruanhofer Institute. It was and still is maintained in a separate
+ * branch of the original SER. We are therefore migrating it to
+ * Kamailio/SR and look forward to maintaining it from here on out.
+ * 2011/2012 Smile Communications, Pty. Ltd.
+ * ported/maintained/improved by 
+ * Jason Penton (jason(dot)penton(at)smilecoms.com and
+ * Richard Good (richard(dot)good(at)smilecoms.com) as part of an 
+ * effort to add full IMS support to Kamailio/SR using a new and
+ * improved architecture
+ * 
+ * NB: Alot of this code was originally part of OpenIMSCore,
+ * FhG Fokus. 
+ * Copyright (C) 2004-2006 FhG Fokus
+ * Thanks for great work! This is an effort to 
+ * break apart the various CSCF functions into logically separate
+ * components. We hope this will drive wider use. We also feel
+ * that in this way the architecture is more complete and thereby easier
+ * to manage in the Kamailio/SR environment
+ *
+ * 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 "registrar_notify.h"
+
+#include "reg_mod.h"
+#include "../../lib/ims/ims_getters.h"
+#include "regtime.h"
+#include "usrloc_cb.h"
+
+#include "../../lib/ims/useful_defs.h"
+
+/**
+ * Initializes the reg notifications list.
+ */
+reg_notification_list *notification_list = 0; //< List of pending notifications
+
+extern struct tm_binds tmb;
+
+extern int subscription_default_expires;
+extern int subscription_min_expires;
+extern int subscription_max_expires;
+
+extern str scscf_name_str;
+
+static str event_hdr = {"Event: reg\r\n", 12};
+static str maxfwds_hdr = {"Max-Forwards: 70\r\n", 18};
+static str subss_hdr1 = {"Subscription-State: ", 20};
+static str subss_hdr2 = {"\r\n", 2};
+static str ctype_hdr1 = {"Content-Type: ", 14};
+static str ctype_hdr2 = {"\r\n", 2};
+
+int notify_init() {
+    notification_list = shm_malloc(sizeof (reg_notification_list));
+    if (!notification_list) return 0;
+    memset(notification_list, 0, sizeof (reg_notification_list));
+    notification_list->lock = lock_alloc();
+    if (!notification_list->lock) return 0;
+    notification_list->lock = lock_init(notification_list->lock);
+    return 1;
+}
+
+/**
+ * Destroys the reg notifications list.
+ */
+void notify_destroy() {
+    reg_notification *n, *nn;
+    lock_get(notification_list->lock);
+    n = notification_list->head;
+    while (n) {
+        nn = n->next;
+        free_notification(n);
+        n = nn;
+    }
+    lock_destroy(notification_list->lock);
+    lock_dealloc(notification_list->lock);
+    shm_free(notification_list);
+}
+
+int can_subscribe_to_reg(struct sip_msg *msg, char *_t, char *str2) {
+
+    int ret = CSCF_RETURN_FALSE;
+    str presentity_uri = {0, 0};
+    str event;
+    str asserted_id;
+
+    ucontact_t* c = 0;
+    impurecord_t* r;
+    int res;
+
+    ims_public_identity *pi = 0;
+    int i, j;
+
+    LM_DBG("Checking if allowed to subscribe to event\n");
+
+    //check that this is a request
+    if (msg->first_line.type != SIP_REQUEST) {
+        LM_ERR("This message is not a request\n");
+        goto error;
+    }
+
+    //check that this is a subscribe request
+    if (msg->first_line.u.request.method.len != 9 ||
+            memcmp(msg->first_line.u.request.method.s, "SUBSCRIBE", 9) != 0) {
+        LM_ERR("This message is not a SUBSCRIBE\n");
+        goto error;
+    }
+
+    //check that this is a reg event - currently we only support reg event!
+    event = cscf_get_event(msg);
+    if (event.len != 3 || strncasecmp(event.s, "reg", 3) != 0) {
+        LM_ERR("Accepting only <Event: reg>. Found: <%.*s>\n",
+                event.len, event.s);
+        ret = CSCF_RETURN_FALSE;
+        goto done;
+    }
+
+    //get the target/presentity URI from the request uri
+    presentity_uri = cscf_get_public_identity_from_requri(msg);
+
+
+    asserted_id = cscf_get_asserted_identity(msg);
+    if (!asserted_id.len) {
+        LM_ERR("P-Asserted-Identity empty.\n");
+        ret = CSCF_RETURN_FALSE;
+        goto done;
+    }
+    LM_DBG("P-Asserted-Identity <%.*s>.\n",
+            asserted_id.len, asserted_id.s);
+
+    LM_DBG("Looking for IMPU in usrloc <%.*s>\n", presentity_uri.len, presentity_uri.s);
+
+    ul.lock_udomain((udomain_t*) _t, &presentity_uri);
+    res = ul.get_impurecord((udomain_t*) _t, &presentity_uri, &r);
+
+    if (res > 0) {
+        LM_DBG("'%.*s' Not found in usrloc\n", presentity_uri.len, presentity_uri.s);
+        ul.unlock_udomain((udomain_t*) _t, &presentity_uri);
+        ret = CSCF_RETURN_FALSE;
+        goto done;
+    }
+
+    //check if the asserted identity is in the same group as that presentity uri
+    if (r->public_identity.len == asserted_id.len &&
+            strncasecmp(r->public_identity.s, asserted_id.s, asserted_id.len) == 0) {
+        LM_DBG("Identity found as AOR <%.*s>\n",
+                presentity_uri.len, presentity_uri.s);
+        ul.unlock_udomain((udomain_t*) _t, &presentity_uri);
+        ret = CSCF_RETURN_TRUE;
+        goto done;
+    }
+
+    //check if asserted identity is in service profile
+    lock_get(r->s->lock);
+    if (r->s) {
+        for (i = 0; i < r->s->service_profiles_cnt; i++)
+            for (j = 0; j < r->s->service_profiles[i].public_identities_cnt; j++) {
+                pi = &(r->s->service_profiles[i].public_identities[j]);
+                if (!pi->barring &&
+                        pi->public_identity.len == asserted_id.len &&
+                        strncasecmp(pi->public_identity.s, asserted_id.s, asserted_id.len) == 0) {
+                    LM_DBG("Identity found in SP[%d][%d]\n",
+                            i, j);
+                    ret = CSCF_RETURN_TRUE;
+                    ul.unlock_udomain((udomain_t*) _t, &presentity_uri);
+                    lock_release(r->s->lock);
+                    goto done;
+                }
+            }
+    }
+    lock_release(r->s->lock);
+
+    //check if asserted is present in any of the path headers
+    c = r->contacts;
+
+    while (c) {
+        if (c->path.len) {
+            for (i = 0; i < c->path.len - asserted_id.len; i++)
+                if (strncasecmp(c->path.s + i, asserted_id.s, asserted_id.len) == 0) {
+                    LM_DBG("Identity found in Path <%.*s>\n",
+                            c->path.len, c->path.s);
+                    ret = CSCF_RETURN_TRUE;
+                    ul.unlock_udomain((udomain_t*) _t, &presentity_uri);
+                    goto done;
+                }
+        }
+        c = c->next;
+    }
+
+
+done:
+    if (presentity_uri.s) shm_free(presentity_uri.s);
+    return ret;
+error:
+    if (presentity_uri.s) shm_free(presentity_uri.s);
+    ret = CSCF_RETURN_ERROR;
+    return ret;
+}
+
+/*
+ * called to deliver new event into notification process
+ * return 0 on success. anything else failure
+ */
+int event_reg(udomain_t* _d, impurecord_t* r_passed, ucontact_t* c_passed, int event_type, str *presentity_uri, str *watcher_contact) {
+
+    str content = {0, 0};
+    impurecord_t* r;
+    int num_impus;
+    str* impu_list;
+
+    LM_DBG("Sending Reg event notifies\n");
+    LM_DBG("Switching on event type: %d", event_type);
+    switch (event_type) {
+        case IMS_REGISTRAR_NONE:
+            return 0;
+        case IMS_REGISTRAR_SUBSCRIBE:
+            if (r_passed || c_passed || !presentity_uri || !watcher_contact || !_d) {
+                LM_ERR("this is a subscribe called from cfg file: r_passed and c_passed should both be zero and presentity_uri, watcher_contact and _d should be valid for a subscribe");
+                return 0;
+            }
+            LM_DBG("Event type is IMS REGISTRAR SUBSCRIBE about to get reginfo_full");
+            //lets get IMPU list for presentity as well as register for callbacks (IFF its a new SUBSCRIBE)
+
+            ul.lock_udomain(_d, presentity_uri);
+            int res = ul.get_impurecord(_d, presentity_uri, &r);
+            if (res != 0) {
+                LM_WARN("Strange, '%.*s' Not found in usrloc\n", presentity_uri->len, presentity_uri->s);
+                ul.unlock_udomain(_d, presentity_uri);
+                //no point in continuing
+                return 1;
+            }
+
+            //get IMPU set from the presentity's subscription
+            res = ul.get_impus_from_subscription_as_string(_d, r,
+                    0/*all unbarred impus*/, &impu_list, &num_impus);
+            if (res != 0) {
+                LM_WARN("failed to get IMPUs from subscription\n");
+                ul.unlock_udomain(_d, presentity_uri);
+                if (impu_list) {
+                    pkg_free(impu_list);
+                }
+                return 1;
+            }
+            ul.unlock_udomain((udomain_t*) _d, presentity_uri);
+
+            content = generate_reginfo_full(_d, impu_list,
+                    num_impus);
+
+            if (impu_list) {
+                pkg_free(impu_list);
+            }
+
+            LM_DBG("About to ceate notification");
+
+            create_notifications(_d, r_passed, c_passed, presentity_uri, watcher_contact, content, event_type);
+            if (content.s) pkg_free(content.s);
+            //            if (send_now) notification_timer(0, 0);
+            return 0;
+            break;
+
+            //richard: we only use reg unreg expired and refresh
+        case IMS_REGISTRAR_CONTACT_UNREGISTERED:
+        case IMS_REGISTRAR_CONTACT_REGISTERED:
+        case IMS_REGISTRAR_CONTACT_REFRESHED:
+        case IMS_REGISTRAR_CONTACT_EXPIRED:
+            if (!r_passed || !c_passed || presentity_uri || watcher_contact || _d) {
+                LM_ERR("this is a contact change passed from ul callback: r_passed and c_passed should both be valid and presentity_uri, watcher_contact and _d should be 0 for ul callback");
+                return 0;
+            }
+
+            content = get_reginfo_partial(r_passed, c_passed, event_type);
+            create_notifications(_d, r_passed, c_passed, presentity_uri, watcher_contact, content, event_type);
+            if (content.s) pkg_free(content.s);
+            //                        if (send_now) notification_timer(0, 0);
+            return 1;
+
+        default:
+            LM_ERR("ERR:event_reg: Unknown event %d\n", event_type);
+            //            if (send_now) notification_timer(0, 0);
+            return 1;
+    }
+}
+
+/**
+ * Save this subscription.
+ * @param msg - the SIP SUBSCRIBE message
+ * @param str1 - not used
+ * @param str2 - not used
+ * @returns #CSCF_RETURN_TRUE if allowed, #CSCF_RETURN_FALSE if not, #CSCF_RETURN_ERROR on error
+ */
+int subscribe_to_reg(struct sip_msg *msg, char *_t, char *str2) {
+    int ret = CSCF_RETURN_FALSE;
+    int res;
+    str presentity_uri = {0, 0};
+    str event;
+    int event_i = IMS_EVENT_NONE;
+    int expires = 0, expires_time = 0;
+    str watcher_impu;
+    str watcher_contact;
+    impurecord_t* presentity_impurecord;
+    reg_subscriber *reg_subscriber;
+    subscriber_data_t subscriber_data;
+
+    int new_subscription = 1;
+    int event_type = IMS_REGISTRAR_NONE;
+
+    int rt = 0;
+
+    str callid = {0, 0};
+    str ftag = {0, 0};
+    str ttag = {0, 0};
+    str record_route = {0, 0};
+
+    int remote_cseq = 0;
+    int local_cseq = 0;
+
+    udomain_t* domain = (udomain_t*) _t;
+
+    LM_DBG("Saving SUBSCRIBE\n");
+
+    //check we have a valid transaction, if not, create one TODO
+
+    //check that this is a request
+    if (msg->first_line.type != SIP_REQUEST) {
+        LM_ERR("This message is not a request\n");
+        ret = CSCF_RETURN_FALSE;
+        goto error;
+    }
+
+    //check that this is a subscribe
+    if (msg->first_line.u.request.method.len != 9 ||
+            memcmp(msg->first_line.u.request.method.s, "SUBSCRIBE", 9) != 0) {
+        LM_ERR("This message is not a SUBSCRIBE\n");
+        ret = CSCF_RETURN_FALSE;
+        goto error;
+    }
+
+    //check that this is a reg event  - we currently only support reg event
+    event = cscf_get_event(msg);
+    if (event.len != 3 || strncasecmp(event.s, "reg", 3) != 0) {
+        LM_WARN("Accepting only <Event: reg>. Found: <%.*s>\n",
+                event.len, event.s);
+        ret = CSCF_RETURN_FALSE;
+        goto error;
+    }
+    if (event.len == 0 && strncasecmp(event.s, "reg", 3) == 0)
+        event_i = IMS_EVENT_REG;
+
+    //get callid, from and to tags to be able to identify dialog
+    //callid
+    callid = cscf_get_call_id(msg, 0);
+    if (callid.len <= 0 || !callid.s) {
+        LM_ERR("unable to get callid\n");
+        ret = CSCF_RETURN_FALSE;
+        goto error;
+    }
+    //ftag
+    if (!cscf_get_from_tag(msg, &ftag)) {
+        LM_ERR("Unable to get ftag\n");
+        ret = CSCF_RETURN_FALSE;
+        goto error;
+    }
+    //to tag - doesn't exist in request, must use tm to get it
+    tmb.t_get_reply_totag(msg, &ttag);
+    LM_DBG("Got to tag from sent response: %.*s", ttag.len, ttag.s);
+
+    //get cseq
+    remote_cseq = cscf_get_cseq(msg, 0);
+    local_cseq = remote_cseq + 1;
+
+    //get sockinfo_str
+    str sockinfo_str = msg->rcv.bind_address->sock_str;
+
+    //get record route
+    /*process record route and add it to a string*/
+    if (msg->record_route != NULL) {
+        rt = print_rr_body(msg->record_route, &record_route, 0, 0);
+        if (rt != 0) {
+            LM_ERR("Failed processing the record route [%d]\n", rt);
+            record_route.s = NULL;
+            record_route.len = 0;
+            ret = CSCF_RETURN_FALSE;
+            goto error;
+        }
+    }
+
+    //get the presentity uri from the request uri
+    presentity_uri = cscf_get_public_identity_from_requri(msg);
+
+    //get the watcher uri from the to header
+    cscf_get_from_uri(msg, &watcher_impu);
+
+    if (!watcher_impu.len) {
+        LM_ERR("Failed to get URI from To header.\n");
+        ret = CSCF_RETURN_FALSE;
+        goto error;
+    }
+    LM_DBG("To header URI (watcher URI) <%.*s>.\n",
+            watcher_impu.len, watcher_impu.s);
+
+    //get the watcher contact from contact header
+    watcher_contact = cscf_get_contact(msg);
+    if (!watcher_contact.len) {
+        LM_ERR("ERR: Contact empty.\n");
+        ret = CSCF_RETURN_FALSE;
+        goto error;
+    }
+    LM_DBG("watcher Contact <%.*s>.\n",
+            watcher_contact.len, watcher_contact.s);
+
+    //get expires
+    expires = cscf_get_expires_hdr(msg, 0);
+    if (expires == -1) expires = subscription_default_expires;
+
+    //build subscriber parcel for passing data around more easily
+    subscriber_data.callid = &callid;
+    subscriber_data.event = event_i;
+    subscriber_data.ftag = &ftag;
+    subscriber_data.ttag = &ttag;
+    subscriber_data.record_route = &record_route;
+    subscriber_data.sockinfo_str = &sockinfo_str;
+    subscriber_data.local_cseq = local_cseq;
+
+    if (expires > 0) {
+        LM_DBG("expires is more than zero - SUBSCRIBE");
+        event_type = IMS_REGISTRAR_SUBSCRIBE;
+
+        if (expires < subscription_min_expires) expires = subscription_min_expires;
+        if (expires > subscription_max_expires) expires = subscription_max_expires;
+
+        get_act_time();
+        expires_time = expires + act_time;
+
+        subscriber_data.expires = expires_time;
+
+
+        LM_DBG("Subscription expires time <%d> expiry length <%d>\n",
+                expires_time, expires);
+
+        LM_DBG("Received a new subscription (expires > 0), checking to see of impurecord for presentity exists\n");
+        ul.lock_udomain(domain, &presentity_uri);
+        res = ul.get_impurecord(domain, &presentity_uri, &presentity_impurecord);
+        if (res != 0) {
+            LM_DBG("usrloc does not have imprecord for presnetity being subscribed too, we should create one.... TODO\n");
+            ul.unlock_udomain(domain, &presentity_uri);
+            ret = CSCF_RETURN_FALSE;
+            goto error;
+        }
+
+        LM_DBG("Received impurecord for presentity being subscribed to [%.*s]\n", presentity_impurecord->public_identity.len, presentity_impurecord->public_identity.s);
+
+        res = ul.get_subscriber(presentity_impurecord, &presentity_uri, &watcher_contact, event_i, &reg_subscriber);
+        if (res != 0) {
+            LM_DBG("this must be a new subscriber, lets add it\n");
+            res = ul.add_subscriber(presentity_impurecord, &watcher_impu, &watcher_contact, &subscriber_data, &reg_subscriber);
+            if (res != 0) {
+                LM_ERR("Failed to add new subscription\n");
+                ul.unlock_udomain(domain, &presentity_uri);
+                ret = CSCF_RETURN_FALSE;
+                goto error;
+            }
+            //send full update on first registration
+
+            new_subscription = 1;
+
+        } else {
+
+            LM_DBG("this must be a re subscribe, lets update it\n");
+            res = ul.update_subscriber(presentity_impurecord, &watcher_impu, &watcher_contact, &expires_time, &reg_subscriber);
+            if (res != 1) {
+                LM_ERR("Failed to update subscription - expires is %d\n", expires_time);
+                ul.unlock_udomain(domain, &presentity_uri);
+                ret = CSCF_RETURN_FALSE;
+                goto error;
+            }
+            new_subscription = 0;
+        }
+
+        ul.unlock_udomain(domain, &presentity_uri);
+
+        ret = CSCF_RETURN_TRUE;
+        LM_DBG("Sending 200 OK to subscribing user");
+        subscribe_reply(msg, 200, MSG_REG_SUBSCRIBE_OK, &expires, &scscf_name_str);
+
+        //only do reg event on new subscriptions
+        if (new_subscription) {
+            if (event_reg(domain, 0, 0, event_type, &presentity_uri, &watcher_contact) != 0) {
+                LM_ERR("failed to send NOTIFYs for reg events\n");
+                ret = CSCF_RETURN_BREAK;
+                goto error;
+            } else {
+                LM_DBG("success sending NOTIFY\n");
+            }
+        }
+    } else {
+        event_type = IMS_REGISTRAR_UNSUBSCRIBE;
+        LM_DBG("expires is zero or less - UNSUBSCRIBE");
+
+        ul.lock_udomain(domain, &presentity_uri);
+        res = ul.get_impurecord(domain, &presentity_uri, &presentity_impurecord);
+        if (res != 0) {
+            LM_DBG("usrloc does not have imprecord for presnetity being subscribed too, we should create one.... TODO\n");
+            ul.unlock_udomain(domain, &presentity_uri);
+            goto error;
+        }
+        LM_DBG("Received impurecord for presentity being unsubscribed to [%.*s]\n", presentity_impurecord->public_identity.len, presentity_impurecord->public_identity.s);
+        //        //get the subscription if it exists
+        LM_DBG("Getting subscription s from usrloc");
+
+        res = ul.get_subscriber(presentity_impurecord, &presentity_uri, &watcher_contact, event_i, &reg_subscriber);
+        if (res != 0) {
+            LM_WARN("could not get subscriber\n");
+            ret = CSCF_RETURN_FALSE;
+            ul.unlock_udomain(domain, &presentity_uri);
+            goto error;
+        } else {
+            LM_DBG("subscription s exists");
+            LM_DBG("deleting subscriber from usrloc");
+            ul.external_delete_subscriber(reg_subscriber, (udomain_t*) _t);
+        }
+        ret = CSCF_RETURN_TRUE;
+        LM_DBG("Sending 200 OK to subscribing user");
+        subscribe_reply(msg, 200, MSG_REG_UNSUBSCRIBE_OK, &expires, &scscf_name_str);
+    }
+
+    //free memory
+    if (record_route.s) pkg_free(record_route.s);
+    if (presentity_uri.s) shm_free(presentity_uri.s);
+    return ret;
+error:
+    //free memory
+    if (record_route.s) pkg_free(record_route.s);
+    if (presentity_uri.s) shm_free(presentity_uri.s);
+
+    return ret;
+}
+
+
+
+str expires_hdr1 = {"Expires: ", 9};
+str expires_hdr2 = {"\r\n", 2};
+str contact_hdr1 = {"Contact: <", 10};
+str contact_hdr2 = {">\r\n", 3};
+
+/**
+ * Replies to a SUBSCRIBE and also adds the need headers.
+ * Path for example.
+ * @param msg - the SIP SUBSCRIBE message
+ * @param code - response code to send
+ * @param text - response phrase to send
+ * @param expires - expiration interval in seconds
+ * @param contact - contact to add to reply
+ * @returns the tmn.r_reply returned value value
+ */
+int subscribe_reply(struct sip_msg *msg, int code, char *text, int *expires, str * contact) {
+    str hdr = {0, 0};
+
+    if (expires) {
+        hdr.len = expires_hdr1.len + 12 + expires_hdr1.len;
+        hdr.s = pkg_malloc(hdr.len);
+        if (!hdr.s) {
+            LM_ERR("Error allocating %d bytes.\n",
+                    hdr.len);
+        } else {
+            hdr.len = 0;
+            STR_APPEND(hdr, expires_hdr1);
+            sprintf(hdr.s + hdr.len, "%d", *expires);
+            hdr.len += strlen(hdr.s + hdr.len);
+            STR_APPEND(hdr, expires_hdr2);
+            cscf_add_header_rpl(msg, &hdr);
+            pkg_free(hdr.s);
+        }
+    }
+
+    if (contact) {
+        hdr.len = contact_hdr1.len + contact->len + contact_hdr2.len;
+        hdr.s = pkg_malloc(hdr.len);
+        if (!hdr.s) {
+            LM_ERR("Error allocating %d bytes.\n",
+                    hdr.len);
+        } else {
+            hdr.len = 0;
+            STR_APPEND(hdr, contact_hdr1);
+            STR_APPEND(hdr, *contact);
+            STR_APPEND(hdr, contact_hdr2);
+            cscf_add_header_rpl(msg, &hdr);
+            pkg_free(hdr.s);
+        }
+    }
+
+    return tmb.t_reply(msg, code, text);
+
+}
+
+static str subs_terminated = {"terminated", 10};
+static str subs_active = {"active;expires=", 15};
+
+/**
+ * Creates notifications with the given content for all of the subscribers.
+ * @param r - r_public* to which it refers
+ * @param for_s - the r_subscriber*  to which it refers or NULL if for all
+ * @param content - the body content
+ * @param expires - the remaining subcription expiration time in seconds
+ */
+void create_notifications(udomain_t* _t, impurecord_t* r_passed, ucontact_t* c_passed, str *presentity_uri, str *watcher_contact, str content, int event_type) {
+
+    reg_notification *n;
+    reg_subscriber *s;
+    impurecord_t* r;
+
+    str subscription_state = {"active;expires=10000000000", 26},
+    content_type = {"application/reginfo+xml", 23};
+
+    int domain_locked = -1;
+
+    get_act_time();
+
+    int res;
+
+    LM_DBG("Creating notification");
+
+    if (r_passed && c_passed && !presentity_uri && !watcher_contact) {
+        LM_DBG("r_passed and c_passed are valid and presentity uri and watcher_contact is 0 - this must be a ul callback no need to lock domain");
+        r = r_passed;
+
+    } else {
+        LM_DBG("This must be a cfg file subscribe need to lock domain and get impurecord");
+        ul.lock_udomain(_t, presentity_uri);
+        res = ul.get_impurecord(_t, presentity_uri, &r);
+        if (res != 0) {
+            LM_WARN("No IMPU... ignoring\n");
+            ul.unlock_udomain(_t, presentity_uri);
+            return;
+        }
+        domain_locked = 1;
+    }
+
+    s = r->shead;
+    while (s) {
+        LM_DBG("Scrolling through reg subscribers for this IMPU");
+
+        if (s->expires > act_time) {
+            LM_DBG("Expires is greater than current time!");
+            subscription_state.s = pkg_malloc(32);
+            subscription_state.len = 0;
+            if (subscription_state.s) {
+
+                sprintf(subscription_state.s, "%.*s%ld", subs_active.len,
+                        subs_active.s, s->expires - act_time);
+                subscription_state.len = strlen(subscription_state.s);
+            }
+
+            LM_DBG("Subscription state: [%.*s]", subscription_state.len, subscription_state.s);
+
+        } else {
+            STR_PKG_DUP(subscription_state, subs_terminated, "pkg subs state");
+            LM_DBG("Expires is past than current time!");
+            LM_DBG("Subscription state: [%.*s]", subscription_state.len, subscription_state.s);
+        }
+
+        //This is a fix to ensure that when a user subscribes a full reg info is only sent to that UE
+        if (event_type == IMS_REGISTRAR_SUBSCRIBE) {
+            if ((watcher_contact->len == s->watcher_contact.len) && (strncasecmp(s->watcher_contact.s, watcher_contact->s, watcher_contact->len) == 0) &&
+                    (presentity_uri->len == s->presentity_uri.len) && (strncasecmp(s->presentity_uri.s, presentity_uri->s, presentity_uri->len) == 0)) {
+                LM_DBG("This is a fix to ensure that we only send full reg info XML to the UE that just subscribed");
+                LM_DBG("about to make new notification!");
+                n = new_notification(subscription_state, content_type, content,
+                        s->version++, s);
+                if (n) {
+                    //LM_DBG("Notification exists - about to add it");
+                    //add_notification(n);
+
+                    //Richard just gonna send it - not bother queueing etc.
+                    //TODO look at impact of this - sending straight away vs queueing and getting another process to send
+                    LM_DBG("About to send notification");
+                    send_notification(n);
+                    LM_DBG("About to free notification");
+                    free_notification(n);
+                } else {
+                    LM_DBG("Notification does not exist");
+                }
+            }
+        } else {
+            LM_DBG("about to make new notification!");
+            n = new_notification(subscription_state, content_type, content,
+                    s->version++, s);
+            if (n) {
+                //LM_DBG("Notification exists - about to add it");
+                //add_notification(n);
+
+                //Richard just gonna send it - not bother queueing etc.
+                //TODO look at impact of this - sending straight away vs queueing and getting another process to send
+                LM_DBG("About to send notification");
+                send_notification(n);
+                LM_DBG("About to free notification");
+                free_notification(n);
+            } else {
+                LM_DBG("Notification does not exist");
+            }
+        }
+        //}
+        s = s->next;
+
+        if (subscription_state.s) {
+            pkg_free(subscription_state.s);
+        }
+    }
+
+    if (domain_locked == 1) {
+        ul.unlock_udomain(_t, presentity_uri);
+    }
+
+    return;
+out_of_memory:
+    return;
+}
+
+/** Maximum reginfo XML size */
+#define MAX_REGINFO_SIZE 16384
+
+static str xml_start = {"<?xml version=\"1.0\"?>\n", 22};
+
+static str r_full = {"full", 4};
+static str r_partial = {"partial", 7};
+static str r_reginfo_s = {"<reginfo xmlns=\"urn:ietf:params:xml:ns:reginfo\" version=\"%s\" state=\"%.*s\">\n", 74};
+static str r_reginfo_e = {"</reginfo>\n", 11};
+
+static str r_active = {"active", 6};
+static str r_terminated = {"terminated", 10};
+static str registration_s = {"\t<registration aor=\"%.*s\" id=\"%p\" state=\"%.*s\">\n", 48};
+static str registration_e = {"\t</registration>\n", 17};
+
+//richard: we only use reg unreg refrsh and expire
+static str r_registered = {"registered", 10};
+static str r_refreshed = {"refreshed", 9};
+static str r_expired = {"expired", 7};
+static str r_unregistered = {"unregistered", 12};
+static str contact_s = {"\t\t<contact id=\"%p\" state=\"%.*s\" event=\"%.*s\" expires=\"%d\">\n", 59};
+static str contact_s_q = {"\t\t<contact id=\"%p\" state=\"%.*s\" event=\"%.*s\" expires=\"%d\" q=\"%.3f\">\n", 69};
+static str contact_e = {"\t\t</contact>\n", 13};
+
+static str uri_s = {"\t\t\t<uri>", 8};
+static str uri_e = {"</uri>\n", 7};
+
+/**
+ * Creates the full reginfo XML.
+ * @param pv - the r_public to create for
+ * @param event_type - event type
+ * @param subsExpires - subscription expiration
+ * @returns the str with the XML content
+ * if its a new subscription we do things like subscribe to updates on IMPU, etc
+ */
+str generate_reginfo_full(udomain_t* _t, str* impu_list, int num_impus) {
+    str x = {0, 0};
+    str buf, pad;
+    char bufc[MAX_REGINFO_SIZE], padc[MAX_REGINFO_SIZE];
+    impurecord_t *r;
+    ucontact_t *c;
+    int i, res;
+
+    buf.s = bufc;
+    buf.len = 0;
+    pad.s = padc;
+    pad.len = 0;
+
+    LM_DBG("Getting reginfo_full");
+
+    STR_APPEND(buf, xml_start);
+    sprintf(pad.s, r_reginfo_s.s, "%d", r_full.len, r_full.s);
+    pad.len = strlen(pad.s);
+    STR_APPEND(buf, pad);
+
+    for (i = 0; i < num_impus; i++) {
+        ul.lock_udomain(_t, &impu_list[i]);
+        LM_DBG("Scrolling through public identities, current one <%.*s>", impu_list[i].len, impu_list[i].s);
+        res = ul.get_impurecord(_t, &(impu_list[i]), &r);
+        if (res != 0) {
+            LM_WARN("impu disappeared, ignoring it\n");
+            continue;
+        }
+        LM_DBG("Retrieved IMPU record");
+
+        if (r->reg_state == IMPU_REGISTERED) {
+            LM_DBG("IMPU reg state is IMPU REGISTERED so putting in status active");
+            sprintf(pad.s, registration_s.s, r->public_identity.len,
+                    r->public_identity.s, r, r_active.len, r_active.s);
+        } else {
+            LM_DBG("IMPU reg state is not IMPU REGISTERED so putting in status terminated");
+            sprintf(pad.s, registration_s.s, r->public_identity.len,
+                    r->public_identity.s, r, r_terminated.len,
+                    r_terminated.s);
+        }
+        pad.len = strlen(pad.s);
+        STR_APPEND(buf, pad);
+        c = r->contacts;
+        LM_DBG("Scrolling through contact for this IMPU");
+        while (c) {
+            if (c->q != -1) {
+                LM_DBG("q value not equal to -1");
+                float q = (float) c->q / 1000;
+                sprintf(pad.s, contact_s_q.s, c, r_active.len, r_active.s,
+                        r_registered.len, r_registered.s, c->expires - act_time,
+                        q);
+            } else {
+                LM_DBG("q value equal to -1");
+                sprintf(pad.s, contact_s.s, c, r_active.len, r_active.s,
+                        r_registered.len, r_registered.s,
+                        c->expires - act_time);
+            }
+            pad.len = strlen(pad.s);
+            STR_APPEND(buf, pad);
+            STR_APPEND(buf, uri_s);
+
+            LM_DBG("Appending contact address: <%.*s>", c->c.len, c->c.s);
+
+            STR_APPEND(buf, (c->c));
+            STR_APPEND(buf, uri_e);
+
+            STR_APPEND(buf, contact_e);
+            c = c->next;
+        }
+        STR_APPEND(buf, registration_e);
+
+        ul.unlock_udomain(_t, &impu_list[i]);
+    }
+
+    STR_APPEND(buf, r_reginfo_e);
+
+    x.s = pkg_malloc(buf.len + 1);
+    if (x.s) {
+        x.len = buf.len;
+        memcpy(x.s, buf.s, buf.len);
+        x.s[x.len] = 0;
+    }
+
+    LM_DBG("Returned full reg-info: [%.*s]", x.len, x.s);
+
+    return x;
+}
+
+/**
+ * Creates the partial reginfo XML.
+ * @param pv - the r_public to create for
+ * @param pc - the r_contatct to create for
+ * @param event_type - event type
+ * @param subsExpires - subscription expiration
+ * @returns the str with the XML content
+ */
+
+str get_reginfo_partial(impurecord_t *r, ucontact_t *c, int event_type) {
+    str x = {0, 0};
+    str buf, pad;
+    char bufc[MAX_REGINFO_SIZE], padc[MAX_REGINFO_SIZE];
+    int expires = -1;
+
+    str state, event;
+
+    buf.s = bufc;
+    buf.len = 0;
+    pad.s = padc;
+    pad.len = 0;
+
+    STR_APPEND(buf, xml_start);
+    sprintf(pad.s, r_reginfo_s.s, "%d", r_partial.len, r_partial.s);
+    pad.len = strlen(pad.s);
+    STR_APPEND(buf, pad);
+
+
+    if (r) {
+        expires = c->expires - act_time;
+        if (r->contacts == c &&
+                //richard we only use expired and unregistered
+                (event_type == IMS_REGISTRAR_CONTACT_EXPIRED ||
+                event_type == IMS_REGISTRAR_CONTACT_UNREGISTERED)
+                )
+            sprintf(pad.s, registration_s.s, r->public_identity.len, r->public_identity.s, r, r_terminated.len, r_terminated.s);
+        else
+            sprintf(pad.s, registration_s.s, r->public_identity.len, r->public_identity.s, r, r_active.len, r_active.s);
+        pad.len = strlen(pad.s);
+        STR_APPEND(buf, pad);
+        if (c) {
+            switch (event_type) {
+
+                    //richard we only use registered and refreshed and expired and unregistered
+                case IMS_REGISTRAR_CONTACT_REGISTERED:
+                    state = r_active;
+                    event = r_registered;
+                    break;
+                case IMS_REGISTRAR_CONTACT_REFRESHED:
+                    state = r_active;
+                    event = r_refreshed;
+                    break;
+                case IMS_REGISTRAR_CONTACT_EXPIRED:
+                    state = r_terminated;
+                    event = r_expired;
+                    expires = 0;
+                    break;
+                case IMS_REGISTRAR_CONTACT_UNREGISTERED:
+                    state = r_terminated;
+                    event = r_unregistered;
+                    expires = 0;
+                    break;
+                default:
+                    state = r_active;
+                    event = r_registered;
+            }
+            if (c->q != -1) {
+                float q = (float) c->q / 1000;
+                sprintf(pad.s, contact_s_q.s, c, r_active.len, r_active.s, r_registered.len, r_registered.s, c->expires - act_time, q);
+            } else
+                sprintf(pad.s, contact_s.s, c, state.len, state.s, event.len, event.s, expires);
+            pad.len = strlen(pad.s);
+            STR_APPEND(buf, pad);
+            STR_APPEND(buf, uri_s);
+            STR_APPEND(buf, (c->c));
+            STR_APPEND(buf, uri_e);
+
+            STR_APPEND(buf, contact_e);
+            STR_APPEND(buf, registration_e);
+        }
+    }
+
+    STR_APPEND(buf, r_reginfo_e);
+
+
+    x.s = pkg_malloc(buf.len + 1);
+    if (x.s) {
+        x.len = buf.len;
+        memcpy(x.s, buf.s, buf.len);
+        x.s[x.len] = 0;
+    }
+    return x;
+}
+
+/**
+ * Callback for the UAC response to NOTIFY
+ */
+void uac_request_cb(struct cell *t, int type, struct tmcb_params * ps) {
+    LM_DBG("DBG:uac_request_cb: Type %d\n", type);
+}
+
+static int free_tm_dlg(dlg_t * td) {
+    if (td) {
+        if (td->route_set)
+            free_rr(&td->route_set);
+        pkg_free(td);
+    }
+    return 0;
+}
+
+/**
+ * Creates a NOTIFY message and sends it
+ * @param n - the r_notification to create the NOTIFY after
+ */
+
+
+void send_notification(reg_notification * n) {
+    str h = {0, 0};
+
+    uac_req_t uac_r;
+    dlg_t* td = NULL;
+
+    str method = {"NOTIFY", 6};
+
+    LM_DBG("DBG:send_notification: NOTIFY about <%.*s>\n", n->watcher_uri.len, n->watcher_uri.s);
+
+    h.len = 0;
+    h.len += contact_hdr1.len + scscf_name_str.len + contact_hdr2.len;
+    if (n->subscription_state.len) h.len += subss_hdr1.len + subss_hdr2.len + n->subscription_state.len;
+    h.len += event_hdr.len;
+    h.len += maxfwds_hdr.len;
+    if (n->content_type.len) h.len += ctype_hdr1.len + ctype_hdr2.len + n->content_type.len;
+    h.s = pkg_malloc(h.len);
+    if (!h.s) {
+        LM_ERR("ERR:send_notification: Error allocating %d bytes\n", h.len);
+        h.len = 0;
+    }
+
+
+    //Add SCSCF name as contact address
+    h.len = 0;
+    STR_APPEND(h, contact_hdr1);
+    STR_APPEND(h, scscf_name_str);
+    STR_APPEND(h, contact_hdr2);
+
+    STR_APPEND(h, event_hdr);
+    STR_APPEND(h, maxfwds_hdr);
+    if (n->subscription_state.len) {
+        STR_APPEND(h, subss_hdr1);
+        STR_APPEND(h, n->subscription_state);
+        STR_APPEND(h, subss_hdr2);
+    }
+    if (n->content_type.len) {
+        STR_APPEND(h, ctype_hdr1);
+        STR_APPEND(h, n->content_type);
+        STR_APPEND(h, ctype_hdr2);
+    }
+
+    /* construct the dlg_t structure */
+    td = build_dlg_t_from_notification(n);
+    if (td == NULL) {
+        LM_ERR("while building dlg_t structure\n");
+        free_tm_dlg(td);
+        return;
+    }
+
+
+    if (n->content.len) {
+
+        LM_DBG("Notification content exists - about to send notification with subscription state: [%.*s] content_type: [%.*s] content: [%.*s] : presentity_uri: [%.*s] watcher_uri: [%.*s]",
+                n->subscription_state.len, n->subscription_state.s, n->content_type.len, n->content_type.s, n->content.len, n->content.s,
+                n->presentity_uri.len, n->presentity_uri.s, n->watcher_uri.len, n->watcher_uri.s);
+
+        set_uac_req(&uac_r, &method, &h, &n->content, td, TMCB_LOCAL_COMPLETED,
+                uac_request_cb, 0);
+        tmb.t_request_within(&uac_r);
+    } else {
+        LM_DBG("o notification content - about to send notification with subscription state: [%.*s] presentity_uri: [%.*s] watcher_uri: [%.*s]",
+                n->subscription_state.len, n->subscription_state.s, n->presentity_uri.len, n->presentity_uri.s,
+                n->watcher_uri.len, n->watcher_uri.s);
+
+
+        set_uac_req(&uac_r, &method, &h, 0, td, TMCB_LOCAL_COMPLETED,
+                uac_request_cb, 0);
+        tmb.t_request_within(&uac_r);
+    }
+    if (h.s) pkg_free(h.s);
+    free_tm_dlg(td);
+
+}
+
+/**
+ * The Notification timer looks for unsent notifications and sends them.
+ *  - because not all events should wait until the notifications for them are sent
+ * @param ticks - the current time
+ * @param param - pointer to the domain_list
+ */
+void notification_timer(unsigned int ticks, void* param) {
+
+    LM_DBG("Running notification timer");
+
+    reg_notification *n = 0;
+    LM_DBG("Getting lock of notification list");
+    lock_get(notification_list->lock);
+    LM_DBG("Scrolling through list");
+    while (notification_list->head) {
+        n = notification_list->head;
+        LM_DBG("Taking notification out of list with watcher uri <%.*s> and presentity uri <%.*s>", n->watcher_uri.len, n->watcher_uri.s, n->presentity_uri.len, n->presentity_uri.s);
+        notification_list->head = n->next;
+        if (n->next) n->next->prev = 0;
+        else notification_list->tail = n->next;
+
+        LM_DBG("Releasing lock");
+        lock_release(notification_list->lock);
+
+        LM_DBG("About to send notification");
+        send_notification(n);
+        LM_DBG("About to free notification");
+        free_notification(n);
+        LM_DBG("Getting lock of notification list again");
+        lock_get(notification_list->lock);
+    }
+    LM_DBG("Releasing lock again");
+    lock_release(notification_list->lock);
+}
+
+/**
+ * Creates a notification based on the given parameters
+ * @param req_uri - the Request-URI for the NOTIFY
+ * @param uri - uri to send to
+ * @param subscription_state - the Subscription-State header value
+ * @param event - the event
+ * @param content_type - content type
+ * @param content - content
+ * @param dialog - dialog to send on
+ * @returns the r_notification or NULL on error
+ */
+reg_notification * new_notification(str subscription_state,
+        str content_type, str content, int version, reg_subscriber * r) {
+
+    reg_notification *n = 0;
+
+    str buf;
+    char bufc[MAX_REGINFO_SIZE];
+
+    sprintf(bufc, content.s, version);
+    buf.s = bufc;
+    buf.len = strlen(bufc);
+
+
+    int len;
+    char *p;
+
+    len = sizeof (reg_notification) + r->call_id.len + r->from_tag.len + r->to_tag.len + r->watcher_uri.len + r->watcher_contact.len +
+            r->record_route.len + r->sockinfo_str.len + r->presentity_uri.len + subscription_state.len + content_type.len + buf.len;
+
+    LM_DBG("Creating new notification");
+
+    n = (reg_notification*) shm_malloc(len);
+    if (n == 0) {
+        LM_ERR("no more shm mem (%d)\n", len);
+        return 0;
+    }
+    memset(n, 0, len);
+
+    p = (char*) (n + 1);
+
+    n->call_id.s = p;
+    n->call_id.len = r->call_id.len;
+    memcpy(p, r->call_id.s, r->call_id.len);
+    p += r->call_id.len;
+    LM_DBG("call id: [%.*s]", n->call_id.len, n->call_id.s);
+
+    n->from_tag.s = p;
+    n->from_tag.len = r->from_tag.len;
+    memcpy(p, r->from_tag.s, r->from_tag.len);
+    p += r->from_tag.len;
+    LM_DBG("from tag: [%.*s]", n->from_tag.len, n->from_tag.s);
+
+    n->to_tag.s = p;
+    n->to_tag.len = r->to_tag.len;
+    memcpy(p, r->to_tag.s, r->to_tag.len);
+    p += r->to_tag.len;
+    LM_DBG("to tag: [%.*s]", n->to_tag.len, n->to_tag.s);
+
+    n->watcher_uri.s = p;
+    n->watcher_uri.len = r->watcher_uri.len;
+    memcpy(p, r->watcher_uri.s, r->watcher_uri.len);
+    p += r->watcher_uri.len;
+    LM_DBG("watcher_uri: [%.*s]", n->watcher_uri.len, n->watcher_uri.s);
+
+    n->watcher_contact.s = p;
+    n->watcher_contact.len = r->watcher_contact.len;
+    memcpy(p, r->watcher_contact.s, r->watcher_contact.len);
+    p += r->watcher_contact.len;
+    LM_DBG("watcher_contact: [%.*s]", n->watcher_contact.len, n->watcher_contact.s);
+
+    n->record_route.s = p;
+    n->record_route.len = r->record_route.len;
+    memcpy(p, r->record_route.s, r->record_route.len);
+    p += r->record_route.len;
+    LM_DBG("record_route: [%.*s]", n->record_route.len, n->record_route.s);
+
+    n->sockinfo_str.s = p;
+    n->sockinfo_str.len = r->sockinfo_str.len;
+    memcpy(p, r->sockinfo_str.s, r->sockinfo_str.len);
+    p += r->sockinfo_str.len;
+    LM_DBG("sockinfo_str: [%.*s]", n->sockinfo_str.len, n->sockinfo_str.s);
+
+    n->presentity_uri.s = p;
+    n->presentity_uri.len = r->presentity_uri.len;
+    memcpy(p, r->presentity_uri.s, r->presentity_uri.len);
+    p += r->presentity_uri.len;
+    LM_DBG("presentity_uri: [%.*s]", n->presentity_uri.len, n->presentity_uri.s);
+
+    n->subscription_state.s = p;
+    n->subscription_state.len = subscription_state.len;
+    memcpy(p, subscription_state.s, subscription_state.len);
+    p += subscription_state.len;
+    LM_DBG("Notification subscription state: [%.*s]", n->subscription_state.len, n->subscription_state.s);
+
+    n->content_type.s = p;
+    n->content_type.len = content_type.len;
+    memcpy(p, content_type.s, content_type.len);
+    p += content_type.len;
+    LM_DBG("Notification content type: [%.*s]", n->content_type.len, n->content_type.s);
+
+    n->content.s = p;
+    n->content.len = buf.len;
+    memcpy(p, buf.s, buf.len);
+    p += buf.len;
+    LM_DBG("Notification content: [%.*s]", n->content.len, n->content.s);
+
+    if (p != (((char*) n) + len)) {
+        LM_CRIT("buffer overflow\n");
+        free_notification(n);
+        return 0;
+    }
+
+    return n;
+}
+
+/**
+ * Adds a notification to the list of notifications at the end (FIFO).
+ * @param n - the notification to be added
+ */
+void add_notification(reg_notification * n) {
+
+    LM_DBG("Adding notification");
+    if (!n) {
+        LM_DBG("Notification does not exist");
+        return;
+    } else {
+        LM_DBG("Notification exists");
+    }
+    LM_DBG("Adding to notification list");
+    lock_get(notification_list->lock);
+    n->next = 0;
+    n->prev = notification_list->tail;
+    if (notification_list->tail) notification_list->tail->next = n;
+    notification_list->tail = n;
+    if (!notification_list->head) notification_list->head = n;
+    lock_release(notification_list->lock);
+}
+
+/**
+ * Frees up space taken by a notification
+ * @param n - the notification to be freed
+ */
+void free_notification(reg_notification * n) {
+    if (n) {
+        shm_free(n);
+    }
+}
+
+dlg_t * build_dlg_t_from_notification(reg_notification * n) {
+    dlg_t* td = NULL;
+    td = (dlg_t*) pkg_malloc(sizeof (dlg_t));
+    if (td == NULL) {
+        LM_ERR("Error ran out of package memory");
+    }
+    memset(td, 0, sizeof (dlg_t));
+
+    LM_DBG("Building dlg_t structure");
+
+    td->loc_seq.value = n->local_cseq;
+    LM_DBG("local cseq %d", n->local_cseq);
+    td->loc_seq.is_set = 1;
+
+    td->id.call_id = n->call_id;
+    LM_DBG("call id %.*s", n->call_id.len, n->call_id.s);
+
+    td->id.rem_tag = n->from_tag;
+    LM_DBG("ftag %.*s", n->from_tag.len, n->from_tag.s);
+
+
+    td->id.loc_tag = n->to_tag;
+    LM_DBG("ttag %.*s", n->to_tag.len, n->to_tag.s);
+
+
+    td->loc_uri = n->presentity_uri;
+    LM_DBG("loc uri %.*s", n->presentity_uri.len, n->presentity_uri.s);
+
+    td->rem_target = n->watcher_contact;
+    LM_DBG("rem target %.*s", n->watcher_contact.len, n->watcher_contact.s);
+
+    td->rem_uri = n->watcher_uri;
+    LM_DBG("rem uri %.*s", n->watcher_uri.len, n->watcher_uri.s);
+
+    if (n->record_route.s && n->record_route.len) {
+        if (parse_rr_body(n->record_route.s, n->record_route.len,
+                &td->route_set) < 0) {
+            LM_ERR("in function parse_rr_body\n");
+            goto error;
+        }
+    }
+    td->state = DLG_CONFIRMED;
+
+    if (n->sockinfo_str.len) {
+        int port, proto;
+        str host;
+        char* tmp;
+        if ((tmp = as_asciiz(&n->sockinfo_str)) == NULL) {
+            LM_ERR("no pkg memory left\n");
+            goto error;
+        }
+        if (parse_phostport(tmp, &host.s,
+                &host.len, &port, &proto)) {
+            LM_ERR("bad sockinfo string\n");
+            pkg_free(tmp);
+            goto error;
+        }
+        pkg_free(tmp);
+        td->send_sock = grep_sock_info(
+                &host, (unsigned short) port, (unsigned short) proto);
+    }
+
+    return td;
+
+error:
+    free_tm_dlg(td);
+    return NULL;
+}

+ 142 - 0
modules/registrar_scscf/registrar_notify.h

@@ -0,0 +1,142 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * 
+ * The initial version of this code was written by Dragos Vingarzan
+ * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
+ * Fruanhofer Institute. It was and still is maintained in a separate
+ * branch of the original SER. We are therefore migrating it to
+ * Kamailio/SR and look forward to maintaining it from here on out.
+ * 2011/2012 Smile Communications, Pty. Ltd.
+ * ported/maintained/improved by 
+ * Jason Penton (jason(dot)penton(at)smilecoms.com and
+ * Richard Good (richard(dot)good(at)smilecoms.com) as part of an 
+ * effort to add full IMS support to Kamailio/SR using a new and
+ * improved architecture
+ * 
+ * NB: Alot of this code was originally part of OpenIMSCore,
+ * FhG Fokus. 
+ * Copyright (C) 2004-2006 FhG Fokus
+ * Thanks for great work! This is an effort to 
+ * break apart the various CSCF functions into logically separate
+ * components. We hope this will drive wider use. We also feel
+ * that in this way the architecture is more complete and thereby easier
+ * to manage in the Kamailio/SR environment
+ *
+ * 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 S_CSCF_REGISTRAR_NOTIFY_H_
+#define S_CSCF_REGISTRAR_NOTIFY_H_
+
+
+#include "../usrloc_scscf/usrloc.h"
+#include "../../locking.h"
+
+
+#define MSG_REG_SUBSCRIBE_OK "Subscription to REG saved"
+#define MSG_REG_UNSUBSCRIBE_OK "Subscription to REG dropped"
+
+
+
+typedef struct _reg_notification {
+    
+    str subscription_state; /**< Subscription-state header value*/
+    str content_type; /**< content type					*/
+    str content; /**< content						*/
+    
+    str watcher_contact;
+    str watcher_uri;
+    str presentity_uri;
+    
+    
+    unsigned int local_cseq;
+    str call_id;
+    str from_tag;
+    str to_tag;
+    str record_route;
+    str sockinfo_str;
+    
+    struct _reg_notification *next; /**< next notification in the list	*/
+    struct _reg_notification *prev; /**< previous notification in the list	*/
+} reg_notification;
+
+
+
+/** Notification List Structure */
+typedef struct {
+    gen_lock_t *lock; /**< lock for notifications ops		*/
+    reg_notification *head; /**< first notification in the list	*/
+    reg_notification *tail; /**< last notification in the list	*/
+} reg_notification_list;
+
+/** Events for subscriptions */
+enum {
+    IMS_EVENT_NONE, /**< Generic, no event					*/
+    IMS_EVENT_REG /**< Registration event					*/
+} IMS_Events;
+
+/** Event types for "reg" to generated notifications after */
+enum {
+    IMS_REGISTRAR_NONE, /**< no event - donothing 							*/
+    IMS_REGISTRAR_SUBSCRIBE, /**< Initial SUBSCRIBE - just send all data - this should not be treated though */
+    IMS_REGISTRAR_UNSUBSCRIBE, /**< Final UnSUBSCRIBE - just send a NOTIFY which will probably fail */
+    IMS_REGISTRAR_SUBSCRIBE_EXPIRED, /**< The subscribe has expired 						*/
+
+    //richard we only use contact reg, refresh, expired and unreg
+    IMS_REGISTRAR_CONTACT_REGISTERED, /**< Registered with REGISTER						*/
+    IMS_REGISTRAR_CONTACT_REFRESHED, /**< The expiration was refreshed					*/
+    IMS_REGISTRAR_CONTACT_EXPIRED, /**< A contact has expired and will be removed		*/
+    IMS_REGISTRAR_CONTACT_UNREGISTERED, /**< User unregistered with Expires 0				*/
+} IMS_Registrar_events;
+
+
+int can_subscribe_to_reg(struct sip_msg *msg, char *str1, char *str2);
+
+int subscribe_to_reg(struct sip_msg *msg, char *str1, char *str2);
+
+int subscribe_reply(struct sip_msg *msg, int code, char *text, int *expires, str *contact);
+
+int event_reg(udomain_t* _d, impurecord_t* r_passed, ucontact_t* c_passed, int event_type, str *presentity_uri, str *watcher_contact);
+
+
+str generate_reginfo_full(udomain_t* _t, str* impu_list, int new_subscription);
+
+str get_reginfo_partial(impurecord_t *r, ucontact_t *c, int event_type);
+
+void create_notifications(udomain_t* _t, impurecord_t* r_passed, ucontact_t* c_passed, str *presentity_uri, str *watcher_contact, str content, int event_type);
+
+void notification_timer(unsigned int ticks, void* param);
+
+void free_notification(reg_notification *n);
+
+void send_notification(reg_notification * n);
+
+void add_notification(reg_notification *n);
+reg_notification* new_notification(str subscription_state,
+        str content_type, str content, int version, reg_subscriber* r);
+
+dlg_t* build_dlg_t_from_notification(reg_notification* n);
+
+
+int notify_init();
+void notify_destroy();
+
+#endif //S_CSCF_REGISTRAR_NOTIFY_H_

+ 541 - 0
modules/registrar_scscf/regpv.c

@@ -0,0 +1,541 @@
+/*
+ * $Id$
+ *
+ * Export vontact attrs as PV
+ *
+ * Copyright (C) 2008 Daniel-Constantin Mierla (asipto.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
+ */
+
+/*!
+ * \file
+ * \brief SIP registrar module - export contacts as PV
+ * \ingroup registrar   
+ */  
+
+
+#include <string.h>
+#include "../../dprint.h"
+#include "../../mem/mem.h"
+#include "../../mod_fix.h"
+#include "../usrloc_scscf/usrloc.h"
+#include "reg_mod.h"
+#include "common.h"
+#include "regpv.h"
+
+typedef struct _regpv_profile {
+	str pname;
+	str domain;
+	str aor;
+	int flags;
+	unsigned int aorhash;
+	int nrc;
+	ucontact_t* contacts;
+	struct _regpv_profile *next;
+} regpv_profile_t;
+
+typedef struct _regpv_name {
+	regpv_profile_t *rp;
+	int attr;
+} regpv_name_t;
+
+static regpv_profile_t *_regpv_profile_list = NULL;
+
+static inline regpv_profile_t* regpv_get_profile(str *name)
+{
+	regpv_profile_t *rp;
+
+	if(name==NULL || name->len<=0)
+	{
+		LM_ERR("invalid parameters\n");
+		return NULL;
+	}
+
+	rp = _regpv_profile_list;
+	while(rp)
+	{
+		if(rp->pname.len == name->len
+				&& strncmp(rp->pname.s, name->s, name->len)==0)
+			return rp;
+		rp = rp->next;
+	}
+
+	rp = (regpv_profile_t*)pkg_malloc(sizeof(regpv_profile_t));
+	if(rp==NULL)
+	{
+		LM_ERR("no more pkg\n");
+		return NULL;
+	}
+	memset(rp, 0, sizeof(regpv_profile_t));
+	rp->pname.s = (char*)pkg_malloc((name->len+1)*sizeof(char));
+	if(rp->pname.s==NULL)
+	{
+		LM_ERR("no more pkg\n");
+		pkg_free(rp);
+		return NULL;
+	}
+	memcpy(rp->pname.s, name->s, name->len);
+	rp->pname.s[name->len] = '\0';
+	rp->pname.len = name->len;
+
+	rp->next = _regpv_profile_list;
+	_regpv_profile_list = rp;
+	return rp;
+}
+
+static void regpv_free_profile(regpv_profile_t *rpp)
+{
+	ucontact_t* ptr;
+	ucontact_t* ptr0;
+
+	if(rpp==NULL)
+		return;
+
+	ptr = rpp->contacts;
+	while(ptr)
+	{
+		ptr0 = ptr;
+		ptr = ptr->next;
+		pkg_free(ptr0);
+	}
+	if(rpp->domain.s!=NULL)
+	{
+		rpp->domain.s = 0;
+		rpp->domain.len = 0;
+	}
+	if(rpp->aor.s!=NULL)
+	{
+		pkg_free(rpp->aor.s);
+		rpp->aor.s = 0;
+		rpp->aor.len = 0;
+	}
+
+	rpp->flags = 0;
+	rpp->aorhash = 0;
+	rpp->nrc = 0;
+	rpp->contacts = 0;
+
+}
+
+void regpv_free_profiles(void)
+{
+	regpv_profile_t *rp;
+	regpv_profile_t *rp0;
+
+	rp = _regpv_profile_list;
+
+	while(rp)
+	{
+		if(rp->pname.s!=NULL)
+			pkg_free(rp->pname.s);
+		rp0 = rp;
+		regpv_free_profile(rp0);
+		rp = rp->next;
+	}
+	_regpv_profile_list = 0;
+}
+
+int pv_get_ulc(struct sip_msg *msg,  pv_param_t *param,
+		pv_value_t *res)
+{
+	regpv_name_t *rp;
+	regpv_profile_t *rpp;
+	ucontact_t *c;
+	int idx;
+	int i;
+
+	if(param==NULL)
+	{
+		LM_ERR("invalid params\n");
+		return -1;
+	}
+	rp = (regpv_name_t*)param->pvn.u.dname;
+	if(rp==NULL || rp->rp==NULL)
+	{
+		LM_DBG("no profile in params\n");
+		return pv_get_null(msg, param, res);
+	}
+	rpp = rp->rp;
+
+	if(rpp->flags==0 || rpp->contacts==NULL)
+	{
+		LM_DBG("profile not set or no contacts there\n");
+		return pv_get_null(msg, param, res);
+	}
+	/* get index */
+	if(pv_get_spec_index(msg, param, &idx, &i)!=0)
+	{
+		LM_ERR("invalid index\n");
+		return -1;
+	}
+
+	/* work only with positive indexes by now */
+	if(idx<0)
+		idx = 0;
+
+	/* get contact */
+	i = 0;
+	c = rpp->contacts;
+	while(rpp)
+	{
+		if(i == idx)
+			break;
+		i++;
+		c = c->next;
+	}
+	if(c==NULL)
+		return pv_get_null(msg, param, res);
+
+	switch(rp->attr)
+	{
+		case 0: /* aor */
+			return  pv_get_strval(msg, param, res, &rpp->aor);
+		break;
+		case 1: /* domain */
+			return  pv_get_strval(msg, param, res, &rpp->domain);
+		break;
+		case 2: /* aorhash */
+			return pv_get_uintval(msg, param, res, rpp->aorhash);
+		break;
+		case 3: /* addr */
+			return  pv_get_strval(msg, param, res, &c->c);
+		break;
+		case 4: /* path */
+			return  pv_get_strval(msg, param, res, &c->path);
+		break;
+		case 5: /* received */
+			return  pv_get_strval(msg, param, res, &c->received);
+		break;
+		case 6: /* expires */
+			return pv_get_uintval(msg, param, res,
+					(unsigned int)c->expires);
+		break;
+		case 7: /* callid */
+			return  pv_get_strval(msg, param, res, &c->callid);
+		break;
+		case 8: /* q */
+			return pv_get_sintval(msg, param, res, (int)c->q);
+		break;
+		case 9: /* cseq */
+			return pv_get_sintval(msg, param, res, c->cseq);
+		break;
+		case 10: /* flags */
+			return pv_get_uintval(msg, param, res, c->flags);
+		break;
+		case 11: /* cflags */
+			return pv_get_uintval(msg, param, res, c->cflags);
+		break;
+		case 12: /* user agent */
+			return  pv_get_strval(msg, param, res, &c->user_agent);
+		break;
+		case 14: /* socket */
+			if(c->sock==NULL)
+				return pv_get_null(msg, param, res);
+			return pv_get_strval(msg, param, res, &c->sock->sock_str);
+		break;
+		case 15: /* modified */
+			return pv_get_uintval(msg, param, res,
+					(unsigned int)c->last_modified);
+		break;
+		case 16: /* methods */
+			return pv_get_uintval(msg, param, res, c->methods);
+		break;
+		case 17: /* count */
+			return pv_get_sintval(msg, param, res, rpp->nrc);
+		break;
+	}
+
+	return pv_get_null(msg, param, res);
+}
+
+int pv_set_ulc(struct sip_msg* msg, pv_param_t *param,
+		int op, pv_value_t *val)
+{
+	return 0;
+}
+
+int pv_parse_ulc_name(pv_spec_p sp, str *in)
+{
+	str pn;
+	str pa;
+	regpv_name_t *rp;
+	regpv_profile_t *rpp;
+
+	if(sp==NULL || in==NULL || in->len<=0)
+		return -1;
+
+	pa.s = in->s;
+	while(pa.s < in->s + in->len - 2)
+	{
+		if(*pa.s=='=')
+			break;
+		pa.s++;
+	}
+	
+	if(pa.s >= in->s + in->len - 2)
+	{
+		LM_ERR("invalid contact pv name %.*s\n", in->len, in->s);
+		return -1;
+	}
+	if(*(pa.s+1) != '>')
+	{
+		LM_ERR("invalid contact pv name %.*s.\n", in->len, in->s);
+		return -1;
+	}
+
+	pn.s = in->s;
+	pn.len = pa.s - pn.s;
+
+	LM_DBG("get profile [%.*s]\n", pn.len, pn.s);
+
+	rpp = regpv_get_profile(&pn);
+	if(rpp==NULL)
+	{
+		LM_ERR("cannot get profile [%.*s]\n", pn.len, pn.s);
+		return -1;
+	}
+	pa.s += 2;
+	pa.len = in->s + in->len - pa.s;
+	LM_DBG("get attr [%.*s]\n", pa.len, pa.s);
+
+	rp = (regpv_name_t*)pkg_malloc(sizeof(regpv_name_t));
+	if(rp==0)
+	{
+		LM_ERR("no more pkg\n");
+		return -1;
+	}
+	memset(rp, 0, sizeof(regpv_name_t));
+	rp->rp = rpp;
+
+	switch(pa.len)
+	{
+		case 1: 
+			if(strncmp(pa.s, "q", 1)==0)
+				rp->attr = 8;
+			else goto error;
+		break;
+		case 3: 
+			if(strncmp(pa.s, "aor", 3)==0)
+				rp->attr = 0;
+			else goto error;
+		break;
+		case 4: 
+			if(strncmp(pa.s, "addr", 4)==0)
+				rp->attr = 3;
+			else if(strncmp(pa.s, "path", 4)==0)
+				rp->attr = 4;
+			else if(strncmp(pa.s, "cseq", 4)==0)
+				rp->attr = 9;
+			else goto error;
+		break;
+		case 5: 
+			if(strncmp(pa.s, "flags", 5)==0)
+				rp->attr = 10;
+			else if(strncmp(pa.s, "count", 5)==0)
+				rp->attr = 17;
+			else goto error;
+		break;
+		case 6: 
+			if(strncmp(pa.s, "domain", 6)==0)
+				rp->attr = 1;
+			else if(strncmp(pa.s, "callid", 6)==0)
+				rp->attr = 7;
+			else if(strncmp(pa.s, "cflags", 6)==0)
+				rp->attr = 11;
+			else if(strncmp(pa.s, "socket", 6)==0)
+				rp->attr = 14;
+			else goto error;
+		break;
+		case 7: 
+			if(strncmp(pa.s, "aorhash", 7)==0)
+				rp->attr = 2;
+			else if(strncmp(pa.s, "expires", 7)==0)
+				rp->attr = 6;
+			else if(strncmp(pa.s, "methods", 7)==0)
+				rp->attr = 16;
+			else goto error;
+		break;
+		case 8: 
+			if(strncmp(pa.s, "received", 8)==0)
+				rp->attr = 5;
+			else if(strncmp(pa.s, "modified", 8)==0)
+				rp->attr = 15;
+			else goto error;
+		break;
+		case 10: 
+			if(strncmp(pa.s, "user_agent", 10)==0)
+				rp->attr = 12;
+			else goto error;
+		break;
+		default:
+			goto error;
+	}
+	sp->pvp.pvn.u.dname = (void*)rp;
+	sp->pvp.pvn.type = PV_NAME_PVAR;
+
+	return 0;
+
+error:
+	LM_ERR("unknown contact attr name in %.*s\n", in->len, in->s);
+	return -1;
+}
+
+int pv_fetch_contacts(struct sip_msg* msg, char* table, char* uri,
+		char* profile)
+{
+	impurecord_t* r;
+	ucontact_t* ptr;
+	ucontact_t* ptr0;
+	ucontact_t* c0;
+	regpv_profile_t *rpp;
+	str aor = {0, 0};
+	str u = {0, 0};
+	int res;
+	int olen;
+	int ilen;
+	int n;
+	char *p;
+
+	rpp = regpv_get_profile((str*)profile);
+	if(rpp==0)
+	{
+		LM_ERR("invalid parameters\n");
+		return -1;
+	}
+
+	/* check and free if profile already set */
+	if(rpp->flags)
+		regpv_free_profile(rpp);
+
+	if(fixup_get_svalue(msg, (gparam_p)uri, &u)!=0 || u.len<=0)
+	{
+		LM_ERR("invalid uri parameter\n");
+		return -1;
+	}
+
+	if (extract_aor(&u, &aor) < 0) {
+		LM_ERR("failed to extract Address Of Record\n");
+		return -1;
+	}
+
+	/* copy aor and ul domain */
+	rpp->aor.s = (char*)pkg_malloc(aor.len*sizeof(char));
+	if(rpp->aor.s==NULL)
+	{
+		LM_ERR("no more pkg\n");
+		return -1;
+	}
+	memcpy(rpp->aor.s, aor.s, aor.len);
+	rpp->aor.len = aor.len;
+	rpp->domain = *((udomain_head_t*)table)->name;
+	rpp->flags = 1;
+
+	/* copy contacts */
+	ilen = sizeof(ucontact_t);
+	ul.lock_udomain((udomain_t*)table, &aor);
+	res = ul.get_impurecord((udomain_t*)table, &aor, &r);
+	if (res > 0) {
+		LM_DBG("'%.*s' Not found in usrloc\n", aor.len, ZSW(aor.s));
+		ul.unlock_udomain((udomain_t*)table, &aor);
+		return -1;
+	}
+
+	ptr = r->contacts;
+	ptr0 = NULL;
+	n = 0;
+	while(ptr)
+	{
+		olen = (ptr->c.len + ptr->received.len + ptr->path.len
+			+ ptr->callid.len + ptr->user_agent.len)*sizeof(char) + ilen;
+		c0 = (ucontact_t*)pkg_malloc(olen);
+		if(c0==NULL)
+		{
+			LM_ERR("no more pkg\n");
+			ul.unlock_udomain((udomain_t*)table, &aor);
+			goto error;
+		}
+		memcpy(c0, ptr, ilen);
+		c0->domain = NULL;
+		c0->aor = NULL;
+		c0->next = NULL;
+		c0->prev = NULL;
+
+		c0->c.s = (char*)c0 + ilen;
+		memcpy(c0->c.s, ptr->c.s, ptr->c.len);
+		c0->c.len = ptr->c.len;
+		p = c0->c.s + c0->c.len;
+		
+		if(ptr->received.s!=NULL)
+		{
+			c0->received.s = p;
+			memcpy(c0->received.s, ptr->received.s, ptr->received.len);
+			c0->received.len = ptr->received.len;
+			p += c0->received.len;
+		}
+		if(ptr->path.s!=NULL)
+		{
+			c0->path.s = p;
+			memcpy(c0->path.s, ptr->path.s, ptr->path.len);
+			c0->path.len = ptr->path.len;
+			p += c0->path.len;
+		}
+		c0->callid.s = p;
+		memcpy(c0->callid.s, ptr->callid.s, ptr->callid.len);
+		c0->callid.len = ptr->callid.len;
+		p += c0->callid.len;
+		if(ptr->user_agent.s!=NULL)
+		{
+			c0->user_agent.s = p;
+			memcpy(c0->user_agent.s, ptr->user_agent.s, ptr->user_agent.len);
+			c0->user_agent.len = ptr->user_agent.len;
+			p += c0->user_agent.len;
+		}
+
+		if(ptr0==NULL)
+		{
+			rpp->contacts = c0;
+		} else {
+			ptr0->next = c0;
+			c0->prev = ptr0;
+		}
+		n++;
+		ptr0 = c0;
+		ptr = ptr->next;
+	}
+	ul.unlock_udomain((udomain_t*)table, &aor);
+	rpp->nrc = n;
+	LM_DBG("fetched <%d> contacts for <%.*s> in [%.*s]\n",
+			n, aor.len, aor.s, rpp->pname.len, rpp->pname.s);
+	return 1;
+
+error:
+	regpv_free_profile(rpp);
+	return -1;
+}
+int pv_free_contacts(struct sip_msg* msg, char* profile, char* s2)
+{
+	regpv_profile_t *rpp;
+
+	rpp = regpv_get_profile((str*)profile);
+	if(rpp==0)
+		return -1;
+
+	regpv_free_profile(rpp);
+
+	return 1;
+}

+ 49 - 0
modules/registrar_scscf/regpv.h

@@ -0,0 +1,49 @@
+/*
+ * $Id$
+ *
+ * Export vontact attrs as PV
+ *
+ * Copyright (C) 2008 Daniel-Constantin Mierla (asipto.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
+ */
+
+/*!
+ * \file
+ * \brief SIP registrar module - export contacts as PV
+ * \ingroup registrar   
+ */  
+
+
+#ifndef _REGPV_H_
+#define _REGPV_H_
+
+#include "../../pvar.h"
+
+int pv_get_ulc(struct sip_msg *msg,  pv_param_t *param,
+		pv_value_t *res);
+int pv_set_ulc(struct sip_msg* msg, pv_param_t *param,
+		int op, pv_value_t *val);
+int pv_parse_ulc_name(pv_spec_p sp, str *in);
+
+int pv_fetch_contacts(struct sip_msg* msg, char* table, char* uri,
+		char* profile);
+int pv_free_contacts(struct sip_msg* msg, char* profile, char *s2);
+
+void regpv_free_profiles(void);
+
+#endif

+ 39 - 0
modules/registrar_scscf/regtime.c

@@ -0,0 +1,39 @@
+/*
+ * $Id$
+ *
+ * Registrar time related functions
+ *
+ * Copyright (C) 2001-2003 FhG Fokus
+ *
+ * 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 "regtime.h"
+
+
+time_t act_time;
+
+
+/*! \brief
+ * Get actual time and store
+ * value in act_time
+ */
+void get_act_time(void)
+{
+	act_time = time(0);
+}

+ 48 - 0
modules/registrar_scscf/regtime.h

@@ -0,0 +1,48 @@
+/*
+ * $Id$
+ *
+ * Registrar time related functions
+ *
+ * Copyright (C) 2001-2003 FhG Fokus
+ *
+ * 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
+ */
+
+/*!
+ * \file
+ * \brief SIP registrar module - time related functions
+ * \ingroup registrar   
+ */  
+
+
+#ifndef REGTIME_H
+#define REGTIME_H
+
+#include <time.h>
+
+
+extern time_t act_time;
+
+
+/*! \brief
+ * Get actual time and store
+ * value in act_time
+ */
+void get_act_time(void);
+
+
+#endif /* REGTIME_H */

+ 770 - 0
modules/registrar_scscf/reply.c

@@ -0,0 +1,770 @@
+/*
+ * $Id$
+ *
+ * Send a reply
+ *
+ * Copyright (C) 2001-2003 FhG Fokus
+ *
+ * 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
+ *
+ * History:
+ * --------
+ * 2003-01-18: buffer overflow patch committed (Jan on behalf of Maxim)
+ * 2003-01-21: Errors reported via Error-Info header field - janakj
+ * 2003-09-11: updated to new build_lump_rpl() interface (bogdan)
+ * 2003-11-11: build_lump_rpl() removed, add_lump_rpl() has flags (bogdan)
+ */
+
+/*!
+ * \file
+ * \brief SIP registrar module - Send a reply
+ * \ingroup registrar   
+ */
+
+#include "../../ut.h"
+#include "../../parser/msg_parser.h"
+#include "../../parser/contact/contact.h"
+#include "../../lib/kcore/parse_supported.h"
+#include "../../data_lump_rpl.h"
+#include "../usrloc_scscf/usrloc.h"
+#include "rerrno.h"
+#include "reg_mod.h"
+#include "regtime.h"
+#include "reply.h"
+#include "config.h"
+
+#define MAX_CONTACT_BUFFER 1024
+
+#define E_INFO "P-Registrar-Error: "
+#define E_INFO_LEN (sizeof(E_INFO) - 1)
+
+#define CONTACT_BEGIN "Contact: "
+#define CONTACT_BEGIN_LEN (sizeof(CONTACT_BEGIN) - 1)
+
+#define Q_PARAM ";q="
+#define Q_PARAM_LEN (sizeof(Q_PARAM) - 1)
+
+#define EXPIRES_PARAM ";expires="
+#define EXPIRES_PARAM_LEN (sizeof(EXPIRES_PARAM) - 1)
+
+#define CONTACT_SEP ", "
+#define CONTACT_SEP_LEN (sizeof(CONTACT_SEP) - 1)
+
+extern str scscf_serviceroute_uri_str;
+
+extern struct tm_binds tmb;
+
+static struct {
+    char* buf;
+    int buf_len;
+    int data_len;
+} p_associated_uri = {0, 0, 0};
+
+
+/*! \brief
+ * Buffer for Contact header field
+ */
+//static struct {
+//	char* buf;
+//	int buf_len;
+//	int data_len;
+//} contact = {0, 0, 0};
+
+/*! \brief
+ * Calculate the length of buffer needed to
+ * print contacts
+ */
+static inline unsigned int calc_buf_len(ucontact_t* c) {
+    unsigned int len;
+    int qlen;
+
+    len = 0;
+    while (c) {
+        if (VALID_CONTACT(c, act_time)) {
+            if (len) len += CONTACT_SEP_LEN;
+            len += 2 /* < > */ + c->c.len;
+            qlen = len_q(c->q);
+            if (qlen) len += Q_PARAM_LEN + qlen;
+            len += EXPIRES_PARAM_LEN + INT2STR_MAX_LEN;
+            if (c->received.s) {
+                len += 1 /* ; */
+                        + rcv_param.len
+                        + 1 /* = */
+                        + 1 /* dquote */
+                        + c->received.len
+                        + 1 /* dquote */
+                        ;
+            }
+        }
+        c = c->next;
+    }
+
+    if (len) len += CONTACT_BEGIN_LEN + CRLF_LEN;
+    return len;
+}
+
+
+#define MSG_200 "OK"
+#define MSG_400 "Bad Request"
+#define MSG_420 "Bad Extension"
+#define MSG_500 "Server Internal Error"
+#define MSG_503 "Service Unavailable"
+
+#define EI_R_FINE       "No problem"                                /* R_FINE */
+#define EI_R_UL_DEL_R   "usrloc_record_delete failed"               /* R_UL_DEL_R */
+#define	EI_R_UL_GET_R   "usrloc_record_get failed"                  /* R_UL_GET */
+#define	EI_R_UL_NEW_R   "usrloc_record_new failed"                  /* R_UL_NEW_R */
+#define	EI_R_INV_CSEQ   "Invalid CSeq number"                       /* R_INV_CSEQ */
+#define	EI_R_UL_INS_C   "usrloc_contact_insert failed"              /* R_UL_INS_C */
+#define	EI_R_UL_INS_R   "usrloc_record_insert failed"               /* R_UL_INS_R */
+#define	EI_R_UL_DEL_C   "usrloc_contact_delete failed"              /* R_UL_DEL_C */
+#define	EI_R_UL_UPD_C   "usrloc_contact_update failed"              /* R_UL_UPD_C */
+#define	EI_R_TO_USER    "No username in To URI"                     /* R_TO_USER */
+#define	EI_R_AOR_LEN    "Address Of Record too long"                /* R_AOR_LEN */
+#define	EI_R_AOR_PARSE  "Error while parsing AOR"                   /* R_AOR_PARSE */
+#define	EI_R_INV_EXP    "Invalid expires param in contact"          /* R_INV_EXP */
+#define	EI_R_INV_Q      "Invalid q param in contact"                /* R_INV_Q */
+#define	EI_R_PARSE      "Message parse error"                       /* R_PARSE */
+#define	EI_R_TO_MISS    "To header not found"                       /* R_TO_MISS */
+#define	EI_R_CID_MISS   "Call-ID header not found"                  /* R_CID_MISS */
+#define	EI_R_CS_MISS    "CSeq header not found"                     /* R_CS_MISS */ 
+#define	EI_R_PARSE_EXP	"Expires parse error"                       /* R_PARSE_EXP */
+#define	EI_R_PARSE_CONT	"Contact parse error"                       /* R_PARSE_CONT */
+#define	EI_R_STAR_EXP	"* used in contact and expires is not zero" /* R_STAR__EXP */
+#define	EI_R_STAR_CONT	"* used in contact and more than 1 contact" /* R_STAR_CONT */
+#define	EI_R_OOO	"Out of order request"                      /* R_OOO */
+#define	EI_R_RETRANS	"Retransmission"                            /* R_RETRANS */
+#define EI_R_UNESCAPE   "Error while unescaping username"           /* R_UNESCAPE */
+#define EI_R_TOO_MANY   "Too many registered contacts"              /* R_TOO_MANY */
+#define EI_R_CONTACT_LEN  "Contact/received too long"               /* R_CONTACT_LEN */
+#define EI_R_CALLID_LEN  "Callid too long"                          /* R_CALLID_LEN */
+#define EI_R_PARSE_PATH  "Path parse error"                         /* R_PARSE_PATH */
+#define EI_R_PATH_UNSUP  "No support for found Path indicated"      /* R_PATH_UNSUP */
+#define EI_R_SAR_FAILED  "SAR failed"      							/* R_SAR_FAILED */
+
+str error_info[] = {
+    {EI_R_FINE, sizeof (EI_R_FINE) - 1},
+    {EI_R_UL_DEL_R, sizeof (EI_R_UL_DEL_R) - 1},
+    {EI_R_UL_GET_R, sizeof (EI_R_UL_GET_R) - 1},
+    {EI_R_UL_NEW_R, sizeof (EI_R_UL_NEW_R) - 1},
+    {EI_R_INV_CSEQ, sizeof (EI_R_INV_CSEQ) - 1},
+    {EI_R_UL_INS_C, sizeof (EI_R_UL_INS_C) - 1},
+    {EI_R_UL_INS_R, sizeof (EI_R_UL_INS_R) - 1},
+    {EI_R_UL_DEL_C, sizeof (EI_R_UL_DEL_C) - 1},
+    {EI_R_UL_UPD_C, sizeof (EI_R_UL_UPD_C) - 1},
+    {EI_R_TO_USER, sizeof (EI_R_TO_USER) - 1},
+    {EI_R_AOR_LEN, sizeof (EI_R_AOR_LEN) - 1},
+    {EI_R_AOR_PARSE, sizeof (EI_R_AOR_PARSE) - 1},
+    {EI_R_INV_EXP, sizeof (EI_R_INV_EXP) - 1},
+    {EI_R_INV_Q, sizeof (EI_R_INV_Q) - 1},
+    {EI_R_PARSE, sizeof (EI_R_PARSE) - 1},
+    {EI_R_TO_MISS, sizeof (EI_R_TO_MISS) - 1},
+    {EI_R_CID_MISS, sizeof (EI_R_CID_MISS) - 1},
+    {EI_R_CS_MISS, sizeof (EI_R_CS_MISS) - 1},
+    {EI_R_PARSE_EXP, sizeof (EI_R_PARSE_EXP) - 1},
+    {EI_R_PARSE_CONT, sizeof (EI_R_PARSE_CONT) - 1},
+    {EI_R_STAR_EXP, sizeof (EI_R_STAR_EXP) - 1},
+    {EI_R_STAR_CONT, sizeof (EI_R_STAR_CONT) - 1},
+    {EI_R_OOO, sizeof (EI_R_OOO) - 1},
+    {EI_R_RETRANS, sizeof (EI_R_RETRANS) - 1},
+    {EI_R_UNESCAPE, sizeof (EI_R_UNESCAPE) - 1},
+    {EI_R_TOO_MANY, sizeof (EI_R_TOO_MANY) - 1},
+    {EI_R_CONTACT_LEN, sizeof (EI_R_CONTACT_LEN) - 1},
+    {EI_R_CALLID_LEN, sizeof (EI_R_CALLID_LEN) - 1},
+    {EI_R_PARSE_PATH, sizeof (EI_R_PARSE_PATH) - 1},
+    {EI_R_PATH_UNSUP, sizeof (EI_R_PATH_UNSUP) - 1},
+    {EI_R_SAR_FAILED, sizeof (EI_R_SAR_FAILED) - 1}
+
+};
+
+int codes[] = {
+    200, /* R_FINE */
+    500, /* R_UL_DEL_R */
+    500, /* R_UL_GET */
+    500, /* R_UL_NEW_R */
+    400, /* R_INV_CSEQ */
+    500, /* R_UL_INS_C */
+    500, /* R_UL_INS_R */
+    500, /* R_UL_DEL_C */
+    500, /* R_UL_UPD_C */
+    400, /* R_TO_USER */
+    500, /* R_AOR_LEN */
+    400, /* R_AOR_PARSE */
+    400, /* R_INV_EXP */
+    400, /* R_INV_Q */
+    400, /* R_PARSE */
+    400, /* R_TO_MISS */
+    400, /* R_CID_MISS */
+    400, /* R_CS_MISS */
+    400, /* R_PARSE_EXP */
+    400, /* R_PARSE_CONT */
+    400, /* R_STAR_EXP */
+    400, /* R_STAR_CONT */
+    200, /* R_OOO */
+    200, /* R_RETRANS */
+    400, /* R_UNESCAPE */
+    503, /* R_TOO_MANY */
+    400, /* R_CONTACT_LEN */
+    400, /* R_CALLID_LEN */
+    400, /* R_PARSE_PATH */
+    420, /* R_PATH_UNSUP */
+    500 /* R_SAR_FAILED */
+};
+
+
+#define RETRY_AFTER "Retry-After: "
+#define RETRY_AFTER_LEN (sizeof(RETRY_AFTER) - 1)
+
+static int add_retry_after(struct sip_msg* _m) {
+    char* buf, *ra_s;
+    int ra_len;
+
+    ra_s = int2str(cfg_get(registrar, registrar_cfg, retry_after), &ra_len);
+    buf = (char*) pkg_malloc(RETRY_AFTER_LEN + ra_len + CRLF_LEN);
+    if (!buf) {
+        LM_ERR("no pkg memory left\n");
+        return -1;
+    }
+    memcpy(buf, RETRY_AFTER, RETRY_AFTER_LEN);
+    memcpy(buf + RETRY_AFTER_LEN, ra_s, ra_len);
+    memcpy(buf + RETRY_AFTER_LEN + ra_len, CRLF, CRLF_LEN);
+    add_lump_rpl(_m, buf, RETRY_AFTER_LEN + ra_len + CRLF_LEN,
+            LUMP_RPL_HDR | LUMP_RPL_NODUP);
+    return 0;
+}
+
+#define PATH "Path: "
+#define PATH_LEN (sizeof(PATH) - 1)
+
+static int add_path(struct sip_msg* _m, str* _p) {
+    char* buf;
+
+    buf = (char*) pkg_malloc(PATH_LEN + _p->len + CRLF_LEN);
+    if (!buf) {
+        LM_ERR("no pkg memory left\n");
+        return -1;
+    }
+    memcpy(buf, PATH, PATH_LEN);
+    memcpy(buf + PATH_LEN, _p->s, _p->len);
+    memcpy(buf + PATH_LEN + _p->len, CRLF, CRLF_LEN);
+    add_lump_rpl(_m, buf, PATH_LEN + _p->len + CRLF_LEN,
+            LUMP_RPL_HDR | LUMP_RPL_NODUP);
+    return 0;
+}
+
+
+#define SERVICEROUTE_START "Service-Route: <"
+#define SERVICEROUTE_START_LEN (sizeof(SERVICEROUTE_START) -1)
+
+#define SERVICEROUTE_END ";lr>\r\n"
+#define SERVICEROUTE_END_LEN (sizeof(SERVICEROUTE_END) -1)
+
+static int add_service_route(struct sip_msg* _m, str* _uri) {
+    char* buf;
+
+    buf = (char*) pkg_malloc(SERVICEROUTE_START_LEN + _uri->len + SERVICEROUTE_END_LEN);
+    if (!buf) {
+        LM_ERR("no pkg memory left\n");
+        return -1;
+    }
+
+    memcpy(buf, SERVICEROUTE_START, SERVICEROUTE_START_LEN);
+    memcpy(buf + SERVICEROUTE_START_LEN, _uri->s, _uri->len);
+    memcpy(buf + SERVICEROUTE_START_LEN + _uri->len, SERVICEROUTE_END, SERVICEROUTE_END_LEN);
+    add_lump_rpl(_m, buf, SERVICEROUTE_START_LEN + _uri->len + SERVICEROUTE_END_LEN, LUMP_RPL_HDR | LUMP_RPL_NODUP);
+    return 0;
+}
+
+#define UNSUPPORTED "Unsupported: "
+#define UNSUPPORTED_LEN (sizeof(UNSUPPORTED) - 1)
+
+static int add_unsupported(struct sip_msg* _m, str* _p) {
+    char* buf;
+
+    buf = (char*) pkg_malloc(UNSUPPORTED_LEN + _p->len + CRLF_LEN);
+    if (!buf) {
+        LM_ERR("no pkg memory left\n");
+        return -1;
+    }
+    memcpy(buf, UNSUPPORTED, UNSUPPORTED_LEN);
+    memcpy(buf + UNSUPPORTED_LEN, _p->s, _p->len);
+    memcpy(buf + UNSUPPORTED_LEN + _p->len, CRLF, CRLF_LEN);
+    add_lump_rpl(_m, buf, UNSUPPORTED_LEN + _p->len + CRLF_LEN,
+            LUMP_RPL_HDR | LUMP_RPL_NODUP);
+    return 0;
+}
+
+int build_expired_contact(contact_t* chi, contact_for_header_t** contact_header) {
+
+    char *p, *cp;
+    int len;
+    char *tmp;
+    int old_data_len = 0;
+
+    contact_for_header_t* c_header = 0;
+
+    len = chi->uri.len
+                + 2 /*<>*/
+                + chi->uri.len
+                + EXPIRES_PARAM_LEN + INT2STR_MAX_LEN;
+
+    if (c_header && c_header->data_len > 0) {
+    	//contact header has already been started
+    	old_data_len = c_header->data_len - CRLF_LEN;
+    	c_header->data_len = c_header->data_len + len + CONTACT_SEP_LEN;
+    } else {
+    	if (!c_header) {//not yet built one
+    		c_header = shm_malloc(sizeof(contact_for_header_t));
+    		if (!c_header) {
+    			LM_ERR("no more shm mem\n");
+    			return 0;
+    		}
+    		memset(c_header, 0, sizeof(contact_for_header_t));
+    	}
+    	c_header->data_len = CONTACT_BEGIN_LEN + len + CRLF_LEN;
+    }
+
+    if (!c_header->data_len)
+    	return 0;
+
+    if (!c_header->buf || (c_header->buf_len < c_header->data_len)) {
+        tmp = (char*) shm_malloc(c_header->data_len);
+        if (!tmp) {
+        	c_header->data_len = 0;
+        	c_header->buf_len = 0;
+            LM_ERR("no pkg memory left\n");
+            return -1;
+        }
+        if (c_header->buf) {
+            //copy and free
+            memcpy(tmp, c_header->buf, old_data_len);
+            shm_free(c_header->buf);
+            c_header->buf = tmp;
+        } else {
+        	c_header->buf = tmp;
+        }
+    }
+    p = c_header->buf + old_data_len; //just in case we append
+
+    if (old_data_len) {
+        //copy new contact
+        memcpy(p, CONTACT_SEP, CONTACT_SEP_LEN);
+        p += CONTACT_SEP_LEN;
+
+    } else {
+        //copy full new structure plus new contact
+        memcpy(p, CONTACT_BEGIN, CONTACT_BEGIN_LEN);
+        p += CONTACT_BEGIN_LEN;
+    }
+    memcpy(p++, "<", 1);
+    memcpy(p, chi->uri.s, chi->uri.len);
+    p += chi->uri.len;
+    memcpy(p++, ">", 1);
+    memcpy(p, EXPIRES_PARAM, EXPIRES_PARAM_LEN);
+    p += EXPIRES_PARAM_LEN;
+    cp = int2str((int) (0), &len);
+    memcpy(p, cp, len);
+    p += len;
+    memcpy(p, CRLF, CRLF_LEN);
+    p += CRLF_LEN;
+    c_header->data_len = p - c_header->buf;
+
+    LM_DBG("de-reg contact is [%.*s]\n", c_header->data_len, c_header->buf);
+    *contact_header = c_header;
+    return 0;
+}
+
+/*! \brief
+ * Allocate a memory buffer and print Contact
+ * header fields into it
+ */
+
+//We use shared memory for this so we can use it when we use async diameter
+
+int build_contact(ucontact_t* c, contact_for_header_t** contact_header) {
+
+    char *p, *cp;
+    int fl, len;
+
+    *contact_header = 0;
+
+    contact_for_header_t* tmp_contact_header = shm_malloc(sizeof (contact_for_header_t));
+    if (!tmp_contact_header) {
+    	LM_ERR("no more memory\n");
+    	return -1;
+    }
+    memset(tmp_contact_header, 0, sizeof (contact_for_header_t));
+
+    tmp_contact_header->data_len = calc_buf_len(c);
+    tmp_contact_header->buf = (char*)shm_malloc(tmp_contact_header->data_len);
+
+    p = tmp_contact_header->buf;
+
+    memcpy(p, CONTACT_BEGIN, CONTACT_BEGIN_LEN);
+    p += CONTACT_BEGIN_LEN;
+
+    fl = 0;
+    while (c) {
+        if (VALID_CONTACT(c, act_time)) {
+            if (fl) {
+                memcpy(p, CONTACT_SEP, CONTACT_SEP_LEN);
+                p += CONTACT_SEP_LEN;
+            } else {
+                fl = 1;
+            }
+
+            *p++ = '<';
+            memcpy(p, c->c.s, c->c.len);
+            p += c->c.len;
+            *p++ = '>';
+
+            len = len_q(c->q);
+            if (len) {
+                memcpy(p, Q_PARAM, Q_PARAM_LEN);
+                p += Q_PARAM_LEN;
+                memcpy(p, q2str(c->q, 0), len);
+                p += len;
+            }
+
+            memcpy(p, EXPIRES_PARAM, EXPIRES_PARAM_LEN);
+            p += EXPIRES_PARAM_LEN;
+            cp = int2str((int) (c->expires - act_time), &len);
+            memcpy(p, cp, len);
+            p += len;
+
+            if (c->received.s) {
+                *p++ = ';';
+                memcpy(p, rcv_param.s, rcv_param.len);
+                p += rcv_param.len;
+                *p++ = '=';
+                *p++ = '\"';
+                memcpy(p, c->received.s, c->received.len);
+                p += c->received.len;
+                *p++ = '\"';
+            }
+        }
+
+        c = c->next;
+    }
+
+    memcpy(p, CRLF, CRLF_LEN);
+    p += CRLF_LEN;
+
+    tmp_contact_header->data_len = p - tmp_contact_header->buf;
+
+    LM_DBG("created Contact HF: %.*s\n", tmp_contact_header->data_len, tmp_contact_header->buf);
+    *contact_header = tmp_contact_header;
+    return 0;
+}
+
+#define PASSOCIATEDURI "P-Associated-URI: "
+#define PASSOCIATEDURI_LEN (sizeof(PASSOCIATEDURI)-1)
+
+/*! \brief
+ * Calculate the length of buffer needed to
+ * print p-associated-uri
+ */
+static inline unsigned int calc_associateduri_buf_len(ims_subscription* s) {
+    unsigned int len;
+    int i, j;
+    ims_public_identity* id;
+
+    len = 0;
+    for (i = 0; i < s->service_profiles_cnt; i++)
+        for (j = 0; j < s->service_profiles[i].public_identities_cnt; j++) {
+            id = &(s->service_profiles[i].public_identities[j]);
+            if (!id->barring)
+                len += 4 + id->public_identity.len; /*4 is for ">, <""*/
+        }
+
+    if (len) len += PASSOCIATEDURI_LEN + 2 + CRLF_LEN; // <> and \r\n;
+
+    return len;
+}
+
+/*! \brief
+ * Allocate a memory buffer and print P-Associated_URI
+ * header fields into it
+ */
+int build_p_associated_uri(ims_subscription* s) {
+    char *p;
+    int i, j, cnt = 0;
+    ims_public_identity* id;
+
+    LM_DBG("Building P-Associated-URI\n");
+
+    if (!s) {
+        LM_ERR("Strange, no ims subscription data - how did we get here\n");
+        return -1;
+    }
+    p_associated_uri.data_len = calc_associateduri_buf_len(s);
+    if (!p_associated_uri.data_len)
+        return -1;
+
+    if (!p_associated_uri.buf || (p_associated_uri.buf_len < p_associated_uri.data_len)) {
+        if (p_associated_uri.buf)
+            pkg_free(p_associated_uri.buf);
+        p_associated_uri.buf = (char*) pkg_malloc(p_associated_uri.data_len);
+        if (!p_associated_uri.buf) {
+            p_associated_uri.data_len = 0;
+            p_associated_uri.buf_len = 0;
+            LM_ERR("no pkg memory left\n");
+            return -1;
+        } else {
+            p_associated_uri.buf_len = p_associated_uri.data_len;
+        }
+    }
+
+    p = p_associated_uri.buf;
+    memcpy(p, PASSOCIATEDURI, PASSOCIATEDURI_LEN);
+    p += PASSOCIATEDURI_LEN;
+
+    for (i = 0; i < s->service_profiles_cnt; i++)
+        for (j = 0; j < s->service_profiles[i].public_identities_cnt; j++) {
+            id = &(s->service_profiles[i].public_identities[j]);
+            if (!id->barring) {
+                if (cnt == 0)
+                    *p++ = '<';
+                else {
+                    memcpy(p, ">, <", 4);
+                    p += 4;
+                }
+                memcpy(p, id->public_identity.s, id->public_identity.len);
+                p += id->public_identity.len;
+                cnt++;
+            }
+        }
+    if (cnt)
+        *p++ = '>';
+
+    memcpy(p, "\r\n", CRLF_LEN);
+    p += CRLF_LEN;
+    p_associated_uri.data_len = p - p_associated_uri.buf;
+    LM_DBG("Created P-Associated-URI HF %.*s\n", p_associated_uri.data_len, p_associated_uri.buf);
+
+    return 0;
+}
+
+/*! \brief
+ * Send a reply
+ */
+int reg_send_reply_transactional(struct sip_msg* _m, contact_for_header_t* contact_header, struct cell* t_cell) {
+    str unsup = str_init(SUPPORTED_PATH_STR);
+    long code;
+    str msg = str_init(MSG_200); /* makes gcc shut up */
+    char* buf;
+
+    if (contact_header && contact_header->buf && contact_header->data_len > 0) {
+    	LM_DBG("Contacts: %.*s\n", contact_header->data_len, contact_header->buf);
+        add_lump_rpl(_m, contact_header->buf, contact_header->data_len, LUMP_RPL_HDR | LUMP_RPL_NODUP | LUMP_RPL_NOFREE);
+        contact_header->data_len = 0;
+    }
+
+    if (rerrno == R_FINE && path_enabled && _m->path_vec.s) {
+        if (path_mode != PATH_MODE_OFF) {
+            if (parse_supported(_m) < 0 && path_mode == PATH_MODE_STRICT) {
+                rerrno = R_PATH_UNSUP;
+                if (add_unsupported(_m, &unsup) < 0)
+                    return -1;
+                if (add_path(_m, &_m->path_vec) < 0)
+                    return -1;
+            } else if (get_supported(_m) & F_SUPPORTED_PATH) {
+                if (add_path(_m, &_m->path_vec) < 0)
+                    return -1;
+            } else if (path_mode == PATH_MODE_STRICT) {
+                rerrno = R_PATH_UNSUP;
+                if (add_unsupported(_m, &unsup) < 0)
+                    return -1;
+                if (add_path(_m, &_m->path_vec) < 0)
+                    return -1;
+            }
+        }
+    }
+
+    code = codes[rerrno];
+    switch (code) {
+        case 200: msg.s = MSG_200;
+            msg.len = sizeof (MSG_200) - 1;
+            break;
+        case 400: msg.s = MSG_400;
+            msg.len = sizeof (MSG_400) - 1;
+            break;
+        case 420: msg.s = MSG_420;
+            msg.len = sizeof (MSG_420) - 1;
+            break;
+        case 500: msg.s = MSG_500;
+            msg.len = sizeof (MSG_500) - 1;
+            break;
+        case 503: msg.s = MSG_503;
+            msg.len = sizeof (MSG_503) - 1;
+            break;
+    }
+
+    if (code != 200) {
+        buf = (char*) pkg_malloc(E_INFO_LEN + error_info[rerrno].len + CRLF_LEN + 1);
+        if (!buf) {
+            LM_ERR("no pkg memory left\n");
+            return -1;
+        }
+        memcpy(buf, E_INFO, E_INFO_LEN);
+        memcpy(buf + E_INFO_LEN, error_info[rerrno].s, error_info[rerrno].len);
+        memcpy(buf + E_INFO_LEN + error_info[rerrno].len, CRLF, CRLF_LEN);
+        add_lump_rpl(_m, buf, E_INFO_LEN + error_info[rerrno].len + CRLF_LEN,
+                LUMP_RPL_HDR | LUMP_RPL_NODUP);
+
+        if (code >= 500 && code < 600 && cfg_get(registrar, registrar_cfg, retry_after)) {
+            if (add_retry_after(_m) < 0) {
+                return -1;
+            }
+        }
+    }
+
+    if ((code > 199) && (code < 299)) {
+        if (p_associated_uri.data_len > 0) {
+            add_lump_rpl(_m, p_associated_uri.buf, p_associated_uri.data_len,
+                    LUMP_RPL_HDR | LUMP_RPL_NODUP | LUMP_RPL_NOFREE);
+            p_associated_uri.data_len = 0;
+        }
+        if (add_service_route(_m, &scscf_serviceroute_uri_str) < 0) { //TODO - need to insert orig into this scscf_name
+            return -1;
+        }
+    }
+
+    //if (slb.freply(_m, code, &msg) < 0) {
+    if (tmb.t_reply_trans(t_cell, _m, code, msg.s) < 0){
+        LM_ERR("failed to send %ld %.*s\n", code, msg.len, msg.s);
+        return -1;
+    } else return 0;
+}
+
+
+/*! \brief
+ * Send a reply
+ */
+int reg_send_reply(struct sip_msg* _m, contact_for_header_t* contact_header) {
+    str unsup = str_init(SUPPORTED_PATH_STR);
+    long code;
+    str msg = str_init(MSG_200); /* makes gcc shut up */
+    char* buf;
+
+    if (contact_header && contact_header->buf && contact_header->data_len > 0) {
+    	LM_DBG("Contacts: %.*s\n", contact_header->data_len, contact_header->buf);
+        add_lump_rpl(_m, contact_header->buf, contact_header->data_len, LUMP_RPL_HDR | LUMP_RPL_NODUP | LUMP_RPL_NOFREE);
+        contact_header->data_len = 0;
+    }
+
+    if (rerrno == R_FINE && path_enabled && _m->path_vec.s) {
+        if (path_mode != PATH_MODE_OFF) {
+            if (parse_supported(_m) < 0 && path_mode == PATH_MODE_STRICT) {
+                rerrno = R_PATH_UNSUP;
+                if (add_unsupported(_m, &unsup) < 0)
+                    return -1;
+                if (add_path(_m, &_m->path_vec) < 0)
+                    return -1;
+            } else if (get_supported(_m) & F_SUPPORTED_PATH) {
+                if (add_path(_m, &_m->path_vec) < 0)
+                    return -1;
+            } else if (path_mode == PATH_MODE_STRICT) {
+                rerrno = R_PATH_UNSUP;
+                if (add_unsupported(_m, &unsup) < 0)
+                    return -1;
+                if (add_path(_m, &_m->path_vec) < 0)
+                    return -1;
+            }
+        }
+    }
+
+    code = codes[rerrno];
+    switch (code) {
+        case 200: msg.s = MSG_200;
+            msg.len = sizeof (MSG_200) - 1;
+            break;
+        case 400: msg.s = MSG_400;
+            msg.len = sizeof (MSG_400) - 1;
+            break;
+        case 420: msg.s = MSG_420;
+            msg.len = sizeof (MSG_420) - 1;
+            break;
+        case 500: msg.s = MSG_500;
+            msg.len = sizeof (MSG_500) - 1;
+            break;
+        case 503: msg.s = MSG_503;
+            msg.len = sizeof (MSG_503) - 1;
+            break;
+    }
+
+    if (code != 200) {
+        buf = (char*) pkg_malloc(E_INFO_LEN + error_info[rerrno].len + CRLF_LEN + 1);
+        if (!buf) {
+            LM_ERR("no pkg memory left\n");
+            return -1;
+        }
+        memcpy(buf, E_INFO, E_INFO_LEN);
+        memcpy(buf + E_INFO_LEN, error_info[rerrno].s, error_info[rerrno].len);
+        memcpy(buf + E_INFO_LEN + error_info[rerrno].len, CRLF, CRLF_LEN);
+        add_lump_rpl(_m, buf, E_INFO_LEN + error_info[rerrno].len + CRLF_LEN,
+                LUMP_RPL_HDR | LUMP_RPL_NODUP);
+
+        if (code >= 500 && code < 600 && cfg_get(registrar, registrar_cfg, retry_after)) {
+            if (add_retry_after(_m) < 0) {
+                return -1;
+            }
+        }
+    }
+
+    if ((code > 199) && (code < 299)) {
+        if (p_associated_uri.data_len > 0) {
+            add_lump_rpl(_m, p_associated_uri.buf, p_associated_uri.data_len,
+                    LUMP_RPL_HDR | LUMP_RPL_NODUP | LUMP_RPL_NOFREE);
+            p_associated_uri.data_len = 0;
+        }
+        if (add_service_route(_m, &scscf_serviceroute_uri_str) < 0) { //TODO - need to insert orig into this scscf_name
+            return -1;
+        }
+    }
+
+    if (slb.freply(_m, code, &msg) < 0) {
+        LM_ERR("failed to send %ld %.*s\n", code, msg.len, msg.s);
+        return -1;
+    } else return 0;
+}
+
+/*! \brief
+ * Release contact buffer if any
+ */
+void free_contact_buf(contact_for_header_t* contact_header) {
+    if (contact_header && contact_header->buf) {
+        shm_free(contact_header->buf);
+        contact_header->buf = 0;
+        contact_header->buf_len = 0;
+        contact_header->data_len = 0;
+    }
+    if (contact_header)shm_free(contact_header);
+}
+
+void free_p_associated_uri_buf(void) {
+    if (p_associated_uri.buf) {
+        pkg_free(p_associated_uri.buf);
+        p_associated_uri.buf = 0;
+        p_associated_uri.buf_len = 0;
+        p_associated_uri.data_len = 0;
+    }
+}
+
+void free_expired_contact_buf(void) {
+
+}

+ 84 - 0
modules/registrar_scscf/reply.h

@@ -0,0 +1,84 @@
+/*
+ * $Id$
+ *
+ * Send a reply
+ *
+ * Copyright (C) 2001-2003 FhG Fokus
+ *
+ * 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
+ */
+
+/*!
+ * \file
+ * \brief SIP registrar module - send a reply
+ * \ingroup registrar   
+ */  
+
+
+#ifndef REPLY_H
+#define REPLY_H
+
+#include "../../parser/msg_parser.h"
+#include "../usrloc_scscf/ucontact.h"
+#include "../../parser/contact/contact.h"
+
+#include "../../modules/tm/tm_load.h"
+
+//we use shared memory for this so we can use async diameter
+/*! \brief
+ * Buffer for Contact header field
+ */
+typedef struct contact_for_header {
+	char* buf;
+	int buf_len;
+	int data_len;
+} contact_for_header_t;
+
+
+/*! \brief
+ * Send a reply
+ */
+int reg_send_reply(struct sip_msg* _m, contact_for_header_t* contact_header);
+
+
+/*! \brief
+ * Send a reply using tm
+ */
+int reg_send_reply_transactional(struct sip_msg* _m, contact_for_header_t* contact_header, struct cell* t_cell);
+
+
+/*! \brief
+ * Build Contact HF for reply
+ */
+int build_contact(ucontact_t* c, contact_for_header_t** contact_header);
+int build_expired_contact(contact_t* chi, contact_for_header_t** contact_header); //this is for building the expired response - ie reply to dereg
+
+int build_p_associated_uri(ims_subscription* s);
+
+
+/*! \brief
+ * Release contact buffer if any
+ */
+void free_contact_buf(contact_for_header_t* contact_header);
+
+void free_p_associated_uri_buf(void);
+
+void free_expired_contact_buf(void);
+
+
+
+#endif /* REPLY_H */

+ 35 - 0
modules/registrar_scscf/rerrno.c

@@ -0,0 +1,35 @@
+/*
+ * $Id$
+ *
+ * Registrar errno
+ *
+ * Copyright (C) 2001-2003 FhG Fokus
+ *
+ * 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
+ */
+
+/*!
+ * \file
+ * \brief SIP registrar module - registrar errno
+ * \ingroup registrar   
+ */  
+
+
+#include "rerrno.h"
+
+rerr_t rerrno;
+

+ 71 - 0
modules/registrar_scscf/rerrno.h

@@ -0,0 +1,71 @@
+/*
+ * $Id$
+ *
+ * Registrar errno
+ *
+ * Copyright (C) 2001-2003 FhG Fokus
+ *
+ * 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
+ */
+
+/*!
+ * \file
+ * \brief SIP registrar module - registrar errno
+ * \ingroup registrar   
+ */
+
+#ifndef RERRNO_H
+#define RERRNO_H
+
+typedef enum rerr {
+	R_FINE = 0, 			/*!< Everything went OK */
+	R_UL_DEL_R, 			/*!< Usrloc record delete failed */
+	R_UL_GET_R, 			/*!< Usrloc record get failed */
+	R_UL_NEW_R, 			/*!< Usrloc new record failed */
+	R_INV_CSEQ, 			/*!< Invalid CSeq value */
+	R_UL_INS_C, 			/*!< Usrloc insert contact failed */
+	R_UL_INS_R, 			/*!< Usrloc insert record failed */
+	R_UL_DEL_C, 			/*!< Usrloc contact delete failed */
+	R_UL_UPD_C, 			/*!< Usrloc contact update failed */
+	R_TO_USER, 				/*!< No username part in To URI */
+	R_AOR_LEN, 				/*!< Address Of Record too long */
+	R_AOR_PARSE, 			/*!< Error while parsing Address Of Record */
+	R_INV_EXP, 				/*!< Invalid expires parameter in contact */
+	R_INV_Q, 				/*!< Invalid q parameter in contact */
+	R_PARSE, 				/*!< Error while parsing message */
+	R_TO_MISS,				/*!< Missing To header field */
+	R_CID_MISS, 			/*!< Missing Call-ID header field */
+	R_CS_MISS, 				/*!< Missing CSeq header field */
+	R_PARSE_EXP, 			/*!< Error while parsing Expires */
+	R_PARSE_CONT, 			/*!< Error while parsing Contact */
+	R_STAR_EXP, 			/*!< star and expires != 0 */
+	R_STAR_CONT, 			/*!< star and more contacts */
+	R_OOO, 					/*!< Out-Of-Order request */
+	R_RETRANS, 				/*!< Request is retransmission */
+	R_UNESCAPE, 			/*!< Error while unescaping username */
+	R_TOO_MANY, 			/*!< Too many contacts */
+	R_CONTACT_LEN,			/*!< Contact URI or RECEIVED too long */
+	R_CALLID_LEN, 			/*!< Callid too long */
+	R_PARSE_PATH, 			/*!< Error while parsing Path */
+	R_PATH_UNSUP,			/*!< Path not supported by UAC */
+	R_SAR_FAILED			/*!< SAR failed */
+
+} rerr_t;
+
+extern rerr_t rerrno;
+
+#endif /* RERRNO_H */

+ 1270 - 0
modules/registrar_scscf/save.c

@@ -0,0 +1,1270 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * 
+ * The initial version of this code was written by Dragos Vingarzan
+ * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
+ * Fruanhofer Institute. It was and still is maintained in a separate
+ * branch of the original SER. We are therefore migrating it to
+ * Kamailio/SR and look forward to maintaining it from here on out.
+ * 2011/2012 Smile Communications, Pty. Ltd.
+ * ported/maintained/improved by 
+ * Jason Penton (jason(dot)penton(at)smilecoms.com and
+ * Richard Good (richard(dot)good(at)smilecoms.com) as part of an 
+ * effort to add full IMS support to Kamailio/SR using a new and
+ * improved architecture
+ * 
+ * NB: Alot of this code was originally part of OpenIMSCore,
+ * FhG Fokus. 
+ * Copyright (C) 2004-2006 FhG Fokus
+ * Thanks for great work! This is an effort to 
+ * break apart the various CSCF functions into logically separate
+ * components. We hope this will drive wider use. We also feel
+ * that in this way the architecture is more complete and thereby easier
+ * to manage in the Kamailio/SR environment
+ *
+ * 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 "../../str.h"
+#include "../../socket_info.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 "../../mod_fix.h"
+#include "../../lib/kcore/cmpapi.h"
+#include "../../lib/kcore/statistics.h"
+
+#ifdef USE_TCP
+#include "../../tcp_server.h"
+#endif
+
+#include "../usrloc_scscf/usrloc.h"
+#include "common.h"
+#include "sip_msg.h"
+#include "rerrno.h"
+#include "reply.h"
+#include "reg_mod.h"
+#include "regtime.h"
+#include "path.h"
+#include "save.h"
+#include "config.h"
+#include "server_assignment.h"
+#include "userdata_parser.h"
+#include "../../lib/ims/ims_getters.h"
+
+#include "cxdx_sar.h"
+
+extern struct tm_binds tmb;
+extern int store_data_on_dereg; /**< should we store SAR user data on de-registration  */
+
+/*! \brief
+ * Calculate absolute expires value per contact as follows:
+ * 1) If the contact has expires value, use the value. If it
+ *    is not zero, add actual time to it
+ * 2) If the contact has no expires parameter, use expires
+ *    header field in the same way
+ * 3) If the message contained no expires header field, use
+ *    the default value
+ */
+static inline int calc_contact_expires(contact_t *c, unsigned int expires_hdr, int sos_reg) {
+    unsigned int r;
+
+    if (expires_hdr >= 0)
+        r = expires_hdr;
+    else {
+        r = (sos_reg > 0) ? default_registrar_cfg.em_default_expires : default_registrar_cfg.default_expires;
+        goto end;
+    }
+    if (c && c->expires)
+        str2int(&(c->expires->body), (unsigned int*) &r);
+    if (r > 0) {
+        if (!sos_reg && r < default_registrar_cfg.min_expires) {
+            r = default_registrar_cfg.min_expires;
+            goto end;
+        }
+        if (sos_reg && r < default_registrar_cfg.em_min_expires) {
+            r = default_registrar_cfg.em_min_expires;
+            goto end;
+        }
+    }
+    if (!sos_reg && r > default_registrar_cfg.max_expires) {
+        r = default_registrar_cfg.max_expires;
+        goto end;
+    }
+    if (sos_reg && r > default_registrar_cfg.em_max_expires)
+        r = default_registrar_cfg.em_min_expires;
+
+end:
+    LM_DBG("Calculated expires for contact is %d\n", r);
+    return time(NULL) + r;
+}
+
+/*! \brief
+ * Process request that contained a star, in that case, 
+ * we will remove all bindings with the given impu
+ * from the usrloc and return 200 OK response
+ */
+static inline int star(udomain_t* _d, str* _a) {
+    impurecord_t* r;
+    ucontact_t* c = 0;
+
+    ul.lock_udomain(_d, _a);
+
+    if (!ul.get_impurecord(_d, _a, &r)) {
+        c = r->contacts;
+    } else {
+        r = NULL;
+    }
+
+    if (ul.delete_impurecord(_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
+         * contacts
+         */
+        rerrno = R_UL_DEL_R;
+
+        if (!ul.get_impurecord(_d, _a, &r)) {
+            contact_for_header_t** contact_header = 0;
+            build_contact(c, contact_header);
+            free_contact_buf(*contact_header);
+        }
+        ul.unlock_udomain(_d, _a);
+        return -1;
+    }
+    ul.unlock_udomain(_d, _a);
+    return 0;
+}
+
+/*! \brief
+ */
+static struct socket_info *get_sock_hdr(struct sip_msg *msg) {
+    struct socket_info *sock;
+    struct hdr_field *hf;
+    str socks;
+    str hosts;
+    int port;
+    int proto;
+    char c;
+
+    if (parse_headers(msg, HDR_EOH_F, 0) == -1) {
+        LM_ERR("failed to parse message\n");
+        return 0;
+    }
+
+    for (hf = msg->headers; hf; hf = hf->next) {
+        if (cmp_hdrname_str(&hf->name, &sock_hdr_name) == 0)
+            break;
+    }
+
+    /* hdr found? */
+    if (hf == 0)
+        return 0;
+
+    trim_len(socks.len, socks.s, hf->body);
+    if (socks.len == 0)
+        return 0;
+
+    /*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) {
+        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) {
+        LM_ERR("non-local socket <%.*s>\n", socks.len, socks.s);
+        return 0;
+    }
+
+    LM_DBG("%d:<%.*s>:%d -> p=%p\n", proto, socks.len, socks.s, port_no, sock);
+
+    return sock;
+}
+
+/*! \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, unsigned int _e, unsigned int _f) {
+    static ucontact_info_t ci;
+    static str no_ua = str_init("n/a");
+    static str callid;
+    static str path_received = {0, 0};
+    static str path;
+    static str received = {0, 0};
+    static int received_found;
+    static unsigned int allowed, allow_parsed;
+    static struct sip_msg *m = 0;
+    int_str val;
+
+    if (_m != 0) {
+        memset(&ci, 0, sizeof (ucontact_info_t));
+
+        /* Get callid of the message */
+        callid = _m->callid->body;
+        trim_trailing(&callid);
+        if (callid.len > CALLID_MAX_SIZE) {
+            rerrno = R_CALLID_LEN;
+            LM_ERR("callid too long\n");
+            goto error;
+        }
+        ci.callid = &callid;
+
+        /* Get CSeq number of the message */
+        if (str2int(&get_cseq(_m)->number, (unsigned int*) &ci.cseq) < 0) {
+            rerrno = R_INV_CSEQ;
+            LM_ERR("failed to convert cseq number\n");
+            goto error;
+        }
+
+        /* set received socket */
+        if (_m->flags & sock_flag) {
+            ci.sock = get_sock_hdr(_m);
+            if (ci.sock == 0)
+                ci.sock = _m->rcv.bind_address;
+        } else {
+            ci.sock = _m->rcv.bind_address;
+        }
+
+        /* additional info from message */
+        if (parse_headers(_m, HDR_USERAGENT_F, 0) != -1 && _m->user_agent
+                && _m->user_agent->body.len > 0 && _m->user_agent->body.len < UA_MAX_SIZE) {
+            ci.user_agent = &_m->user_agent->body;
+        } else {
+            ci.user_agent = &no_ua;
+        }
+
+        /* extract Path headers */
+        if (path_enabled) {
+            if (build_path_vector(_m, &path, &path_received) < 0) {
+                rerrno = R_PARSE_PATH;
+                goto error;
+            }
+            if (path.len && path.s) {
+                ci.path = &path;
+                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 */
+    }
+
+    if (_c != 0) {
+        /* Calculate q value of the contact */
+        if (calc_contact_q(_c->q, &ci.q) < 0) {
+            rerrno = R_INV_Q;
+            LM_ERR("failed to calculate q\n");
+            goto error;
+        }
+
+        /* set expire time */
+        ci.expires = _e;
+
+        /* Get methods of contact */
+        if (_c->methods) {
+            if (parse_methods(&(_c->methods->body), &ci.methods) < 0) {
+                rerrno = R_PARSE;
+                LM_ERR("failed to parse contact methods\n");
+                goto error;
+            }
+        } else {
+            /* check on Allow hdr */
+            if (allow_parsed == 0) {
+                if (m && parse_allow(m) != -1) {
+                    allowed = get_allow_methods(m);
+                } else {
+                    allowed = ALL_METHODS;
+                }
+                allow_parsed = 1;
+            }
+            ci.methods = allowed;
+        }
+
+        /* get received */
+        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_found = 1;
+                }
+                ci.received = received;
+            }
+        }
+
+    }
+
+    return &ci;
+error:
+    return 0;
+}
+
+/**
+ * Deallocates memory used by a subscription.
+ * \note Must be called with the lock got to avoid races
+ * @param s - the ims_subscription to free
+ */
+void free_ims_subscription_data(ims_subscription *s) {
+    int i, j, k;
+    if (!s)
+        return;
+    /*	lock_get(s->lock); - must be called with the lock got */
+    for (i = 0; i < s->service_profiles_cnt; i++) {
+        for (j = 0; j < s->service_profiles[i].public_identities_cnt; j++) {
+            if (s->service_profiles[i].public_identities[j].public_identity.s)
+                shm_free(
+                    s->service_profiles[i].public_identities[j].public_identity.s);
+            if (s->service_profiles[i].public_identities[j].wildcarded_psi.s)
+                shm_free(
+                    s->service_profiles[i].public_identities[j].wildcarded_psi.s);
+
+        }
+        if (s->service_profiles[i].public_identities)
+            shm_free(s->service_profiles[i].public_identities);
+
+        for (j = 0; j < s->service_profiles[i].filter_criteria_cnt; j++) {
+            if (s->service_profiles[i].filter_criteria[j].trigger_point) {
+                for (k = 0;
+                        k
+                        < s->service_profiles[i].filter_criteria[j].trigger_point->spt_cnt;
+                        k++) {
+                    switch (s->service_profiles[i].filter_criteria[j].trigger_point->spt[k].type) {
+                        case IFC_REQUEST_URI:
+                            if (s->service_profiles[i].filter_criteria[j].trigger_point->spt[k].request_uri.s)
+                                shm_free(
+                                    s->service_profiles[i].filter_criteria[j].trigger_point->spt[k].request_uri.s);
+                            break;
+                        case IFC_METHOD:
+                            if (s->service_profiles[i].filter_criteria[j].trigger_point->spt[k].method.s)
+                                shm_free(
+                                    s->service_profiles[i].filter_criteria[j].trigger_point->spt[k].method.s);
+                            break;
+                        case IFC_SIP_HEADER:
+                            if (s->service_profiles[i].filter_criteria[j].trigger_point->spt[k].sip_header.header.s)
+                                shm_free(
+                                    s->service_profiles[i].filter_criteria[j].trigger_point->spt[k].sip_header.header.s);
+                            if (s->service_profiles[i].filter_criteria[j].trigger_point->spt[k].sip_header.content.s)
+                                shm_free(
+                                    s->service_profiles[i].filter_criteria[j].trigger_point->spt[k].sip_header.content.s);
+                            break;
+                        case IFC_SESSION_CASE:
+                            break;
+                        case IFC_SESSION_DESC:
+                            if (s->service_profiles[i].filter_criteria[j].trigger_point->spt[k].session_desc.line.s)
+                                shm_free(
+                                    s->service_profiles[i].filter_criteria[j].trigger_point->spt[k].session_desc.line.s);
+                            if (s->service_profiles[i].filter_criteria[j].trigger_point->spt[k].session_desc.content.s)
+                                shm_free(
+                                    s->service_profiles[i].filter_criteria[j].trigger_point->spt[k].session_desc.content.s);
+                            break;
+
+                    }
+                }
+                if (s->service_profiles[i].filter_criteria[j].trigger_point->spt)
+                    shm_free(
+                        s->service_profiles[i].filter_criteria[j].trigger_point->spt);
+                shm_free(
+                        s->service_profiles[i].filter_criteria[j].trigger_point);
+            }
+            if (s->service_profiles[i].filter_criteria[j].application_server.server_name.s)
+                shm_free(
+                    s->service_profiles[i].filter_criteria[j].application_server.server_name.s);
+            if (s->service_profiles[i].filter_criteria[j].application_server.service_info.s)
+                shm_free(
+                    s->service_profiles[i].filter_criteria[j].application_server.service_info.s);
+            if (s->service_profiles[i].filter_criteria[j].profile_part_indicator)
+                shm_free(
+                    s->service_profiles[i].filter_criteria[j].profile_part_indicator);
+        }
+        if (s->service_profiles[i].filter_criteria)
+            shm_free(s->service_profiles[i].filter_criteria);
+
+        if (s->service_profiles[i].cn_service_auth)
+            shm_free(s->service_profiles[i].cn_service_auth);
+
+        if (s->service_profiles[i].shared_ifc_set)
+            shm_free(s->service_profiles[i].shared_ifc_set);
+    }
+    if (s->service_profiles)
+        shm_free(s->service_profiles);
+    if (s->private_identity.s)
+        shm_free(s->private_identity.s);
+    lock_release(s->lock);
+    lock_destroy(s->lock);
+    lock_dealloc(s->lock);
+    shm_free(s);
+
+}
+
+/** Check if an impu record exists.
+ * 1. must be in registered state (impurecord)
+ * 2. must have at least one valid contact
+ */
+static inline int is_impu_registered(udomain_t* _d, str* public_identity) {
+    int res, ret = 1;
+    impurecord_t* impu;
+
+    ul.lock_udomain(_d, public_identity);
+    res = ul.get_impurecord(_d, public_identity, &impu);
+    if (res != 0) {
+        ul.unlock_udomain(_d, public_identity);
+        return 0;
+    } else {
+        //check reg status
+        if (impu->reg_state != IMPU_REGISTERED) {
+            LM_DBG("IMPU <%.*s> is not currently registered\n", public_identity->len, public_identity->s);
+            ret = 0;
+        }
+        //		//check valid contacts
+        //		if (res != 0) {
+        //			//valid contacts were found
+        //			ret = 1;
+        //		}
+    }
+    //ul.release_impurecord(impu);
+    ul.unlock_udomain(_d, public_identity);
+    return ret;
+
+}
+
+struct sip_msg* get_request_from_reply(struct sip_msg *reply) {
+    struct cell *t;
+    t = tmb.t_gett();
+    if (!t || t == (void*) - 1) {
+        LM_ERR("get_request_from_reply: Reply without transaction\n");
+        return 0;
+    }
+    if (t)
+        return t->uas.request;
+    else
+        return 0;
+}
+
+/**
+ * update the contacts for a public identity. Make sure you have the lock on the domain before calling this
+ * returns 0 on success, -1 on failure
+ */
+static inline int update_contacts_helper(struct sip_msg* msg, impurecord_t* impu_rec, int assignment_type, unsigned int expires_hdr) {
+    struct hdr_field* h;
+    contact_t* chi; //contact header information
+    ucontact_info_t* ci; //ucontact info
+    qvalue_t qvalue;
+    int sos = 0, expires;
+    struct ucontact* ucontact;
+
+    LM_DBG("updating the contacts for IMPU <%.*s>\n", impu_rec->public_identity.len, impu_rec->public_identity.s);
+
+    switch (assignment_type) {
+
+        case AVP_IMS_SAR_USER_DEREGISTRATION:
+            LM_DBG("update_contacts_helper: doing de-reg\n");
+            break;
+
+        case AVP_IMS_SAR_REGISTRATION:
+        case AVP_IMS_SAR_RE_REGISTRATION:
+
+            for (h = msg->contact; h; h = h->next) {
+                if (h->type == HDR_CONTACT_T && h->parsed) {
+
+                    for (chi = ((contact_body_t*) h->parsed)->contacts; chi; chi =
+                            chi->next) {
+                        if (calc_contact_q(chi->q, &qvalue) != 0) {
+                            LM_ERR("error on <%.*s>\n", chi->uri.len, chi->uri.s);
+                            goto error;
+                        }
+                        sos = cscf_get_sos_uri_param(chi->uri);
+                        if (sos < 0) {
+                            LM_ERR("Error trying to determine if this is a sos contact <%.*s>\n", chi->uri.len, chi->uri.s);
+                            goto error;
+                        }
+                        expires = calc_contact_expires(chi, expires_hdr, sos);
+                        //TODO: this next line will fail if the expires is in the main body and not the contact body //FIXED
+                        LM_DBG("Need to update contact: <%.*s>: "
+                                "q_value [%d],"
+                                "sos: [%d],"
+                                "expires [%ld]\n", chi->uri.len, chi->uri.s, qvalue, sos, expires - time(NULL));
+
+                        LM_DBG("packing contact information\n");
+                        if ((ci = pack_ci(msg, chi, expires, 0)) == 0) {
+                            LM_ERR("Failed to extract contact info\n");
+                            goto error;
+                        }
+
+                        LM_DBG("adding/updating contact based on prior existence\n");
+                        //stick the contacts into usrloc
+                        if (ul.get_ucontact(impu_rec, &chi->uri, ci->callid,
+                                ci->path, ci->cseq, &ucontact) != 0) {
+                            LM_DBG("inserting new contact\n");
+                            if (ul.insert_ucontact(impu_rec, &chi->uri, ci,
+                                    &ucontact) != 0) {
+                                LM_ERR("Error inserting contact <%.*s>\n", chi->uri.len, chi->uri.s);
+                                goto error;
+                            }
+                        } else {
+                            LM_DBG("Contact already exists - updating\n");
+                            if (ul.update_ucontact(impu_rec, ucontact, ci) != 0) {
+                                LM_ERR("Error updating contact <%.*s>\n", chi->uri.len, chi->uri.s);
+                                goto error;
+                            }
+                        }
+
+                    }
+                }
+            }
+            break;
+    }
+
+    return 0;
+
+error:
+    return -1;
+}
+
+/*NB remember to lock udomain pritor to calling this*/
+static inline int unregister_contact(udomain_t* _d, str* public_identity, contact_t* chi) {
+    impurecord_t* impu_rec;
+    struct ucontact* ucontact;
+    str callid = {0, 0};
+    str path = {0, 0};
+
+    if (ul.get_impurecord(_d, public_identity, &impu_rec) != 0) {
+        LM_ERR("Error, no public identity exists for <%.*s>\n", public_identity->len, public_identity->s);
+        goto error;
+    }
+
+    if (ul.get_ucontact(impu_rec, &chi->uri, &callid, &path, 0/*cseq*/, &ucontact) != 0) {
+        LM_ERR("Can't unregister contact that does not exist <%.*s>\n", chi->uri.len, chi->uri.s);
+        goto error;
+    }
+    if (ul.delete_ucontact(impu_rec, ucontact) != 0) {
+        LM_ERR("Failed to delete ucontact <%.*s>\n", chi->uri.len, chi->uri.s);
+    }
+    LM_DBG("Contact deleted successfully <%.*s>\n", chi->uri.len, chi->uri.s);
+    return 0;
+
+error:
+    return -1;
+}
+
+/* return
+ * 1 - success(contacts left) - unregistered contacts and remaining contacts in contact buffer for reply message
+ * 2 - success(no contacts left)
+ * <=0 - on failure
+ * */
+
+int update_contacts_new(struct sip_msg* msg, udomain_t* _d,
+        str* public_identity, int assignment_type, ims_subscription** s,
+        str* ccf1, str* ccf2, str* ecf1, str* ecf2, contact_for_header_t** contact_header) {
+    int reg_state, i, j;
+    ims_public_identity* pi = 0;
+    impurecord_t* impu_rec, *tmp_impu_rec;
+    int expires_hdr = -1; //by default registration doesn't expire
+    struct hdr_field* h;
+    contact_t* chi; //contact header information
+    qvalue_t qvalue;
+    int sos = 0;
+    ims_subscription* subscription = 0;
+    int first_unbarred_impu = 1;		//this is used to flag the IMPU as anchor for implicit set
+    int is_primary_impu = 0;
+    int ret = 1;
+
+    if (msg) {
+        expires_hdr = cscf_get_expires_hdr(msg, 0); //get the expires from the main body of the sip message (global)
+    }
+
+    switch (assignment_type) {
+        case AVP_IMS_SAR_REGISTRATION:
+            LM_DBG("updating contacts in REGISTRATION state\n");
+            reg_state = IMS_USER_REGISTERED;
+            if (!s) {
+                LM_ERR("no userdata supplied for AVP_IMS_SAR_REGISTRATION\n");
+                goto error;
+            }
+            for (i = 0; i < (*s)->service_profiles_cnt; i++)
+                for (j = 0; j < (*s)->service_profiles[i].public_identities_cnt; j++) {
+                    pi = &((*s)->service_profiles[i].public_identities[j]);
+                    ul.lock_udomain(_d, &pi->public_identity);
+                    if (first_unbarred_impu && !pi->barring) {
+                    	is_primary_impu = 1;
+                    	first_unbarred_impu = 0;
+                    } else {
+                    	is_primary_impu = 0;
+                    }
+                    if (ul.update_impurecord(_d, &pi->public_identity, reg_state,
+                            pi->barring, is_primary_impu, s, ccf1, ccf2, ecf1, ecf2, &impu_rec) != 0) {
+                        LM_ERR("Unable to update impurecord for <%.*s>\n", pi->public_identity.len, pi->public_identity.s);
+                        ul.unlock_udomain(_d, &pi->public_identity);
+                        goto error;
+                    }
+                    //here we can do something with impu_rec if we want but we must unlock when done
+                    //lets update the contacts
+                    if (update_contacts_helper(msg, impu_rec, assignment_type, expires_hdr) != 0) {
+                        LM_ERR("Failed trying to update contacts\n");
+                        ul.unlock_udomain(_d, &pi->public_identity);
+                        goto error;
+                    }
+                    ul.unlock_udomain(_d, &pi->public_identity);
+                }
+            //if we were successful up to this point, then we need to copy the contacts from main impu record (asserted IMPU) into the register response
+            ul.lock_udomain(_d, public_identity);
+            if (ul.get_impurecord(_d, public_identity, &impu_rec) != 0) {
+                LM_ERR("Error, we should have a record after registraion\n");
+                ul.unlock_udomain(_d, public_identity);
+                goto error;
+            }
+            //now build the contact buffer to be include in the reply message and unlock
+            build_contact(impu_rec->contacts, contact_header);
+            ul.unlock_udomain(_d, public_identity);
+            break;
+        case AVP_IMS_SAR_RE_REGISTRATION:
+            /* first update all the implicit IMPU based on the existing IMPUs subscription
+             * then, once done with the implicits, update the explicit with the new subscription data
+             */
+            LM_DBG("updating contacts in RE-REGISTRATION state\n");
+            reg_state = IMS_USER_REGISTERED;
+            ul.lock_udomain(_d, public_identity);
+            if (ul.get_impurecord(_d, public_identity, &impu_rec) != 0) {
+                LM_ERR("No IMPU record fond for re-registration...aborting\n");
+                ul.unlock_udomain(_d, public_identity);
+                goto error;
+            }
+
+            if (update_contacts_helper(msg, impu_rec, assignment_type, expires_hdr) != 0) { //update the contacts for the explicit IMPU
+                LM_ERR("Failed trying to update contacts for re-registration\n");
+                ul.unlock_udomain(_d, public_identity);
+                goto error;
+            }
+            //build the contact buffer for all registered contacts on explicit IMPU
+            build_contact(impu_rec->contacts, contact_header);
+
+            subscription = impu_rec->s;
+            if (!subscription) {
+                LM_ERR("No subscriber info associated with <%.*s>, not doing any implicit re-registrations\n", impu_rec->public_identity.len, impu_rec->public_identity.s);
+                //update the new subscription infor for the explicit IMPU
+                if (ul.update_impurecord(_d, public_identity, reg_state, 0 /*this is explicit so barring must be 0*/, 0, s, ccf1, ccf2,
+                        ecf1, ecf2, &impu_rec) != 0) {
+                    LM_ERR("Unable to update explicit impurecord for <%.*s>\n", public_identity->len, public_identity->s);
+                }
+                ul.unlock_udomain(_d, public_identity);
+                build_contact(impu_rec->contacts, contact_header);
+                break;
+            }
+
+            lock_get(subscription->lock);
+            subscription->ref_count++;
+            LM_DBG("ref count after add is now %d\n", subscription->ref_count);
+            lock_release(subscription->lock);
+            ul.unlock_udomain(_d, public_identity);
+
+            //now update the implicit set
+            for (i = 0; i < subscription->service_profiles_cnt; i++) {
+                for (j = 0; j < subscription->service_profiles[i].public_identities_cnt; j++) {
+                    pi = &((*s)->service_profiles[i].public_identities[j]);
+
+                    if (memcmp(public_identity->s, pi->public_identity.s, public_identity->len) == 0) { //we don't need to update the explicit IMPU
+                        LM_DBG("Ignoring explicit identity <%.*s>, updating later.....\n", public_identity->len, public_identity->s);
+                        continue;
+                    }
+                    ul.lock_udomain(_d, &pi->public_identity);
+
+                    //				LM_DBG("implicitly update IMPU <%.*s> for re-registration\n", pi->public_identity.len, pi->public_identity.s);
+                    //				if (ul.get_impurecord(_d, &pi->public_identity,	&tmp_impu_rec) != 0) {
+                    //					LM_ERR("Can't find IMPU for implicit re-registration update.....continuning\n");
+                    //					ul.unlock_udomain(_d, &pi->public_identity);
+                    //					continue;
+                    //				}
+
+                    //update the implicit IMPU with the new data
+                    if (ul.update_impurecord(_d, &pi->public_identity,
+                            reg_state, pi->barring, 0, s, ccf1, ccf2, ecf1, ecf2,
+                            &impu_rec) != 0) {
+                        LM_ERR("Unable to update implicit impurecord for <%.*s>.... continuing\n", pi->public_identity.len, pi->public_identity.s);
+                        ul.unlock_udomain(_d, &pi->public_identity);
+                        continue;
+                    }
+
+                    //update the contacts for the explicit IMPU
+                    if (update_contacts_helper(msg, impu_rec, assignment_type, expires_hdr) != 0) {
+                        LM_ERR("Failed trying to update contacts for re-registration of implicit IMPU <%.*s>.......continuing\n", pi->public_identity.len, pi->public_identity.s);
+                        ul.unlock_udomain(_d, &pi->public_identity);
+                        continue;
+                    }
+                    ul.unlock_udomain(_d, &pi->public_identity);
+                }
+            }
+            lock_get(subscription->lock);
+            subscription->ref_count--;
+            LM_DBG("ref count after sub is now %d\n", subscription->ref_count);
+            lock_release(subscription->lock);
+
+            //finally we update the explicit IMPU record with the new data
+            ul.lock_udomain(_d, public_identity);
+            if (ul.update_impurecord(_d, public_identity, reg_state, 0 /*this is explicit so barring must be 0*/, 0, s, ccf1, ccf2, ecf1, ecf2, &impu_rec) != 0) {
+                LM_ERR("Unable to update explicit impurecord for <%.*s>\n", public_identity->len, public_identity->s);
+            }
+            ul.unlock_udomain(_d, public_identity);
+            break;
+        case AVP_IMS_SAR_USER_DEREGISTRATION:
+            /*TODO: if its not a star lets find all the contact records and remove them*/
+            ul.lock_udomain(_d, public_identity);
+            for (h = msg->contact; h; h = h->next) {
+                if (h->type == HDR_CONTACT_T && h->parsed) {
+                    for (chi = ((contact_body_t*) h->parsed)->contacts; chi; chi = chi->next) {
+                        if (calc_contact_q(chi->q, &qvalue) != 0) {
+                            LM_ERR("error on <%.*s>\n", chi->uri.len, chi->uri.s);
+                            ul.unlock_udomain(_d, public_identity);
+                            goto error;
+                        }
+                        sos = cscf_get_sos_uri_param(chi->uri);
+                        if (sos < 0) {
+                            LM_ERR("Error trying to determine if this is a sos contact <%.*s>\n", chi->uri.len, chi->uri.s);
+                            ul.unlock_udomain(_d, public_identity);
+                            goto error;
+                        }
+                        calc_contact_expires(chi, expires_hdr, sos);
+                        if (unregister_contact(_d, public_identity, chi) != 0) {
+                            LM_ERR("Unable to remove contact <%.*s\n", chi->uri.len, chi->uri.s);
+
+                        }
+                        //add this contact to the successful unregistered in the 200OK so the PCSCF can also see what is de-registered
+                        build_expired_contact(chi, contact_header);
+                    }
+                }
+            }
+            /*now lets see if we still have any contacts left to decide on return value*/
+            if (ul.get_impurecord(_d, public_identity, &impu_rec) != 0) {
+                LM_ERR("Error retrieving impu record\n");
+                ul.unlock_udomain(_d, public_identity);
+                goto error;
+            }
+
+            if (impu_rec->contacts) {
+                LM_DBG("contacts still available\n");
+                //TODO: add all other remaining contacts to reply message (contacts still registered for this IMPU)
+                ret = 1;
+            } else {
+                LM_DBG("no more contacts available\n");
+                ret = 2;
+            }
+
+            //before we release IMPU record - lets save the subscription for implicit dereg
+            ims_subscription* subscription = impu_rec->s;
+
+            if (!subscription) {
+                LM_WARN("subscription is null..... continuing without de-registering implicit set\n");
+                unlock(subscription->lock);
+            } else {
+                lock(subscription->lock);
+                subscription->ref_count++; //this is so we can de-reg the implicit set just now without holding the lock on the current IMPU
+                unlock(subscription->lock);
+
+                ul.unlock_udomain(_d, public_identity);
+
+                //lock(subscription->lock);
+                for (i = 0; i < subscription->service_profiles_cnt; i++) {
+                    for (j = 0; j < subscription->service_profiles[i].public_identities_cnt; j++) {
+                        pi = &(subscription->service_profiles[i].public_identities[j]);
+                        if (memcmp(public_identity->s, pi->public_identity.s, public_identity->len) == 0) { //we don't need to update the explicit IMPU
+                            LM_DBG("Ignoring explicit identity <%.*s>, already de-reg/updated\n", public_identity->len, public_identity->s);
+                            ul.unlock_udomain(_d, &pi->public_identity);
+                            continue;
+                        }
+                        ul.lock_udomain(_d, &pi->public_identity);
+                        if (ul.get_impurecord(_d, &pi->public_identity, &tmp_impu_rec) != 0) {
+                            LM_ERR("Can't find IMPU for implicit de-registration update....continuing\n");
+                            ul.unlock_udomain(_d, &pi->public_identity);
+                            continue;
+                        }
+                        LM_DBG("Implicit deregistration of IMPU <%.*s>\n", pi->public_identity.len, pi->public_identity.s);
+                        for (h = msg->contact; h; h = h->next) {
+                            if (h->type == HDR_CONTACT_T && h->parsed) {
+                                for (chi = ((contact_body_t*) h->parsed)->contacts; chi; chi = chi->next) {
+                                    if (calc_contact_q(chi->q, &qvalue) != 0) {
+                                        LM_ERR("error on <%.*s>\n", chi->uri.len, chi->uri.s);
+                                        ul.unlock_udomain(_d, &pi->public_identity);
+                                        LM_ERR("no q value of implicit de-reg....continuing\n");
+                                        continue;
+                                    }
+                                    sos = cscf_get_sos_uri_param(chi->uri);
+                                    if (sos < 0) {
+                                        LM_ERR("Error trying to determine if this is a sos contact <%.*s>\n", chi->uri.len, chi->uri.s);
+                                        ul.unlock_udomain(_d, public_identity);
+                                        goto error;
+                                    }
+                                    calc_contact_expires(chi, expires_hdr, sos);
+                                    if (unregister_contact(_d, &pi->public_identity, chi) != 0) {
+                                        LM_ERR("Unable to remove contact <%.*s\n", chi->uri.len, chi->uri.s);
+
+                                    }
+                                }
+                            }
+                        }
+                        /*now lets see if we still have any contacts left to decide on return value*/
+                        if (ul.get_impurecord(_d, &pi->public_identity, &impu_rec) != 0) {
+                            LM_ERR("Error retrieving impu record for implicit de-reg....continuing\n");
+                            ul.unlock_udomain(_d, &pi->public_identity);
+                            continue;
+                        }
+
+                        if (impu_rec->contacts)
+                            LM_DBG("contacts still available after implicit dereg for IMPU: <%.*s>\n", pi->public_identity.len, pi->public_identity.s);
+                        else {
+                            LM_DBG("no contacts left after implicit dereg for IMPU: <%.*s>\n", pi->public_identity.len, pi->public_identity.s);
+                            //no contacts left for implicit IMPU so we can delete it
+                            LM_DBG("TODO: We should check if there are any subscribers to this reg left  - if so DO NOT DELETE");
+                            ul.delete_impurecord(_d, &pi->public_identity, tmp_impu_rec);
+                        }
+
+                        ul.unlock_udomain(_d, &pi->public_identity);
+                    }
+                }
+                lock(subscription->lock);
+                subscription->ref_count--;
+                unlock(subscription->lock);
+            }
+
+            if (ret == 2) {
+                //we need to remove the IMPU as there are no more contacts left
+                ul.lock_udomain(_d, public_identity);
+                LM_DBG("TODO: We should check if there are any subscribers to this reg left  - if so DO NOT DELETE");
+                ul.delete_impurecord(_d, public_identity, NULL);
+                ul.unlock_udomain(_d, public_identity);
+            }
+            break;
+
+        case AVP_IMS_SAR_UNREGISTERED_USER:
+            LM_DBG("updating contacts for UNREGISTERED_USER state\n");
+            reg_state = IMS_USER_UNREGISTERED;
+            for (i = 0; i < (*s)->service_profiles_cnt; i++)
+                for (j = 0; j < (*s)->service_profiles[i].public_identities_cnt;
+                        j++) {
+                    pi = &((*s)->service_profiles[i].public_identities[j]);
+                    ul.lock_udomain(_d, &pi->public_identity);
+                    if (ul.update_impurecord(_d, &pi->public_identity, reg_state,
+                            pi->barring, 0, s, ccf1, ccf2, ecf1, ecf2, &impu_rec)
+                            != 0) {
+                        LM_ERR("Unable to update impurecord for <%.*s>\n", pi->public_identity.len, pi->public_identity.s);
+                        ul.unlock_udomain(_d, &pi->public_identity);
+                        goto error;
+                    }
+                    ul.unlock_udomain(_d, &pi->public_identity);
+                }
+            //if we were successful up to this point, then we need to copy the contacts from main impu record (asserted IMPU) into the register response
+            break;
+        default:
+            LM_ERR("unimplemented assignment_type when trying to update contacts\n");
+    }
+    return ret;
+
+error:
+    return -1;
+
+}
+
+int assign_server_unreg(struct sip_msg* _m, char* str1, str* direction) {
+    str private_identity = {0, 0}, public_identity = {0, 0};
+    int assignment_type = AVP_IMS_SAR_NO_ASSIGNMENT;
+    int data_available = AVP_IMS_SAR_USER_DATA_NOT_AVAILABLE;
+    int require_user_data = 1;
+    rerrno = R_FINE;
+    tm_cell_t *t = 0;
+
+    saved_transaction_t* saved_t;
+    cfg_action_t* cfg_action;
+
+    sar_param_t* ap = (sar_param_t*) str1;
+    cfg_action = ap->paction->next;
+
+    LM_DBG("Assigning unregistered user for direction [%.*s]\n", direction->len, direction->s);
+
+    enum cscf_dialog_direction dir = cscf_get_dialog_direction(direction->s);
+    switch (dir) {
+        case CSCF_MOBILE_ORIGINATING:
+            public_identity = cscf_get_asserted_identity(_m);
+            break;
+        case CSCF_MOBILE_TERMINATING:
+            public_identity = cscf_get_public_identity_from_requri(_m);
+            break;
+        default:
+            LM_ERR("Bad dialog direction [%.*s]\n", direction->len, direction->s);
+            rerrno = R_SAR_FAILED;
+            goto error;
+    }
+
+    if (!public_identity.s || public_identity.len <= 0) {
+        LM_ERR("No public identity\n");
+        rerrno = R_SAR_FAILED;
+        goto error;
+    }
+
+    assignment_type = AVP_IMS_SAR_UNREGISTERED_USER;
+    data_available = AVP_IMS_SAR_USER_DATA_NOT_AVAILABLE; //TODO: check this
+
+
+    //before we send lets suspend the transaction
+    t = tmb.t_gett();
+    if (t == NULL || t == T_UNDEFINED) {
+        if (tmb.t_newtran(_m) < 0) {
+            LM_ERR("cannot create the transaction for SAR async\n");
+            rerrno = R_SAR_FAILED;
+            goto error;
+        }
+        t = tmb.t_gett();
+        if (t == NULL || t == T_UNDEFINED) {
+            LM_ERR("cannot lookup the transaction\n");
+            rerrno = R_SAR_FAILED;
+            goto error;
+        }
+    }
+
+    saved_t = shm_malloc(sizeof (saved_transaction_t));
+    if (!saved_t) {
+        LM_ERR("no more memory trying to save transaction state\n");
+        rerrno = R_SAR_FAILED;
+        goto error;
+
+    }
+    memset(saved_t, 0, sizeof (saved_transaction_t));
+    saved_t->act = cfg_action;
+
+    saved_t->expires = 1; //not a dereg as this is server_assign_unreg
+    saved_t->require_user_data = require_user_data;
+    saved_t->sar_assignment_type = assignment_type;
+    saved_t->domain = (udomain_t*) ap->param;
+
+    saved_t->contact_header = 0;
+
+    LM_DBG("Setting default AVP return code used for async callbacks to default as ERROR \n");
+    create_return_code(CSCF_RETURN_ERROR);
+
+    LM_DBG("Suspending SIP TM transaction\n");
+    if (tmb.t_suspend(_m, &saved_t->tindex, &saved_t->tlabel) < 0) {
+        LM_ERR("failed to suspend the TM processing\n");
+        free_saved_transaction_data(saved_t);
+        rerrno = R_SAR_FAILED;
+        goto error;
+    }
+
+    if (scscf_assign_server(_m, public_identity, private_identity, assignment_type, data_available, saved_t) != 0) {
+        LM_ERR("ERR:I_MAR: Error sending SAR or SAR time-out\n");
+        tmb.t_cancel_suspend(saved_t->tindex, saved_t->tlabel);
+        free_saved_transaction_data(saved_t);
+        rerrno = R_SAR_FAILED;
+        goto error;
+    }
+
+    if (public_identity.s && dir == CSCF_MOBILE_TERMINATING)
+        shm_free(public_identity.s); // shm_malloc in cscf_get_public_identity_from_requri
+
+    return CSCF_RETURN_BREAK;
+
+
+error:
+    update_stat(rejected_registrations, 1);
+    if ((is_route_type(REQUEST_ROUTE)) && (reg_send_reply(_m, 0) < 0))
+        return CSCF_RETURN_ERROR;
+    return CSCF_RETURN_BREAK;
+
+
+}
+
+/*!\brief
+ * Process REGISTER request and save it's contacts
+ * 1. ensure request
+ * 2. get impu, impi,realm,expiry,etc
+ * 3. check expiry
+ * 4. do SAR (reg,re-reg,dereg and no contacts left)
+ * 5. update usrloc based on SAR results
+ */
+//int save(struct sip_msg* msg, udomain_t* _d) {
+
+int save(struct sip_msg* msg, char* str1) {
+    int expires;
+    int require_user_data = 0;
+    int data_available;
+    contact_t* c;
+    int st;
+    str public_identity, private_identity, realm;
+    int sar_assignment_type = AVP_IMS_SAR_NO_ASSIGNMENT;
+
+    rerrno = R_FINE;
+    get_act_time();
+
+    tm_cell_t *t = 0;
+
+    saved_transaction_t* saved_t;
+    cfg_action_t* cfg_action;
+
+    contact_for_header_t* contact_header = 0;
+
+    sar_param_t* ap = (sar_param_t*) str1;
+    cfg_action = ap->paction->next;
+
+    //check which route block we are in - if not request then we fail out.
+    if (!is_route_type(REQUEST_ROUTE)) {
+        LM_ERR("Should only be called in REQUEST route\n");
+        rerrno = R_SAR_FAILED;
+        goto error;
+    }
+
+    /* check and see that all the required headers are available and can be parsed */
+    if (parse_message_for_register(msg) < 0) {
+        LM_ERR("Unable to parse register message correctly\n");
+        rerrno = R_SAR_FAILED;
+        goto error;
+    }
+    /** check we have valid contacts according to IMS spec. */
+    if (check_contacts(msg, &st) > 0) {
+        LM_ERR("contacts not valid for REGISTER\n");
+        rerrno = R_SAR_FAILED;
+        goto error;
+    }
+
+    /* get IMPU,IMPI,realm,expires */
+    public_identity = cscf_get_public_identity(msg);
+    if (public_identity.len <= 0 || !public_identity.s) {
+        LM_ERR("failed to extract Address Of Record\n");
+        rerrno = R_SAR_FAILED;
+        goto error;
+    }
+    realm = cscf_get_realm_from_uri(public_identity);
+    if (realm.len <= 0 || !realm.s) {
+        LM_ERR("can't get realm\n");
+        rerrno = R_SAR_FAILED;
+        goto error;
+    }
+
+    private_identity = cscf_get_private_identity(msg, realm);
+    if (private_identity.len <= 0 || !private_identity.s) {
+        LM_ERR("cant get private identity\n");
+    }
+
+    expires = cscf_get_max_expires(msg, 0); //check all contacts for max expires
+    if (expires != 0) { //if <0 then no expires was found in which case we treat as reg/re-reg with default expires.
+        if (is_impu_registered((udomain_t*) ap->param, &public_identity)) {
+            LM_DBG("preparing for SAR assignment for RE-REGISTRATION <%.*s>\n", public_identity.len, public_identity.s);
+            sar_assignment_type = AVP_IMS_SAR_RE_REGISTRATION;
+        } else {
+            LM_DBG("preparing for SAR assignment for new REGISTRATION <%.*s>\n", public_identity.len, public_identity.s);
+            sar_assignment_type = AVP_IMS_SAR_REGISTRATION;
+            require_user_data = 1;
+        }
+    } else {//de-reg
+        if (store_data_on_dereg) {
+            LM_DBG("preparing for SAR assignment for DE-REGISTRATION with storage <%.*s>\n", public_identity.len, public_identity.s);
+            sar_assignment_type = AVP_IMS_SAR_USER_DEREGISTRATION_STORE_SERVER_NAME;
+        } else {
+            LM_DBG("preparing for SAR assignment for DE-REGISTRATION <%.*s>\n", public_identity.len, public_identity.s);
+            sar_assignment_type = AVP_IMS_SAR_USER_DEREGISTRATION;
+        }
+
+        c = get_first_contact(msg);
+        if (!c && !st) { //no contacts found - no need to do anything
+            LM_ERR("no contacts found for de-registration and no star\n");
+            rerrno = R_SAR_FAILED;
+            goto error;
+        }
+
+        //if we get here there are contacts to deregister, BUT we only send a SAR if there are no contacts (valid) left.
+        if (!st) { //if it is a star then we delete all contacts and send a SAR
+            //unregister the requested contacts, if none left at the end then send a SAR, otherwise return successfully
+            LM_DBG("need to unregister contacts\n");
+            //lets update the contacts - we need to know if all were deleted or not for the public identity
+            int res = update_contacts_new(msg, (udomain_t*) ap->param, &public_identity, sar_assignment_type, 0, 0, 0, 0, 0, &contact_header);
+            if (res <= 0) {
+                LM_ERR("Error processing REGISTER for de-registration\n");
+                free_contact_buf(contact_header);
+                rerrno = R_SAR_FAILED;
+                goto error;
+            } else if (res == 2) {
+                //send sar
+                LM_DBG("no contacts left after de-registration, doing SAR\n");
+            } else { //res=1
+                //still contacts left so return success
+                LM_DBG("contacts still available after deregister.... not doing SAR\n");
+                //we must send the de reged contacts in this! so we only free the contact header after sending
+                //free_contact_buf(contact_header);
+                rerrno = R_FINE;
+                goto no_sar;
+            }
+        }
+    }
+    if (require_user_data)
+        data_available = AVP_IMS_SAR_USER_DATA_NOT_AVAILABLE;
+    else
+        data_available = AVP_IMS_SAR_USER_DATA_ALREADY_AVAILABLE;
+
+    //before we send lets suspend the transaction
+    t = tmb.t_gett();
+    if (t == NULL || t == T_UNDEFINED) {
+        if (tmb.t_newtran(msg) < 0) {
+            LM_ERR("cannot create the transaction for SAR async\n");
+            rerrno = R_SAR_FAILED;
+            goto error;
+        }
+        t = tmb.t_gett();
+        if (t == NULL || t == T_UNDEFINED) {
+            LM_ERR("cannot lookup the transaction\n");
+            rerrno = R_SAR_FAILED;
+            goto error;
+        }
+    }
+
+    saved_t = shm_malloc(sizeof (saved_transaction_t));
+    if (!saved_t) {
+        LM_ERR("no more memory trying to save transaction state\n");
+        free_contact_buf(contact_header);
+        rerrno = R_SAR_FAILED;
+        goto error;
+
+    }
+    memset(saved_t, 0, sizeof (saved_transaction_t));
+    saved_t->act = cfg_action;
+
+    //this is not server assign unreg - this is a save
+    saved_t->expires = expires;
+    saved_t->require_user_data = require_user_data;
+    saved_t->sar_assignment_type = sar_assignment_type;
+    saved_t->domain = (udomain_t*) ap->param;
+
+    saved_t->public_identity.s = (char*) shm_malloc(public_identity.len + 1);
+    if (!saved_t->public_identity.s) {
+        LM_ERR("no more memory trying to save transaction state : callid\n");
+        shm_free(saved_t);
+        free_contact_buf(contact_header);
+        rerrno = R_SAR_FAILED;
+        goto error;
+    }
+    memset(saved_t->public_identity.s, 0, public_identity.len + 1);
+    memcpy(saved_t->public_identity.s, public_identity.s, public_identity.len);
+    saved_t->public_identity.len = public_identity.len;
+
+    saved_t->contact_header = contact_header;
+
+    create_return_code(CSCF_RETURN_ERROR);
+
+    LM_DBG("Suspending SIP TM transaction\n");
+    if (tmb.t_suspend(msg, &saved_t->tindex, &saved_t->tlabel) < 0) {
+        LM_ERR("failed to suspend the TM processing\n");
+        free_saved_transaction_data(saved_t);
+        rerrno = R_SAR_FAILED;
+        goto error;
+    }
+
+    if (scscf_assign_server(msg, public_identity, private_identity, sar_assignment_type, data_available, saved_t) != 0) {
+        LM_ERR("ERR:I_MAR: Error sending SAR or SAR time-out\n");
+        tmb.t_cancel_suspend(saved_t->tindex, saved_t->tlabel);
+        free_saved_transaction_data(saved_t);
+        rerrno = R_SAR_FAILED;
+        goto error;
+    }
+
+    return CSCF_RETURN_BREAK;
+
+no_sar:
+
+    update_stat(accepted_registrations, 1);
+
+        //we must send the de reged contacts in this! so we only free the contact header after sending
+    /* Only send reply upon request, not upon reply */
+    if ((is_route_type(REQUEST_ROUTE)) && (reg_send_reply(msg, contact_header) < 0)) {
+        free_contact_buf(contact_header);
+        return CSCF_RETURN_ERROR;
+    }
+    free_contact_buf(contact_header);
+    return CSCF_RETURN_BREAK;
+
+error:
+    update_stat(rejected_registrations, 1);
+    if ((is_route_type(REQUEST_ROUTE)) && (reg_send_reply(msg, contact_header) < 0))
+        return CSCF_RETURN_ERROR;
+    return CSCF_RETURN_BREAK;
+
+}
+
+int unregister(struct sip_msg* _m, char* _d, char* _uri) {
+    str aor = {0, 0};
+    str uri = {0, 0};
+
+    if (fixup_get_svalue(_m, (gparam_p) _uri, &uri) != 0 || uri.len <= 0) {
+        LM_ERR("invalid uri parameter\n");
+        return -1;
+    }
+
+    if (extract_aor(&uri, &aor) < 0) {
+        LM_ERR("failed to extract Address Of Record\n");
+        return -1;
+    }
+
+    if (star((udomain_t*) _d, &aor) < 0) {
+        LM_ERR("error unregistering user [%.*s]\n", aor.len, aor.s);
+        return -1;
+    }
+    return 1;
+}
+

+ 70 - 0
modules/registrar_scscf/save.h

@@ -0,0 +1,70 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * 
+ * The initial version of this code was written by Dragos Vingarzan
+ * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
+ * Fruanhofer Institute. It was and still is maintained in a separate
+ * branch of the original SER. We are therefore migrating it to
+ * Kamailio/SR and look forward to maintaining it from here on out.
+ * 2011/2012 Smile Communications, Pty. Ltd.
+ * ported/maintained/improved by 
+ * Jason Penton (jason(dot)penton(at)smilecoms.com and
+ * Richard Good (richard(dot)good(at)smilecoms.com) as part of an 
+ * effort to add full IMS support to Kamailio/SR using a new and
+ * improved architecture
+ * 
+ * NB: Alot of this code was originally part of OpenIMSCore,
+ * FhG Fokus. 
+ * Copyright (C) 2004-2006 FhG Fokus
+ * Thanks for great work! This is an effort to 
+ * break apart the various CSCF functions into logically separate
+ * components. We hope this will drive wider use. We also feel
+ * that in this way the architecture is more complete and thereby easier
+ * to manage in the Kamailio/SR environment
+ *
+ * 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 SAVE_H
+#define SAVE_H
+
+
+#include "../../parser/msg_parser.h"
+#include "../usrloc_scscf/usrloc.h"
+#include "reply.h"
+
+
+/*! \brief
+ * Process REGISTER request and save it's contacts
+ */
+int assign_server_unreg(struct sip_msg* _m, char* str1, str* direction);
+
+int save(struct sip_msg* msg, char* str1);
+
+int unregister(struct sip_msg* _m, char* _d, char* _uri);
+
+int update_contacts_new(struct sip_msg* msg, udomain_t* _d,
+        str* public_identity, int assignment_type, ims_subscription** s,
+        str* ccf1, str* ccf2, str* ecf1, str* ecf2, contact_for_header_t** contact_header);
+
+
+#endif /* SAVE_H */

+ 66 - 0
modules/registrar_scscf/screensharing.log

@@ -0,0 +1,66 @@
+DEBUG 10-24 14:36:45.015 CoreScreenShare.java 256 111 org.openmeetings.screen.webstart.CoreScreenShare [javawsApplicationMain] - arg: webex.it.za.smilecoms.com
+DEBUG 10-24 14:36:45.020 CoreScreenShare.java 261 111 org.openmeetings.screen.webstart.CoreScreenShare [javawsApplicationMain] - arg: openmeetings/10
+DEBUG 10-24 14:36:45.020 CoreScreenShare.java 261 111 org.openmeetings.screen.webstart.CoreScreenShare [javawsApplicationMain] - arg: 1935
+DEBUG 10-24 14:36:45.020 CoreScreenShare.java 261 111 org.openmeetings.screen.webstart.CoreScreenShare [javawsApplicationMain] - arg: 8bc4a82d9529045531a9f06b2149e6db
+DEBUG 10-24 14:36:45.020 CoreScreenShare.java 261 111 org.openmeetings.screen.webstart.CoreScreenShare [javawsApplicationMain] - arg: Desktop sharer;Click Start to publish your screen;Start sharing;Pause sharing;Select your screen area:;Change width;Change height;X-Offset;Y-Offset;Width:;Height:;Connection was closed by server;Show mouse position to viewers;Recording;You may record and share your screen at the same time. To enable others to see your screen just hit the start button on the top.;Start recording;Stop recording;Close;Quality of the screen share:;Very high quality;High quality;Medium quality;Low quality;Publish;Start Publish;Stop Publish;Host;Publish App;Publish Id;Reduce the width of the SharingScreen before you try to move it left;Reduce the height of the SharingScreen before you try to move it bottom;Reduce the x of the SharingScreen before you try to make it wider;Reduce the y of the SharingScreen before you try to make it higher;Fill these settings stream your screen data to 3th party providers like justin.tv;Please start Screen sharing to be able to publish it;Moderator permission required to start recording
+DEBUG 10-24 14:36:45.020 CoreScreenShare.java 261 111 org.openmeetings.screen.webstart.CoreScreenShare [javawsApplicationMain] - arg: 1
+DEBUG 10-24 14:36:45.021 CoreScreenShare.java 262 111 org.openmeetings.screen.webstart.CoreScreenShare [javawsApplicationMain] - arg: 1
+DEBUG 10-24 14:36:45.021 CoreScreenShare.java 262 111 org.openmeetings.screen.webstart.CoreScreenShare [javawsApplicationMain] - arg: 1
+DEBUG 10-24 14:36:45.021 CoreScreenShare.java 262 111 org.openmeetings.screen.webstart.CoreScreenShare [javawsApplicationMain] - arg: true
+DEBUG 10-24 14:36:45.022 CoreScreenShare.java 263 131 org.openmeetings.screen.webstart.CoreScreenShare [javawsApplicationMain] - labelTexts :: Desktop sharer;Click Start to publish your screen;Start sharing;Pause sharing;Select your screen area:;Change width;Change height;X-Offset;Y-Offset;Width:;Height:;Connection was closed by server;Show mouse position to viewers;Recording;You may record and share your screen at the same time. To enable others to see your screen just hit the start button on the top.;Start recording;Stop recording;Close;Quality of the screen share:;Very high quality;High quality;Medium quality;Low quality;Publish;Start Publish;Stop Publish;Host;Publish App;Publish Id;Reduce the width of the SharingScreen before you try to move it left;Reduce the height of the SharingScreen before you try to move it bottom;Reduce the x of the SharingScreen before you try to make it wider;Reduce the y of the SharingScreen before you try to make it higher;Fill these settings stream your screen data to 3th party providers like justin.tv;Please start Screen sharing to be able to publish it;Moderator permission required to start recording
+DEBUG 10-24 14:36:45.022 CoreScreenShare.java 263 133 org.openmeetings.screen.webstart.CoreScreenShare [javawsApplicationMain] - textArray Length 36
+DEBUG 10-24 14:36:45.022 CoreScreenShare.java 263 136 org.openmeetings.screen.webstart.CoreScreenShare [javawsApplicationMain] - 0 :: Desktop sharer
+DEBUG 10-24 14:36:45.022 CoreScreenShare.java 263 136 org.openmeetings.screen.webstart.CoreScreenShare [javawsApplicationMain] - 1 :: Click Start to publish your screen
+DEBUG 10-24 14:36:45.023 CoreScreenShare.java 264 136 org.openmeetings.screen.webstart.CoreScreenShare [javawsApplicationMain] - 2 :: Start sharing
+DEBUG 10-24 14:36:45.023 CoreScreenShare.java 264 136 org.openmeetings.screen.webstart.CoreScreenShare [javawsApplicationMain] - 3 :: Pause sharing
+DEBUG 10-24 14:36:45.023 CoreScreenShare.java 264 136 org.openmeetings.screen.webstart.CoreScreenShare [javawsApplicationMain] - 4 :: Select your screen area:
+DEBUG 10-24 14:36:45.023 CoreScreenShare.java 264 136 org.openmeetings.screen.webstart.CoreScreenShare [javawsApplicationMain] - 5 :: Change width
+DEBUG 10-24 14:36:45.024 CoreScreenShare.java 265 136 org.openmeetings.screen.webstart.CoreScreenShare [javawsApplicationMain] - 6 :: Change height
+DEBUG 10-24 14:36:45.024 CoreScreenShare.java 265 136 org.openmeetings.screen.webstart.CoreScreenShare [javawsApplicationMain] - 7 :: X-Offset
+DEBUG 10-24 14:36:45.024 CoreScreenShare.java 265 136 org.openmeetings.screen.webstart.CoreScreenShare [javawsApplicationMain] - 8 :: Y-Offset
+DEBUG 10-24 14:36:45.024 CoreScreenShare.java 265 136 org.openmeetings.screen.webstart.CoreScreenShare [javawsApplicationMain] - 9 :: Width:
+DEBUG 10-24 14:36:45.025 CoreScreenShare.java 266 136 org.openmeetings.screen.webstart.CoreScreenShare [javawsApplicationMain] - 10 :: Height:
+DEBUG 10-24 14:36:45.025 CoreScreenShare.java 266 136 org.openmeetings.screen.webstart.CoreScreenShare [javawsApplicationMain] - 11 :: Connection was closed by server
+DEBUG 10-24 14:36:45.025 CoreScreenShare.java 266 136 org.openmeetings.screen.webstart.CoreScreenShare [javawsApplicationMain] - 12 :: Show mouse position to viewers
+DEBUG 10-24 14:36:45.025 CoreScreenShare.java 266 136 org.openmeetings.screen.webstart.CoreScreenShare [javawsApplicationMain] - 13 :: Recording
+DEBUG 10-24 14:36:45.025 CoreScreenShare.java 266 136 org.openmeetings.screen.webstart.CoreScreenShare [javawsApplicationMain] - 14 :: You may record and share your screen at the same time. To enable others to see your screen just hit the start button on the top.
+DEBUG 10-24 14:36:45.026 CoreScreenShare.java 267 136 org.openmeetings.screen.webstart.CoreScreenShare [javawsApplicationMain] - 15 :: Start recording
+DEBUG 10-24 14:36:45.026 CoreScreenShare.java 267 136 org.openmeetings.screen.webstart.CoreScreenShare [javawsApplicationMain] - 16 :: Stop recording
+DEBUG 10-24 14:36:45.026 CoreScreenShare.java 267 136 org.openmeetings.screen.webstart.CoreScreenShare [javawsApplicationMain] - 17 :: Close
+DEBUG 10-24 14:36:45.026 CoreScreenShare.java 267 136 org.openmeetings.screen.webstart.CoreScreenShare [javawsApplicationMain] - 18 :: Quality of the screen share:
+DEBUG 10-24 14:36:45.027 CoreScreenShare.java 268 136 org.openmeetings.screen.webstart.CoreScreenShare [javawsApplicationMain] - 19 :: Very high quality
+DEBUG 10-24 14:36:45.027 CoreScreenShare.java 268 136 org.openmeetings.screen.webstart.CoreScreenShare [javawsApplicationMain] - 20 :: High quality
+DEBUG 10-24 14:36:45.027 CoreScreenShare.java 268 136 org.openmeetings.screen.webstart.CoreScreenShare [javawsApplicationMain] - 21 :: Medium quality
+DEBUG 10-24 14:36:45.027 CoreScreenShare.java 268 136 org.openmeetings.screen.webstart.CoreScreenShare [javawsApplicationMain] - 22 :: Low quality
+DEBUG 10-24 14:36:45.027 CoreScreenShare.java 268 136 org.openmeetings.screen.webstart.CoreScreenShare [javawsApplicationMain] - 23 :: Publish
+DEBUG 10-24 14:36:45.027 CoreScreenShare.java 268 136 org.openmeetings.screen.webstart.CoreScreenShare [javawsApplicationMain] - 24 :: Start Publish
+DEBUG 10-24 14:36:45.027 CoreScreenShare.java 268 136 org.openmeetings.screen.webstart.CoreScreenShare [javawsApplicationMain] - 25 :: Stop Publish
+DEBUG 10-24 14:36:45.028 CoreScreenShare.java 269 136 org.openmeetings.screen.webstart.CoreScreenShare [javawsApplicationMain] - 26 :: Host
+DEBUG 10-24 14:36:45.028 CoreScreenShare.java 269 136 org.openmeetings.screen.webstart.CoreScreenShare [javawsApplicationMain] - 27 :: Publish App
+DEBUG 10-24 14:36:45.028 CoreScreenShare.java 269 136 org.openmeetings.screen.webstart.CoreScreenShare [javawsApplicationMain] - 28 :: Publish Id
+DEBUG 10-24 14:36:45.028 CoreScreenShare.java 269 136 org.openmeetings.screen.webstart.CoreScreenShare [javawsApplicationMain] - 29 :: Reduce the width of the SharingScreen before you try to move it left
+DEBUG 10-24 14:36:45.028 CoreScreenShare.java 269 136 org.openmeetings.screen.webstart.CoreScreenShare [javawsApplicationMain] - 30 :: Reduce the height of the SharingScreen before you try to move it bottom
+DEBUG 10-24 14:36:45.028 CoreScreenShare.java 269 136 org.openmeetings.screen.webstart.CoreScreenShare [javawsApplicationMain] - 31 :: Reduce the x of the SharingScreen before you try to make it wider
+DEBUG 10-24 14:36:45.028 CoreScreenShare.java 269 136 org.openmeetings.screen.webstart.CoreScreenShare [javawsApplicationMain] - 32 :: Reduce the y of the SharingScreen before you try to make it higher
+DEBUG 10-24 14:36:45.029 CoreScreenShare.java 270 136 org.openmeetings.screen.webstart.CoreScreenShare [javawsApplicationMain] - 33 :: Fill these settings stream your screen data to 3th party providers like justin.tv
+DEBUG 10-24 14:36:45.029 CoreScreenShare.java 270 136 org.openmeetings.screen.webstart.CoreScreenShare [javawsApplicationMain] - 34 :: Please start Screen sharing to be able to publish it
+DEBUG 10-24 14:36:45.029 CoreScreenShare.java 270 136 org.openmeetings.screen.webstart.CoreScreenShare [javawsApplicationMain] - 35 :: Moderator permission required to start recording
+DEBUG 10-24 14:36:45.029 CoreScreenShare.java 270 143 org.openmeetings.screen.webstart.CoreScreenShare [javawsApplicationMain] - host: webex.it.za.smilecoms.com, app: openmeetings/10, port: 1935, publish: 8bc4a82d9529045531a9f06b2149e6db
+DEBUG 10-24 14:36:45.600 ScreenSharerFrame.java 841 698 org.openmeetings.screen.webstart.gui.ScreenSharerFrame [javawsApplicationMain] - calcRescaleFactors -- 
+DEBUG 10-24 14:36:45.601 ScreenSharerFrame.java 842 715 org.openmeetings.screen.webstart.gui.ScreenSharerFrame [javawsApplicationMain] - resize: X:1920 Y: 1080
+DEBUG 10-24 14:36:45.681 CoreScreenShare.java 922 170 org.openmeetings.screen.webstart.CoreScreenShare [javawsApplicationMain] - initialized
+DEBUG 10-24 14:36:59.142 ScreenSharerFrame.java 14383 698 org.openmeetings.screen.webstart.gui.ScreenSharerFrame [AWT-EventQueue-0] - calcRescaleFactors -- 
+DEBUG 10-24 14:36:59.142 ScreenSharerFrame.java 14383 715 org.openmeetings.screen.webstart.gui.ScreenSharerFrame [AWT-EventQueue-0] - resize: X:1920 Y: 1080
+DEBUG 10-24 14:37:01.478 CoreScreenShare.java 16719 250 org.openmeetings.screen.webstart.CoreScreenShare [AWT-EventQueue-0] - captureScreenStart
+DEBUG 10-24 14:37:01.872 RTMPScreenShare.java 17113 54 org.openmeetings.screen.webstart.RTMPScreenShare [NioProcessor-2] - connection opened
+DEBUG 10-24 14:37:02.173 CoreScreenShare.java 17414 209 org.openmeetings.screen.webstart.CoreScreenShare [NioProcessor-2] - ########## setConnectionAsSharingClient
+DEBUG 10-24 14:37:02.601 CoreScreenShare.java 17842 763 org.openmeetings.screen.webstart.CoreScreenShare [NioProcessor-2] - createPublishStream result stream id: 1
+DEBUG 10-24 14:37:02.601 CoreScreenShare.java 17842 765 org.openmeetings.screen.webstart.CoreScreenShare [NioProcessor-2] - publishing video by name: 8bc4a82d9529045531a9f06b2149e6db
+DEBUG 10-24 14:37:02.603 CoreScreenShare.java 17844 768 org.openmeetings.screen.webstart.CoreScreenShare [NioProcessor-2] - setup capture thread
+DEBUG 10-24 14:37:02.604 CoreScreenShare.java 17845 770 org.openmeetings.screen.webstart.CoreScreenShare [NioProcessor-2] - setup capture thread vScreenSpinnerWidth 1920
+DEBUG 10-24 14:37:02.604 CoreScreenShare.java 17845 772 org.openmeetings.screen.webstart.CoreScreenShare [NioProcessor-2] - setup capture thread vScreenSpinnerHeight 1080
+DEBUG 10-24 14:37:02.659 CoreScreenShare.java 17900 344 org.openmeetings.screen.webstart.CoreScreenShare [NioProcessor-2] - onStreamEvent Invoke: Service: null Method: onStatus Num Params: 1 0: {clientid=1, level=status, details=8bc4a82d9529045531a9f06b2149e6db, description=, code=NetStream.Publish.Start}
+DEBUG 10-24 14:37:02.659 CoreScreenShare.java 17900 351 org.openmeetings.screen.webstart.CoreScreenShare [NioProcessor-2] - onStreamEvent Publish start
+DEBUG 10-24 14:38:24.923 CoreScreenShare.java 100164 272 org.openmeetings.screen.webstart.CoreScreenShare [AWT-EventQueue-0] - INVOKE screenSharerAction
+DEBUG 10-24 14:38:24.929 CoreScreenShare.java 100170 328 org.openmeetings.screen.webstart.CoreScreenShare [NioProcessor-2] - ScreenShare stopStream
+DEBUG 10-24 14:38:24.935 RTMPScreenShare.java 100176 61 org.openmeetings.screen.webstart.RTMPScreenShare [NioProcessor-2] - connection closed

+ 92 - 0
modules/registrar_scscf/server_assignment.c

@@ -0,0 +1,92 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * 
+ * The initial version of this code was written by Dragos Vingarzan
+ * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
+ * Fruanhofer Institute. It was and still is maintained in a separate
+ * branch of the original SER. We are therefore migrating it to
+ * Kamailio/SR and look forward to maintaining it from here on out.
+ * 2011/2012 Smile Communications, Pty. Ltd.
+ * ported/maintained/improved by 
+ * Jason Penton (jason(dot)penton(at)smilecoms.com and
+ * Richard Good (richard(dot)good(at)smilecoms.com) as part of an 
+ * effort to add full IMS support to Kamailio/SR using a new and
+ * improved architecture
+ * 
+ * NB: Alot of this code was originally part of OpenIMSCore,
+ * FhG Fokus. 
+ * Copyright (C) 2004-2006 FhG Fokus
+ * Thanks for great work! This is an effort to 
+ * break apart the various CSCF functions into logically separate
+ * components. We hope this will drive wider use. We also feel
+ * that in this way the architecture is more complete and thereby easier
+ * to manage in the Kamailio/SR environment
+ *
+ * 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 "server_assignment.h"
+#include "reg_mod.h"
+#include "../../lib/ims/ims_getters.h"
+#include "../cdp/diameter_ims_code_avp.h"
+#include "cxdx_sar.h"
+
+extern str scscf_name_str;
+/* Does the Server Assignment procedures, assigning this S-CSCF to the user.
+ * Covered cases:
+ * AVP_IMS_SAR_NO_ASSIGNMENT							= 0			
+ * AVP_IMS_SAR_REGISTRATION								= 1,		YES,HERE
+ * AVP_IMS_SAR_RE_REGISTRATION							= 2,
+ * AVP_IMS_SAR_UNREGISTERED_USER						= 3,		in S_assign_server_unreg
+ * AVP_IMS_SAR_TIMEOUT_DEREGISTRATION					= 4,
+ * AVP_IMS_SAR_USER_DEREGISTRATION						= 5,		YES,HERE
+ * AVP_IMS_SAR_TIMEOUT_DEREGISTRATION_STORE_SERVER_NAME = 6,
+ * AVP_IMS_SAR_USER_DEREGISTRATION_STORE_SERVER_NAME	= 7,		YES,HERE
+ * AVP_IMS_SAR_ADMINISTRATIVE_DEREGISTRATION			= 8,
+ * AVP_IMS_SAR_AUTHENTICATION_FAILURE					= 9,
+ * AVP_IMS_SAR_AUTHENTICATION_TIMEOUT					= 10,
+ * AVP_IMS_SAR_DEREGISTRATION_TOO_MUCH_DATA
+ * 
+ * @param msg - the SIP REGISTER message (that is authorized)
+ * @param str2 - not used
+ * @returns true if ok, false if not, break on error
+ */
+int scscf_assign_server(struct sip_msg *msg,
+		str public_identity, str private_identity, int assignment_type,
+		int data_available, saved_transaction_t* transaction_data) {
+
+
+        int result = -1;        
+
+	if (assignment_type != AVP_IMS_SAR_REGISTRATION
+			&& assignment_type != AVP_IMS_SAR_RE_REGISTRATION
+			&& assignment_type != AVP_IMS_SAR_USER_DEREGISTRATION
+			&& assignment_type != AVP_IMS_SAR_USER_DEREGISTRATION_STORE_SERVER_NAME
+			&& assignment_type != AVP_IMS_SAR_UNREGISTERED_USER) {
+		LM_DBG("Invalid SAR assignment type\n");
+                return result;
+	}
+
+        result = cxdx_send_sar(msg, public_identity, private_identity,
+			scscf_name_str, assignment_type, data_available, transaction_data);
+        
+        return result;
+}

+ 63 - 0
modules/registrar_scscf/server_assignment.h

@@ -0,0 +1,63 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ * Copyright (C) 2012 Smile Communications, [email protected]
+ *
+ * The initial version of this code was written by Dragos Vingarzan
+ * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
+ * Fruanhofer Institute. It was and still is maintained in a separate
+ * branch of the original SER. We are therefore migrating it to
+ * Kamailio/SR and look forward to maintaining it from here on out.
+ * 2011/2012 Smile Communications, Pty. Ltd.
+ * ported/maintained/improved by
+ * Jason Penton (jason(dot)penton(at)smilecoms.com and
+ * Richard Good (richard(dot)good(at)smilecoms.com) as part of an
+ * effort to add full IMS support to Kamailio/SR using a new and
+ * improved architecture
+ *
+ * NB: Alot of this code was originally part of OpenIMSCore,
+ * FhG Fokus.
+ * Copyright (C) 2004-2006 FhG Fokus
+ * Thanks for great work! This is an effort to
+ * break apart the various CSCF functions into logically separate
+ * components. We hope this will drive wider use. We also feel
+ * that in this way the architecture is more complete and thereby easier
+ * to manage in the Kamailio/SR environment
+ *
+ * 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 SERVER_ASSIGNMENT_H
+#define	SERVER_ASSIGNMENT_H
+
+#include "reg_mod.h"
+#include "../cdp/diameter.h"
+#include "reply.h"
+#include "cxdx_sar.h"
+
+extern struct cxdx_binds cxdxb;
+
+
+int scscf_assign_server(struct sip_msg *msg,
+		str public_identity, str private_identity, int assignment_type,
+		int data_available, saved_transaction_t* transaction_data);
+
+
+#endif	/* SERVER_ASSIGNMENT_H */
+

+ 284 - 0
modules/registrar_scscf/sip_msg.c

@@ -0,0 +1,284 @@
+/*
+ * $Id$
+ *
+ * SIP message related functions
+ *
+ * Copyright (C) 2001-2003 FhG Fokus
+ *
+ * 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
+ */
+
+/*!
+ * \file
+ * \brief SIP registrar module - SIP message related functions
+ * \ingroup registrar   
+ */
+
+#include "../../parser/hf.h"
+#include "../../dprint.h"
+#include "../../parser/parse_expires.h"  
+#include "../../ut.h"
+#include "../../qvalue.h"
+#include "reg_mod.h"                     /* Module parameters */
+#include "regtime.h"                     /* act_time */
+#include "rerrno.h"
+#include "sip_msg.h"
+#include "config.h"
+#include "../../lib/ims/ims_getters.h"
+
+static struct hdr_field* act_contact;
+
+/*! \brief
+ *  Return an expire value in the range [ default_expires - range%, default_expires + range% ]
+ */
+static inline int get_expire_val(void) {
+	int expires = cfg_get(registrar, registrar_cfg, default_expires);
+	int range = cfg_get(registrar, registrar_cfg, default_expires_range);
+	/* if no range is given just return default_expires */
+	if (range == 0)
+		return expires;
+	/* select a random value in the range */
+	return expires - (float) range / 100 * expires
+			+ (float) (rand() % 100) / 100 * 2 * (float) range / 100 * expires;
+}
+
+/*! \brief
+ * Return value of Expires header field
+ * if the HF exists converted to absolute
+ * time, if the HF doesn't exist, returns
+ * default value;
+ */
+static inline int get_expires_hf(struct sip_msg* _m) {
+	exp_body_t* p;
+	if (_m->expires) {
+		p = (exp_body_t*) _m->expires->parsed;
+		if (p->valid) {
+			if (p->val != 0) {
+				return p->val + act_time;
+			} else
+				return 0;
+		} else {
+			return act_time + get_expire_val();
+		}
+	} else
+		return act_time + get_expire_val();
+}
+
+/*! \brief
+ * Get the first contact in message
+ */
+contact_t* get_first_contact(struct sip_msg* _m) {
+	if (_m->contact == 0)
+		return 0;
+
+	act_contact = _m->contact;
+	return (((contact_body_t*) _m->contact->parsed)->contacts);
+}
+
+/*! \brief
+ * Get next contact in message
+ */
+contact_t* get_next_contact(contact_t* _c) {
+	struct hdr_field* p;
+	if (_c->next == 0) {
+		p = act_contact->next;
+		while (p) {
+			if (p->type == HDR_CONTACT_T) {
+				act_contact = p;
+				return (((contact_body_t*) p->parsed)->contacts);
+			}
+			p = p->next;
+		}
+		return 0;
+	} else {
+		return _c->next;
+	}
+}
+
+/*! \brief
+ * Calculate absolute expires value per contact as follows:
+ * 1) If the contact has expires value, use the value. If it
+ *    is not zero, add actual time to it
+ * 2) If the contact has no expires parameter, use expires
+ *    header field in the same way
+ * 3) If the message contained no expires header field, use
+ *    the default value
+ */
+//void calc_contact_expires(struct sip_msg* _m, param_t* _ep, int* _e) {
+//	if (!_ep || !_ep->body.len) {
+//		*_e = get_expires_hf(_m);
+//	} else {
+//		if (str2int(&_ep->body, (unsigned int*) _e) < 0) {
+//			*_e = get_expire_val();
+//		}
+//		/* Convert to absolute value */
+//		if (*_e != 0)
+//			*_e += act_time;
+//	}
+//
+//	if ((*_e != 0)
+//			&& ((*_e - act_time)
+//					< cfg_get(registrar, registrar_cfg, min_expires))) {
+//		*_e = cfg_get(registrar, registrar_cfg, min_expires) + act_time;
+//	}
+//
+//	if ((*_e != 0) && cfg_get(registrar, registrar_cfg, max_expires)
+//			&& ((*_e - act_time)
+//					> cfg_get(registrar, registrar_cfg, max_expires))) {
+//		*_e = cfg_get(registrar, registrar_cfg, max_expires) + act_time;
+//	}
+//}
+
+/*! \brief
+ * Calculate contact q value as follows:
+ * 1) If q parameter exists, use it
+ * 2) If the parameter doesn't exist, use the default value
+ */
+int calc_contact_q(param_t* _q, qvalue_t* _r) {
+	if (!_q || (_q->body.len == 0)) {
+		*_r = cfg_get(registrar, registrar_cfg, default_q);
+	} else {
+		if (str2q(_r, _q->body.s, _q->body.len) < 0) {
+			rerrno = R_INV_Q; /* Invalid q parameter */
+			LM_ERR("invalid q parameter\n");
+			return -1;
+		}
+	}
+	return 0;
+}
+
+/*! \brief
+ * Check if the originating REGISTER message was formed correctly
+ * The whole message must be parsed before calling the function
+ * _s indicates whether the contact was star
+ */
+int check_contacts(struct sip_msg* _m, int* _s) {
+	struct hdr_field* p;
+	contact_t* c;
+
+	*_s = 0;
+	/* Message without contacts is OK */
+	if (_m->contact == 0)
+		return 0;
+
+	if (((contact_body_t*) _m->contact->parsed)->star == 1) {
+		/* The first Contact HF is star */
+		/* Expires must be zero */
+
+		if (cscf_get_expires(_m) != 0) {
+			rerrno = R_STAR_EXP;
+			return 1;
+		}
+
+		/* Message must contain no contacts */
+		if (((contact_body_t*) _m->contact->parsed)->contacts) {
+			rerrno = R_STAR_CONT;
+			return 1;
+		}
+
+		/* Message must contain no other Contact HFs */
+		p = _m->contact->next;
+		while (p) {
+			if (p->type == HDR_CONTACT_T) {
+				rerrno = R_STAR_CONT;
+				return 1;
+			}
+			p = p->next;
+		}
+
+		*_s = 1;
+	} else { /* The first Contact HF is not star */
+		/* Message must contain no star Contact HF */
+		p = _m->contact->next;
+		while (p) {
+			if (p->type == HDR_CONTACT_T) {
+				if (((contact_body_t*) p->parsed)->star == 1) {
+					rerrno = R_STAR_CONT;
+					return 1;
+				}
+				/* check also the lenght of all contacts */
+				for (c = ((contact_body_t*) p->parsed)->contacts; c;
+						c = c->next) {
+					if (c->uri.len > CONTACT_MAX_SIZE
+							|| (c->received
+									&& c->received->len > RECEIVED_MAX_SIZE)) {
+						rerrno = R_CONTACT_LEN;
+						return 1;
+					}
+				}
+			}
+			p = p->next;
+		}
+	}
+
+	return 0;
+}
+
+/*! \brief
+ * Parse the whole message and bodies of all header fields
+ * that will be needed by registrar
+ */
+int parse_message_for_register(struct sip_msg* _m) {
+	struct hdr_field* ptr;
+
+	if (parse_headers(_m, HDR_EOH_F, 0) == -1) {
+		rerrno = R_PARSE;
+		LM_ERR("failed to parse headers\n");
+		return -1;
+	}
+
+	if (!_m->to) {
+		rerrno = R_TO_MISS;
+		LM_ERR("To not found\n");
+		return -2;
+	}
+
+	if (!_m->callid) {
+		rerrno = R_CID_MISS;
+		LM_ERR("Call-ID not found\n");
+		return -3;
+	}
+
+	if (!_m->cseq) {
+		rerrno = R_CS_MISS;
+		LM_ERR("CSeq not found\n");
+		return -4;
+	}
+
+	if (_m->expires && !_m->expires->parsed
+			&& (parse_expires(_m->expires) < 0)) {
+		rerrno = R_PARSE_EXP;
+		LM_ERR("failed to parse expires body\n");
+		return -5;
+	}
+
+	if (_m->contact) {
+		ptr = _m->contact;
+		while (ptr) {
+			if (ptr->type == HDR_CONTACT_T) {
+				if (!ptr->parsed && (parse_contact(ptr) < 0)) {
+					rerrno = R_PARSE_CONT;
+					LM_ERR("failed to parse Contact body\n");
+					return -6;
+				}
+			}
+			ptr = ptr->next;
+		}
+	}
+
+	return 0;
+}

+ 77 - 0
modules/registrar_scscf/sip_msg.h

@@ -0,0 +1,77 @@
+/*
+ * $Id$
+ *
+ * SIP message related functions
+ *
+ * Copyright (C) 2001-2003 FhG Fokus
+ *
+ * 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
+ */
+/*!
+ * \file
+ * \brief SIP registrar module - SIP message related functions
+ * \ingroup registrar   
+ */  
+
+
+#ifndef SIP_MSG_H
+#define SIP_MSG_H
+
+#include "../../qvalue.h"
+#include "../../parser/msg_parser.h"
+#include "../../parser/contact/parse_contact.h"
+
+
+/*! \brief
+ * Parse the whole message and bodies of all header fields
+ * that will be needed by registrar
+ */
+int parse_message_for_register(struct sip_msg* _m);
+
+
+/*! \brief
+ * Check if the originating REGISTER message was formed correctly
+ * The whole message must be parsed before calling the function
+ * _s indicates whether the contact was star
+ */
+int check_contacts(struct sip_msg* _m, int* _s);
+
+
+/*! \brief
+ * Calculate absolute expires value per contact as follows:
+ * 1) If the contact has expires value, use the value. If it
+ *    is not zero, add actual time to it
+ * 2) If the contact has no expires parameter, use expires
+ *    header field in the same way
+ * 3) If the message contained no expires header field, use
+ *    the default value
+ */
+//void calc_contact_expires(struct sip_msg* _m, param_t* _ep, int* _e);
+
+
+/*! \brief
+ * Calculate contact q value as follows:
+ * 1) If q parameter exist, use it
+ * 2) If the parameter doesn't exist, use default value
+ */
+int calc_contact_q(param_t* _q, qvalue_t* _r);
+
+contact_t* get_first_contact(struct sip_msg* _m);
+contact_t* get_next_contact(contact_t* _c);
+
+
+#endif /* SIP_MSG_H */

+ 68 - 0
modules/registrar_scscf/stats.c

@@ -0,0 +1,68 @@
+/*
+ * $Id$
+ *
+ * The initial version of this code was written by Dragos Vingarzan
+ * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
+ * Fruanhofer Institute. It was and still is maintained in a separate
+ * branch of the original SER. We are therefore migrating it to
+ * Kamailio/SR and look forward to maintaining it from here on out.
+ * 2011/2012 Smile Communications, Pty. Ltd.
+ * ported/maintained/improved by 
+ * Jason Penton (jason(dot)penton(at)smilecoms.com and
+ * Richard Good (richard(dot)good(at)smilecoms.com) as part of an 
+ * effort to add full IMS support to Kamailio/SR using a new and
+ * improved architecture
+ * 
+ * NB: Alot of this code was originally part of OpenIMSCore,
+ * FhG Fokus. 
+ * Copyright (C) 2004-2006 FhG Fokus
+ * Thanks for great work! This is an effort to 
+ * break apart the various CSCF functions into logically separate
+ * components. We hope this will drive wider use. We also feel
+ * that in this way the architecture is more complete and thereby easier
+ * to manage in the Kamailio/SR environment
+ *
+ * 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 "stats.h"
+
+int register_stats() {
+	//SAR
+	if (register_stat(MOD_NAME, "sar_replies_response_time", &sar_replies_response_time,0 )
+			!= 0) {
+		LM_ERR("failed to register stat\n");
+		return -1;
+	}
+	if (register_stat(MOD_NAME, "sar_replies_received", &sar_replies_received, 0)
+			!= 0) {
+		LM_ERR("failed to register stat\n");
+		return -1;
+	}
+
+	return 1;
+}
+
+unsigned long get_avg_sar_response_time() {
+
+	long rpls_received = get_stat_val(sar_replies_received);
+	if (!rpls_received)
+		return 0;
+
+	return get_stat_val(sar_replies_response_time)/rpls_received;
+}

+ 56 - 0
modules/registrar_scscf/stats.h

@@ -0,0 +1,56 @@
+/*
+ * $Id$
+ *
+ * The initial version of this code was written by Dragos Vingarzan
+ * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
+ * Fruanhofer Institute. It was and still is maintained in a separate
+ * branch of the original SER. We are therefore migrating it to
+ * Kamailio/SR and look forward to maintaining it from here on out.
+ * 2011/2012 Smile Communications, Pty. Ltd.
+ * ported/maintained/improved by 
+ * Jason Penton (jason(dot)penton(at)smilecoms.com and
+ * Richard Good (richard(dot)good(at)smilecoms.com) as part of an 
+ * effort to add full IMS support to Kamailio/SR using a new and
+ * improved architecture
+ * 
+ * NB: Alot of this code was originally part of OpenIMSCore,
+ * FhG Fokus. 
+ * Copyright (C) 2004-2006 FhG Fokus
+ * Thanks for great work! This is an effort to 
+ * break apart the various CSCF functions into logically separate
+ * components. We hope this will drive wider use. We also feel
+ * that in this way the architecture is more complete and thereby easier
+ * to manage in the Kamailio/SR environment
+ *
+ * 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 STATISTICS_H_
+#define STATISTICS_H_
+
+#include "../../lib/kcore/statistics.h"
+
+stat_var* stat_sar_timeouts;
+stat_var* sar_replies_received;
+stat_var* sar_replies_response_time;
+
+int register_stats();
+unsigned long get_avg_sar_response_time();
+
+
+#endif /* STATISTICS_H_ */

+ 1059 - 0
modules/registrar_scscf/userdata_parser.c

@@ -0,0 +1,1059 @@
+/*
+ * $Id$
+ *
+ * The initial version of this code was written by Dragos Vingarzan
+ * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
+ * Fruanhofer Institute. It was and still is maintained in a separate
+ * branch of the original SER. We are therefore migrating it to
+ * Kamailio/SR and look forward to maintaining it from here on out.
+ * 2011/2012 Smile Communications, Pty. Ltd.
+ * ported/maintained/improved by 
+ * Jason Penton (jason(dot)penton(at)smilecoms.com and
+ * Richard Good (richard(dot)good(at)smilecoms.com) as part of an 
+ * effort to add full IMS support to Kamailio/SR using a new and
+ * improved architecture
+ * 
+ * NB: Alot of this code was originally part of OpenIMSCore,
+ * FhG Fokus. 
+ * Copyright (C) 2004-2006 FhG Fokus
+ * Thanks for great work! This is an effort to 
+ * break apart the various CSCF functions into logically separate
+ * components. We hope this will drive wider use. We also feel
+ * that in this way the architecture is more complete and thereby easier
+ * to manage in the Kamailio/SR environment
+ *
+ * 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 "../../parser/parse_uri.h"
+#include "userdata_parser.h"
+#include "../../parser/parse_hname2.h"
+
+int ctxtInit=0;							/**< the XML context		*/
+static xmlDtdPtr dtd;
+static xmlValidCtxtPtr dtdCtxt;
+static xmlSchemaValidCtxtPtr xsdCtxt=0;	/**< Schema Validating context */
+static xmlSchemaPtr xsd;
+
+/**
+ *	Duplicate a string into shm and trim leading&trailing spaces and surrounding quotes.
+ * @param dest - destination
+ * @param src - source
+ */
+void space_quotes_trim_dup(str *dest,char * src) {
+	int i = 0;
+	//right space trim
+	if (src == NULL) return ;
+	dest->len = strlen(src);
+	i = dest->len - 1;
+	while((src[i] == ' '||src[i]=='\t') && i > 0) {
+		dest->len--;
+		i--;
+	}
+	//left space trim
+	i = 0;
+	while((src[i] == ' '||src[i]=='\t') && i<dest->len)
+		i++;
+
+	while(i<dest->len &&(src[i]=='\"'&&src[dest->len-1]=='\"')){
+		i++;
+		if (i<dest->len) dest->len--;
+	}
+
+	dest->len -= i;
+	if (dest->len<=0) return;
+	dest->s = shm_malloc(dest->len);
+	memcpy(dest->s, src+i , dest->len);
+}
+
+/**
+ * Converts strings to integer values.
+ * - False -> 0
+ * - True  -> 1
+ * @param x - input value
+ * @returns int value
+ */
+static inline char ifc_tBool2char(xmlChar *x)
+{
+	int r=0;	
+	while(x[r]){
+		switch(x[r]){
+			case '0': return 0;
+			case '1': return 1;
+			case 't': case 'T': return 1;
+			case 'f': case 'F': return 0;
+		}
+		r++;
+	}
+	return 0;
+}
+
+/**
+ * Converts strings to integer values.
+ * - SESSION_CONTINUED   -> 0
+ * - SESSION_TERMINATED  -> 1
+ * @param x - input value
+ * @returns int value
+ */
+static inline char ifc_tDefaultHandling2char(xmlChar *x)
+{
+	char r;	
+	r = strtol((char*)x, (char **)NULL, 10);
+	if (errno==EINVAL){
+		while(x[0]){
+			if (x[0]=='c'||x[0]=='C') return 0;//SESSION_CONTINUED
+			if (x[0]=='r'||x[0]=='R') return 1;//SESSION_TERMINATED
+			x++;
+		}
+		return 0;
+	} 
+	else return (char)r; 
+}
+
+/**
+ * Converts strings to integer values.
+ * - ORIGINATING_SESSION      -> 0
+ * - TERMINATING_REGISTERED   -> 1
+ * - TERMINATING_UNREGISTERED -> 2
+ * @param x - input value
+ * @returns int value
+ */
+static inline char ifc_tDirectionOfRequest2char(xmlChar *x)
+{
+	int r;	
+	r = strtol((char*)x, (char **)NULL, 10);
+	if (errno==EINVAL){
+		while(x[0]){
+			if (x[0]=='o'||x[0]=='O') return 0;//ORIGINATING_SESSION
+			if (x[0]=='s'||x[0]=='S') return 1;//TERMINATING_REGISTERED
+			if (x[0]=='u'||x[0]=='U') return 2;//TERMINATING_UNREGISTERED
+			x++;
+		}
+		return 0;
+	} 
+	else return (char)r; 
+}
+
+/**
+ * Converts strings to integer values.
+ * - REGISTERED     -> 0
+ * - UNREGISTERED   -> 1
+ * - error (any)	 -> -1
+ * @param x - input value
+ * @returns int value
+ */
+static inline char ifc_tProfilePartIndicator2char(xmlChar *x)
+{
+	int r;	
+	if (x==0||x[0]==0) return -1;
+	r = strtol((char*)x, (char **)NULL, 10);
+	if (errno==EINVAL){
+		while(x[0]){
+			if (x[0]=='r'||x[0]=='R') return 0;//REGISTERED
+			if (x[0]=='u'||x[0]=='U') return 1;//UNREGISTERED
+			x++;
+		}
+		return 0;
+	} 
+	else return (char)r; 
+}
+
+/**
+ * Duplicate a string into shm and trim leading&trailing spaces.
+ * @param dest - destination
+ * @param src - source
+ */
+static inline void space_trim_dup(str *dest, char *src)
+{
+    	int i;
+	dest->s=0;
+	dest->len=0;
+	if (!src) return;
+	dest->len = strlen(src);
+	i = dest->len-1;
+	while((src[i]==' '||src[i]=='\t') && i>0) 
+		i--;
+	i=0;
+	while((src[i]==' '||src[i]=='\t') && i<dest->len)
+		i++;
+	dest->len -= i;
+        
+	dest->s = shm_malloc(dest->len);
+	if (!dest->s) {
+		LM_ERR("Out of memory allocating %d bytes\n",dest->len);
+		dest->len=0;
+		return;
+	}
+	memcpy(dest->s,src+i,dest->len);
+}
+
+/**
+ *	Parse a Application Server.
+ * @param doc - the XML document
+ * @param node - the current node
+ * @param as - structure to fill
+ * @returns 1 on success, 0 on failure
+ */
+static int parse_application_server(xmlDocPtr doc,xmlNodePtr node,ims_application_server *as)
+{
+	xmlNodePtr child;
+	xmlChar *x;
+	as->server_name.s=NULL;as->server_name.len=0;
+	as->default_handling=IFC_NO_DEFAULT_HANDLING;
+	as->service_info.s=NULL;as->service_info.len=0;
+
+	for(child=node->children ; child ; child=child->next)
+		if (child->type==XML_ELEMENT_NODE)
+			switch (child->name[0]) {
+				case 'S':case 's':	{//ServerName / ServiceInfo
+					switch (child->name[4]) {
+						case 'E':case 'e':  //ServerName
+							x = xmlNodeListGetString(doc, child->xmlChildrenNode, 1);
+                                                        space_trim_dup(&(as->server_name),(char*)x);
+							xmlFree((char*)x);
+							break;
+						case 'I':case 'i':  //ServiceInfo
+							x = xmlNodeListGetString(doc, child->xmlChildrenNode, 1);
+                                                        space_trim_dup(&(as->service_info),(char*)x);
+							xmlFree(x);
+							break;
+					}
+					break;
+				}
+				case 'D':case 'd': //DefaultHandling
+					x = xmlNodeListGetString(doc, child->xmlChildrenNode, 1);
+					as->default_handling=ifc_tDefaultHandling2char(x);
+					xmlFree(x);
+					break;
+			}
+	return 1;
+}
+
+
+/**
+ *	Parse SPT for SIP Header.
+ * @param doc - the XML document
+ * @param node - the current node
+ * @param sh - structure to fill
+ * @returns 1 on success, 0 on failure
+ */
+static int parse_sip_header(xmlDocPtr doc,xmlNodePtr node,ims_sip_header *sh)
+{
+	xmlNodePtr child;
+	xmlChar *x;
+	char c[256];
+	int len;
+	struct hdr_field hf;
+	sh->header.s=NULL;sh->header.len=0;
+	sh->content.s=NULL;sh->content.len=0;
+
+	for(child=node->children ; child ; child=child->next)
+		if (child->type==XML_ELEMENT_NODE)
+			switch (child->name[0]) {
+				case 'H':case 'h':	//Header
+					x = xmlNodeListGetString(doc, child->xmlChildrenNode, 1);
+					len = strlen((char*)x);		
+					memcpy(c,x,len);
+					c[len++]=':';
+					c[len]=0;
+					space_trim_dup(&(sh->header),(char*)x);
+					parse_hname2(c,c+(len<4?4:len),&hf);
+					sh->type=(short)hf.type;
+					//LOG(L_CRIT,"[%.*s(%d)]\n",sh->header.len,sh->header.s,sh->type);
+					xmlFree(x);
+					break;
+				case 'C':case 'c':	//Content
+					x = xmlNodeListGetString(doc, child->xmlChildrenNode, 1);
+					space_quotes_trim_dup(&(sh->content),(char*)x);
+					xmlFree(x);
+					break;
+			}
+	return 1;
+}
+
+/**
+ *	Parse SPT for Session Description.
+ * @param doc - the XML document
+ * @param node - the current node
+ * @param sd - structure to fill
+ * @returns 1 on success, 0 on failure
+ */
+static int parse_session_desc(xmlDocPtr doc,xmlNodePtr node,ims_session_desc *sd)
+{
+	xmlNodePtr child;
+	xmlChar *x;
+	sd->line.s=NULL;sd->line.len=0;
+	sd->content.s=NULL;sd->content.len=0;
+
+	for(child=node->children ; child ; child=child->next)
+		if (child->type==XML_ELEMENT_NODE)
+			switch (child->name[0]) {
+				case 'L':case 'l':	//Line
+					x = xmlNodeListGetString(doc, child->xmlChildrenNode, 1);
+					space_trim_dup(&(sd->line),(char*)x);
+					xmlFree(x);
+					break;
+				case 'C':case 'c':	//Content
+					x = xmlNodeListGetString(doc, child->xmlChildrenNode, 1);
+					space_quotes_trim_dup(&(sd->content),(char*)x);
+					xmlFree(x);
+					break;
+			}
+	return 1;
+}
+
+/**
+ *	Parse a Service Point Trigger Extension (RegistrationType).
+ * @param doc - the XML document
+ * @param node - the current node
+ * @param spt - structure to fill
+ * @returns 1 on success, 0 on failure
+ */
+static int parse_spt_extension(xmlDocPtr doc,xmlNodePtr node,ims_spt *spt)
+{
+	xmlNodePtr child;
+	xmlChar *x;
+
+	for(child=node->children ; child ; child=child->next) {
+		if (child->type==XML_ELEMENT_NODE && (child->name[0]=='R' || child->name[0]=='r')) {
+			x = xmlNodeListGetString(doc, child->xmlChildrenNode, 1);
+			switch(atoi((char*)x)) {
+				case 0:
+					spt->registration_type |= IFC_INITIAL_REGISTRATION;
+					break;
+				case 1:
+					spt->registration_type |= IFC_RE_REGISTRATION;
+					break;
+				case 2:
+					spt->registration_type |= IFC_DE_REGISTRATION;
+					break;
+			}								
+			xmlFree(x);
+		}
+	}
+//	LOG(L_CRIT,"INFO:"M_NAME":parse_spt_extension: spt->registration_type=%d\n",spt->registration_type);
+	return 1;			
+}
+
+/**
+ *	Parse a Service Point Trigger.
+ * @param doc - the XML document
+ * @param node - the current node
+ * @param spt_to - structure to fill
+ * @param spt_cnt - structure to fill with the spt count
+ * @returns 1 on success, 0 on failure
+ */
+static int parse_spt(xmlDocPtr doc,xmlNodePtr node,ims_spt *spt_to,unsigned short *spt_cnt)
+{
+	xmlNodePtr child,saved=0;
+	xmlChar *x;
+
+	ims_spt *spt,*spt2;
+	int group;
+	
+	spt = spt_to + *spt_cnt;
+	
+	spt->condition_negated=0;
+	spt->group=0;
+	spt->type=IFC_UNKNOWN;
+	spt->registration_type=0;
+
+	for(child=node->children ; child ; child=child->next)
+		if (child->type==XML_ELEMENT_NODE)
+			switch (child->name[0]) {
+				case 'C':case 'c': //ConditionNegated
+					x = xmlNodeListGetString(doc, child->xmlChildrenNode, 1);
+					spt->condition_negated=ifc_tBool2char(x);
+					xmlFree(x);
+					break;
+				case 'G':case 'g': //Group
+					x = xmlNodeListGetString(doc, child->xmlChildrenNode, 1);
+					spt->group=atoi((char*)x);
+					xmlFree(x);
+					break;
+				case 'R':case 'r': //RequestUri
+					spt->type=IFC_REQUEST_URI;
+					x = xmlNodeListGetString(doc, child->xmlChildrenNode, 1);
+					space_trim_dup(&(spt->request_uri),(char*)x);
+					xmlFree(x);
+					break;
+				case 'E':case 'e': //Extension
+				    parse_spt_extension(doc,child,spt);
+					break;
+				case 'M':case 'm': //method
+					spt->type=IFC_METHOD;
+					x = xmlNodeListGetString(doc, child->xmlChildrenNode, 1);
+					space_trim_dup(&(spt->method),(char*)x);
+					xmlFree(x);
+					break;
+				case 'S':case 's': {//SIPHeader/SessionCase/SessionDescription
+					switch(child->name[7]) {
+						case 'E':case 'e'://SIP_HEADER
+							spt->type=IFC_SIP_HEADER;
+							parse_sip_header(doc,child,&(spt->sip_header));
+							saved = child;
+							break;
+						case 'C':case 'c'://Session Case
+							spt->type=IFC_SESSION_CASE;
+							x = xmlNodeListGetString(doc, child->xmlChildrenNode, 1);
+							spt->session_case=ifc_tDirectionOfRequest2char(x);
+							xmlFree(x);
+							break;
+						case 'D':case 'd'://Session Description
+							spt->type=IFC_SESSION_DESC;
+							parse_session_desc(doc,child,&(spt->session_desc));
+							saved = child;
+							break;
+					}
+
+				}
+					break;
+			}
+	*spt_cnt=*spt_cnt+1;
+
+	/* adding the other nodes for multiple groups */			
+	for(child=node->children ; child ; child=child->next)
+		if (child->type==XML_ELEMENT_NODE)
+			switch (child->name[0]) {
+				case 'G':case 'g': //Group
+					x = xmlNodeListGetString(doc, child->xmlChildrenNode, 1);
+					group=atoi((char*)x);
+					xmlFree(x);
+					if (group != spt->group){
+						spt2 = spt_to + *spt_cnt;
+						spt2->condition_negated = spt->condition_negated;
+						spt2->group = group;
+						spt2->type = spt->type;
+						switch(spt2->type){
+							case IFC_REQUEST_URI:
+								spt2->request_uri.len = spt->request_uri.len;
+								spt2->request_uri.s = shm_malloc(spt2->request_uri.len);
+								if (!spt2->request_uri.s){
+									LM_ERR("Out of memory allocating %d bytes\n",spt->request_uri.len);
+									break;
+								}
+								memcpy(spt2->request_uri.s,spt->request_uri.s,spt->request_uri.len);
+								break;
+							case IFC_METHOD:
+								spt2->method.len = spt->method.len;
+								spt2->method.s = shm_malloc(spt2->method.len);
+								if (!spt2->method.s){
+									LM_ERR("Out of memory allocating %d bytes\n",spt->method.len);
+									break;
+								}
+								memcpy(spt2->method.s,spt->method.s,spt->method.len);
+								break;
+							case IFC_SIP_HEADER:
+								parse_sip_header(doc,saved,&(spt2->sip_header));
+								break;
+							case IFC_SESSION_CASE:
+								spt2->session_case = spt->session_case;
+								break;
+							case IFC_SESSION_DESC:
+								parse_session_desc(doc,saved,&(spt2->session_desc));
+								break;								
+						}
+						spt2->registration_type = spt->registration_type;
+						*spt_cnt = *spt_cnt+1;						
+					}
+					break;
+			}
+	return 1;			
+}
+
+
+/**
+ *	Parse a Trigger Point.
+ * @param doc - the XML document
+ * @param node - the current node
+ * @param tp - structure to fill
+ * @returns 1 on success, 0 on failure
+ * \todo An effective sort for the priority
+ */
+static int parse_trigger_point(xmlDocPtr doc,xmlNodePtr node,ims_trigger_point *tp)
+{
+	xmlNodePtr child,child2;
+	xmlChar *x;
+	unsigned short spt_cnt=0;
+	int i,j;
+	ims_spt spttemp;
+	tp->condition_type_cnf=IFC_DNF;//0
+	tp->spt=NULL;
+	tp->spt_cnt=0;
+
+	for(child=node->children ; child ; child=child->next)
+		if (child->type==XML_ELEMENT_NODE)
+			switch (child->name[0]) {
+				case 'C':case 'c': //ConditionTypeCNF
+					x = xmlNodeListGetString(doc, child->xmlChildrenNode, 1);
+					tp->condition_type_cnf=ifc_tBool2char(x);
+					xmlFree(x);
+					break;
+				case 'S':case 's': //SPT - Service Point Trigger
+					// COUNT all in another groups
+					for(child2=child->children ; child2 ; child2=child2->next)
+						if (child2->type==XML_ELEMENT_NODE)
+							switch (child2->name[0]) {
+								case 'G':case 'g':
+									spt_cnt++;
+							}
+					break;
+			}
+	tp->spt = (ims_spt*) shm_malloc(sizeof(ims_spt)*spt_cnt);
+	if (!tp->spt){
+		LM_ERR("Out of memory allocating %lx bytes\n",sizeof(ims_spt)*spt_cnt);
+		return 0;
+	}
+	for(child=node->children ; child ; child=child->next)
+		if (child->type==XML_ELEMENT_NODE)
+			switch (child->name[0]) {
+				case 'S':case 's': //SPT - Service Point Trigger
+					parse_spt(doc,child,tp->spt,&(tp->spt_cnt));
+					/*i=0;
+					while(i<tp->spt_cnt&&tp->spt[i].group<spttemp.group)
+						i++;
+					for(j=tp->spt_cnt-1;j>=i;j--)
+						tp->spt[j+1]=tp->spt[j];
+					tp->spt[i]=spttemp;
+					tp->spt_cnt++;*/
+			
+					break;
+			}
+	
+	j=1;
+	while(j){
+		j=0;
+		for(i=0;i<tp->spt_cnt-1;i++)
+			if (tp->spt[i].group > tp->spt[i+1].group){
+				j=1;
+				spttemp = tp->spt[i];
+				tp->spt[i]=tp->spt[i+1];
+				tp->spt[i+1]=spttemp;
+			}			
+	}
+	return 1;
+}
+
+/**
+ *	Parse a Filter Criteria.
+ * @param doc - the XML document
+ * @param node - the current node
+ * @param fc - structure to fill
+ * @returns 1 on success, 0 on failure
+ */
+static int parse_filter_criteria(xmlDocPtr doc,xmlNodePtr node,ims_filter_criteria *fc)
+{
+	xmlNodePtr child;
+	xmlChar *x;
+	char k;
+	fc->priority=0;
+	fc->trigger_point=NULL;
+	fc->profile_part_indicator=NULL;
+	//fc->apllication_server init mai tarziu
+	for(child=node->children ; child ; child=child->next)
+		if (child->type==XML_ELEMENT_NODE)
+			switch (child->name[3]) {
+				case 'O':case 'o':	//Priority
+					x = xmlNodeListGetString(doc, child->xmlChildrenNode, 1);
+					fc->priority=atoi((char*)x);
+					xmlFree(x);
+					break;
+				case 'G':case 'g':	//TriggerPoint
+					fc->trigger_point=(ims_trigger_point*) shm_malloc(sizeof(ims_trigger_point));
+					if (!fc->trigger_point){
+						LM_ERR("Out of memory allocating %lx bytes\n",sizeof(ims_trigger_point));
+						break;
+					}
+					if (!parse_trigger_point(doc,child,fc->trigger_point)){
+						shm_free(fc->trigger_point);
+						fc->trigger_point=0;
+						return 0;
+					}
+					break;
+				case 'L':case 'l':	//ApplicationServer
+					parse_application_server(doc,child,&(fc->application_server));
+					break;
+				case 'F':case 'f':	//ProfilePartIndicator
+					x = xmlNodeListGetString(doc, child->xmlChildrenNode, 1);
+					k = ifc_tProfilePartIndicator2char(x);
+					if (k<0) break;
+					fc->profile_part_indicator=(char*)shm_malloc(sizeof(char));
+					if (!fc->profile_part_indicator){
+						LM_ERR("Out of memory allocating %lx bytes\n",sizeof(ims_trigger_point));
+						break;
+					}
+					*fc->profile_part_indicator=k;
+					xmlFree(x);
+					break;
+			}
+	return 1;
+}
+
+
+
+/**
+ * Parse the Public Identity.
+ * @param doc - the XML document
+ * @param root - the current node
+ * @param pi - structure to fill
+ * @returns 1 on success, 0 on failure , 2 if its a wildcardpsi
+ */
+static int parse_public_identity(xmlDocPtr doc, xmlNodePtr root, ims_public_identity *pi)
+{
+	xmlNodePtr child;
+	xmlNodePtr grandson;
+	xmlChar *x;
+	int return_code=1;
+	
+	for(child=root->children;child;child=child->next)
+		if (child->type==XML_ELEMENT_NODE)
+			switch (child->name[0]){
+				case 'I': case 'i':
+					if (!pi->public_identity.len){
+						x = xmlNodeListGetString(doc,child->xmlChildrenNode,1);
+						space_trim_dup(&(pi->public_identity),(char*)x);
+						xmlFree(x);
+					}					
+					break;
+				case 'B': case 'b':
+					x = xmlNodeListGetString(doc,child->xmlChildrenNode,1);
+					pi->barring = ifc_tBool2char(x);
+					xmlFree(x);
+					break;
+				//lets add something 
+				case 'E' : case 'e':
+					// that would be Extension
+					// here i need to parse Identity Type 
+					// if its two then  wildcardedpsi
+					// and then extension!!!
+					// I have to check how you parse this shit
+
+					for(grandson=child->children;grandson;grandson=grandson->next)
+					{
+												
+						if (grandson->type==XML_ELEMENT_NODE)
+						{
+							switch (grandson->name[0]) {
+								case 'I' : case 'i':
+									//identity type 0 public identity 1 distinct psi 2 wildcard psi
+									//x = xmlNodeListGetString(doc,grandson->xmlChildrenNode,1);
+									// i need to compare x with 2, but i have to trim leading 
+									// space characters or tabs			
+									//xmlFree(x);
+									break;
+								case 'W' : case 'w':
+									//wildcardpsi
+									if(!scscf_support_wildcardPSI) {
+										LOG(L_ERR,"Configured without support for Wildcard PSI and got one from HSS\n");
+										LOG(L_ERR,"the identity will be stored but never be matched, please include the parameter to support wildcard PSI in the config file\n");
+									}
+									
+									x = xmlNodeListGetString(doc,grandson->xmlChildrenNode,1);
+									space_trim_dup(&(pi->wildcarded_psi),(char*)x);
+									
+									xmlFree(x);
+									return_code=2;
+									break;
+								default :
+									break;
+							}
+						}
+					}
+										
+					break;				
+			}
+
+	return return_code;
+}
+
+/**
+ *	Parse a Core Network Service Authorization.
+ * @param doc - the XML document
+ * @param node - the current node
+ * @param cn - structure to fill
+ * @returns 1 on success, 0 on failure
+ */
+static int parse_cn_service_auth(xmlDocPtr doc,xmlNodePtr node,ims_cn_service_auth *cn)
+{
+	xmlNodePtr child;
+	xmlChar *x;
+	cn->subscribed_media_profile_id=-1;
+	for(child=node->children ; child ; child=child->next)
+		if (child->type==XML_ELEMENT_NODE)
+			switch (child->name[0]) {
+				case 'S':case 's':	//BarringIndication
+					x = xmlNodeListGetString(doc, child->xmlChildrenNode, 1);
+					cn->subscribed_media_profile_id=atoi((char*)x);
+					xmlFree(x);
+					return 1;
+					break;
+
+			}
+	return 0;
+}
+
+/**
+ *	Parse a Core Network Service Profile.
+ * @param doc - the XML document
+ * @param root - the current node
+ * @param sp - structure to fill
+ * @returns 1 on success, 0 on failure
+ */
+static int parse_service_profile(xmlDocPtr doc, xmlNodePtr root, ims_service_profile *sp) {
+    xmlNodePtr child;
+    xmlChar *x;
+    unsigned short pi_cnt = 0, ifc_cnt = 0, sh_cnt = 0;
+    int i, j;
+    ims_filter_criteria fctemp;
+    int returncode = 0;
+
+    for (child = root->children; child; child = child->next)
+        if (child->type == XML_ELEMENT_NODE) {
+            LM_DBG("child name is [%s]\n", child->name);
+            switch (child->name[0]) {
+                case 'P': case 'p':
+                    pi_cnt++;
+                    break;
+                case 'i':case 'I': //InitialFilterCriteria
+                    ifc_cnt++;
+                    break;
+                case 'c':case 'C': //CoreNetworkServiceAuthorization
+                    sp->cn_service_auth = (ims_cn_service_auth*) shm_malloc(
+                            sizeof (ims_cn_service_auth));
+                    break;
+                case 's':case 'S': //SharedIFCSet
+                    sh_cnt++;
+                    break;
+
+            }
+
+        }
+
+    sp->public_identities = shm_malloc(pi_cnt * sizeof (ims_public_identity));
+    if (!sp->public_identities) {
+        LM_ERR("Out of memory allocating %lx bytes\n", pi_cnt * sizeof (ims_public_identity));
+        return 0;
+    }
+    memset(sp->public_identities, 0, pi_cnt * sizeof (ims_public_identity));
+    
+    sp->filter_criteria = (ims_filter_criteria*) shm_malloc(sizeof (ims_filter_criteria) * ifc_cnt);
+    if (!sp->filter_criteria) {
+        LM_ERR("Out of memory allocating %lx bytes\n", ifc_cnt * sizeof (ims_filter_criteria));
+        return 0;
+    }
+    memset(sp->filter_criteria, 0, ifc_cnt * sizeof (ims_filter_criteria));
+    
+    sp->shared_ifc_set = (int*) shm_malloc(sizeof (int) *sh_cnt);
+    if (!sp->shared_ifc_set) {
+        LM_ERR("Out of memory allocating %lx bytes\n", sh_cnt * sizeof (int));
+        return 0;
+    }
+    memset(sp->shared_ifc_set, 0, sh_cnt * sizeof (int));
+    
+    for (child = root->children; child; child = child->next)
+        if (child->type == XML_ELEMENT_NODE)
+            switch (child->name[0]) {
+                case 'P': case 'p':
+                    returncode = parse_public_identity(doc, child, &(sp->public_identities[sp->public_identities_cnt]));
+                    if (returncode)
+                        sp->public_identities_cnt++;
+                    break;
+                case 'I':case 'i': //InitialFilterCriteria
+                    if (!parse_filter_criteria(doc, child, &(fctemp)))
+                        break;
+                    i = 0;
+                    while (i < sp->filter_criteria_cnt && sp->filter_criteria[i].priority < fctemp.priority)
+                        i++;
+                    for (j = sp->filter_criteria_cnt - 1; j >= i; j--)
+                        sp->filter_criteria[j + 1] = sp->filter_criteria[j];
+                    sp->filter_criteria[i] = fctemp;
+                    sp->filter_criteria_cnt++;
+                    break;
+                case 'C':case 'c': //CoreNetworkServiceAuthorization
+                    if (!parse_cn_service_auth(doc, child, sp->cn_service_auth)) {
+                        shm_free(sp->cn_service_auth);
+                        sp->cn_service_auth = 0;
+                    }
+                    break;
+                case 'S':case 's': //SharedIFCSet
+                    x = xmlNodeListGetString(doc, child->xmlChildrenNode, 1);
+                    sp->shared_ifc_set[sp->shared_ifc_set_cnt++] = atoi((char*) x);
+                    xmlFree(x);
+                    break;
+            }
+    if (returncode == 2)
+        return 2; // i need to know if there is a wildcardpsi hiding in the public_identity
+    return 1;
+}
+
+/**
+ *	Parse a IMS Subscription.
+ * @param doc - the XML document
+ * @param root - the current node
+ * @returns the ims_subscription* on success or NULL on error
+ */
+static ims_subscription* parse_ims_subscription(xmlDocPtr doc, xmlNodePtr root)
+{
+	xmlNodePtr child;
+	xmlChar *x;
+	ims_subscription *s;
+	unsigned short sp_cnt=0;
+	int rc;
+	
+	if (!root) return 0;
+	while(root->type!=XML_ELEMENT_NODE || strcasecmp((char*)root->name,"IMSSubscription")!=0){
+		root = root->next;
+	}
+	if (!root) {
+		LM_ERR("No IMSSubscription node found\n");
+		return 0;
+	}
+	s = (ims_subscription*) shm_malloc(sizeof(ims_subscription));
+	if (!s) {
+		LM_ERR("Out of memory allocating %lx bytes\n",sizeof(ims_subscription));
+		return 0;
+	}
+	memset(s,0,sizeof(ims_subscription));
+	for(child=root->children;child;child=child->next)
+		if (child->type==XML_ELEMENT_NODE)
+			switch (child->name[0]){
+				case 'P':case 'p':  /* Private Identity */
+					if (!s->private_identity.len){
+						x = xmlNodeListGetString(doc,child->xmlChildrenNode,1);
+						space_trim_dup(&(s->private_identity),(char*)x);
+						xmlFree(x);
+					}
+					break;
+				case 'S':case 's':	/* Service Profile */
+					sp_cnt++;
+					break;					
+			}
+	s->service_profiles = (ims_service_profile*) shm_malloc(sp_cnt * sizeof(ims_service_profile));
+	if (!s->service_profiles) {
+		LM_ERR("Out of memory allocating %lx bytes\n",sp_cnt*sizeof(ims_service_profile));
+		return s;	
+	}
+	memset(s->service_profiles,0,sp_cnt * sizeof(ims_service_profile));
+	for(child=root->children;child;child=child->next)
+		if (child->type==XML_ELEMENT_NODE)
+			if (child->name[0]=='S' || child->name[0]=='s')
+			{
+				rc=parse_service_profile(doc,child,&(s->service_profiles[s->service_profiles_cnt]));
+				if (rc==2)
+					s->wpsi=1;
+				if (rc)
+					s->service_profiles_cnt++;
+			}				
+	s->lock = lock_alloc();
+	s->lock = lock_init(s->lock);
+	return s;
+}
+
+
+/**
+ * Parses the user data XML and copies data into a new ims_subscription structure.
+ * @param xml - the input xml (NB must be null terminated)
+ * @returns the ims_subscription* on success or NULL on error
+ */
+ims_subscription *parse_user_data(str xml)
+{
+	xmlDocPtr doc=0;
+	xmlNodePtr root=0;
+	ims_subscription *s = 0;
+	if (!ctxtInit) parser_init(scscf_user_data_dtd,scscf_user_data_xsd);	
+	doc=0;
+	
+	doc = xmlParseDoc((unsigned char *)xml.s);
+	if (!doc){
+		LM_ERR("This is not a valid XML <%.*s>\n", xml.len,xml.s);
+		goto error;
+	}
+
+	if (dtdCtxt){
+		if (xmlValidateDtd(dtdCtxt,doc,dtd)!=1){
+			LM_ERR("Verification of XML against DTD failed <%.*s>\n", xml.len,xml.s);
+			goto error;
+		}
+	}
+	if (xsdCtxt){
+		if (xmlSchemaValidateDoc(xsdCtxt,doc)!=0){
+			LM_ERR("Verification of XML against XSD failed <%.*s>\n", xml.len,xml.s);
+			goto error;
+		}
+	}
+
+	root = xmlDocGetRootElement(doc);
+	if (!root){
+		LM_ERR("Empty XML <%.*s>\n",
+			xml.len,xml.s);
+		goto error;
+	}
+	s = parse_ims_subscription(doc,root);
+	if (!s){
+		LM_ERR("Error while loading into  ims subscription structure\n");
+		goto error;		
+	}
+	xmlFreeDoc(doc);
+	print_user_data(s);
+	return s;
+error:	
+	if (doc) xmlFreeDoc(doc);
+	return 0;	
+}
+
+/**
+ * Initializes the libxml2 parser.
+ * @param dtd_filename - path to the DTD or NULL if none
+ * @param xsd_filename - path to the XSD or NULL if none
+ * @returns 1 on success or 0 on error
+ */
+int parser_init(char *dtd_filename, char *xsd_filename)
+{
+	if (dtd_filename){
+		dtd = xmlParseDTD(NULL,(unsigned char*)dtd_filename);
+		if (!dtd){
+			LM_ERR("unsuccesful DTD parsing from file <%s>\n",
+				dtd_filename);
+			return 0;
+		}
+		dtdCtxt = xmlNewValidCtxt();
+		dtdCtxt->userData = (void*)stderr;
+		dtdCtxt->error = (xmlValidityErrorFunc) fprintf;
+		dtdCtxt->warning = (xmlValidityWarningFunc) fprintf;
+	}
+	if (xsd_filename){
+		xmlSchemaParserCtxtPtr ctxt;
+		ctxt = xmlSchemaNewParserCtxt(xsd_filename);
+		if (!ctxt) {
+			LM_ERR("unsuccesful XSD parsing from file <%s>\n",
+				xsd_filename);
+			return 0;
+		}
+		xmlSchemaSetParserErrors(ctxt,(xmlValidityErrorFunc) fprintf,(xmlValidityWarningFunc) fprintf,stderr);
+		xsd = xmlSchemaParse(ctxt);
+		xmlSchemaFreeParserCtxt(ctxt);		
+		
+		xsdCtxt = xmlSchemaNewValidCtxt(xsd);
+		xmlSchemaSetValidErrors(xsdCtxt,(xmlValidityErrorFunc) fprintf,(xmlValidityWarningFunc) fprintf,stderr);
+	}
+	ctxtInit=1;
+	return 1;
+}
+
+
+/**
+ * Print the contents of an ims_subscription structure.
+ * @param log_level - level to log on
+ * @param s - the ims_subscription to be printed
+ */
+void print_user_data(ims_subscription *s) {
+    int i, j, k;
+
+    LM_DBG("IMSSubscription:\n");
+    if (!s) return;
+    
+    LM_DBG("Private Identity: <%.*s>\n", s->private_identity.len, s->private_identity.s);
+    for (i = 0; i < s->service_profiles_cnt; i++) {
+        LM_DBG("\tService Profile:\n");
+        for (j = 0; j < s->service_profiles[i].public_identities_cnt; j++) {
+            LM_DBG("\t\tPublic Identity: Barring [%d] <%.*s> \n",
+                    s->service_profiles[i].public_identities[j].barring,
+                    s->service_profiles[i].public_identities[j].public_identity.len,
+                    s->service_profiles[i].public_identities[j].public_identity.s);
+        }
+        for (j = 0; j < s->service_profiles[i].filter_criteria_cnt; j++) {
+            LM_DBG("\t\tFilter Criteria: Priority [%d]ProfilePartInd [%d]\n",
+                    s->service_profiles[i].filter_criteria[j].priority,
+                    s->service_profiles[i].filter_criteria[j].profile_part_indicator ?
+                    *(s->service_profiles[i].filter_criteria[j].profile_part_indicator) : -1);
+            if (s->service_profiles[i].filter_criteria[j].trigger_point) {
+                LM_DBG("\t\t\tTrigger Point: CNF [%c] %s\n",
+                        s->service_profiles[i].filter_criteria[j].trigger_point->condition_type_cnf ? 'X' : ' ',
+                        s->service_profiles[i].filter_criteria[j].trigger_point->condition_type_cnf ? "(_|_)&(_|_)" : "(_&_)|(_&_)"
+                        );
+                for (k = 0; k < s->service_profiles[i].filter_criteria[j].trigger_point->spt_cnt; k++) {
+                    LM_DBG("\t\t\t\tSPT: Grp[%d] NOT[%c] RegType[%d]\n",
+                            s->service_profiles[i].filter_criteria[j].trigger_point->spt[k].group,
+                            s->service_profiles[i].filter_criteria[j].trigger_point->spt[k].condition_negated ? 'X' : ' ',
+                            s->service_profiles[i].filter_criteria[j].trigger_point->spt[k].registration_type
+                            );
+                    switch (s->service_profiles[i].filter_criteria[j].trigger_point->spt[k].type) {
+                        case 1:
+                            LM_DBG("\t\t\t\t\t Request-URI == <%.*s>\n",
+                                    s->service_profiles[i].filter_criteria[j].trigger_point->spt[k].request_uri.len,
+                                    s->service_profiles[i].filter_criteria[j].trigger_point->spt[k].request_uri.s);
+                            break;
+                        case 2:
+                            LM_DBG("\t\t\t\t\t Method == <%.*s>\n",
+                                    s->service_profiles[i].filter_criteria[j].trigger_point->spt[k].method.len,
+                                    s->service_profiles[i].filter_criteria[j].trigger_point->spt[k].method.s);
+                            break;
+                        case 3:
+                            LM_DBG("\t\t\t\t\t Hdr(%.*s(%d)) == <%.*s>\n",
+                                    s->service_profiles[i].filter_criteria[j].trigger_point->spt[k].sip_header.header.len,
+                                    s->service_profiles[i].filter_criteria[j].trigger_point->spt[k].sip_header.header.s,
+                                    s->service_profiles[i].filter_criteria[j].trigger_point->spt[k].sip_header.type,
+                                    s->service_profiles[i].filter_criteria[j].trigger_point->spt[k].sip_header.content.len,
+                                    s->service_profiles[i].filter_criteria[j].trigger_point->spt[k].sip_header.content.s);
+                            break;
+                        case 4:
+                            LM_DBG("\t\t\t\t\t SessionCase [%d]\n",
+                                    s->service_profiles[i].filter_criteria[j].trigger_point->spt[k].session_case);
+                            break;
+                        case 5:
+                            LM_DBG("\t\t\t\t\t SDP(%.*s) == <%.*s>\n",
+                                    s->service_profiles[i].filter_criteria[j].trigger_point->spt[k].session_desc.line.len,
+                                    s->service_profiles[i].filter_criteria[j].trigger_point->spt[k].session_desc.line.s,
+                                    s->service_profiles[i].filter_criteria[j].trigger_point->spt[k].session_desc.content.len,
+                                    s->service_profiles[i].filter_criteria[j].trigger_point->spt[k].session_desc.content.s);
+                            break;
+                    }
+                }
+            }
+            LM_DBG("\t\t\tAS: <%.*s> Handling [%d] SrvInfo: <%.*s>\n",
+                    s->service_profiles[i].filter_criteria[j].application_server.server_name.len,
+                    s->service_profiles[i].filter_criteria[j].application_server.server_name.s,
+                    s->service_profiles[i].filter_criteria[j].application_server.default_handling,
+                    s->service_profiles[i].filter_criteria[j].application_server.service_info.len,
+                    s->service_profiles[i].filter_criteria[j].application_server.service_info.s);
+        }
+        if (s->service_profiles[i].cn_service_auth) {
+            LM_DBG("\t\tCN Serv Auth: Subs Media Profile ID [%d]\n",
+                    s->service_profiles[i].cn_service_auth->subscribed_media_profile_id);
+        }
+        for (j = 0; j < s->service_profiles[i].shared_ifc_set_cnt; j++) {
+            LM_DBG("\t\tShared IFC Set: [%d]\n",
+                    s->service_profiles[i].shared_ifc_set[j]);
+        }
+    }
+}
+
+str cscf_get_realm_from_ruri(struct sip_msg *msg) {
+	str realm = { 0, 0 };
+	if (!msg || msg->first_line.type != SIP_REQUEST) {
+		LM_ERR("This is not a request!!!\n");
+		return realm;
+	}
+
+	if (!msg->parsed_orig_ruri_ok)
+		if (parse_orig_ruri(msg) < 0)
+			return realm;
+
+	realm = msg->parsed_orig_ruri.host;
+	return realm;
+}

+ 66 - 0
modules/registrar_scscf/userdata_parser.h

@@ -0,0 +1,66 @@
+/*
+ * $Id$
+ *
+ * The initial version of this code was written by Dragos Vingarzan
+ * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
+ * Fruanhofer Institute. It was and still is maintained in a separate
+ * branch of the original SER. We are therefore migrating it to
+ * Kamailio/SR and look forward to maintaining it from here on out.
+ * 2011/2012 Smile Communications, Pty. Ltd.
+ * ported/maintained/improved by 
+ * Jason Penton (jason(dot)penton(at)smilecoms.com and
+ * Richard Good (richard(dot)good(at)smilecoms.com) as part of an 
+ * effort to add full IMS support to Kamailio/SR using a new and
+ * improved architecture
+ * 
+ * NB: Alot of this code was originally part of OpenIMSCore,
+ * FhG Fokus. 
+ * Copyright (C) 2004-2006 FhG Fokus
+ * Thanks for great work! This is an effort to 
+ * break apart the various CSCF functions into logically separate
+ * components. We hope this will drive wider use. We also feel
+ * that in this way the architecture is more complete and thereby easier
+ * to manage in the Kamailio/SR environment
+ *
+ * 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 USERDATA_PARSER_H
+#define	USERDATA_PARSER_H
+
+#include "../../modules/usrloc_scscf/usrloc.h"
+#include <libxml/xmlschemas.h>
+#include <libxml/xmlschemastypes.h>
+#include <libxml/parser.h>
+#include "../../locking.h"
+
+extern char *scscf_user_data_xsd;
+extern char *scscf_user_data_dtd;
+extern int scscf_support_wildcardPSI;
+
+/**
+ * Parses the user data XML and copies data into a new ims_subscription structure.
+ * @param xml - the input xml
+ * @returns the ims_subscription* on success or NULL on error
+ */
+int parser_init(char *dtd_filename, char *xsd_filename);
+ims_subscription *parse_user_data(str xml);
+void print_user_data(ims_subscription *s);
+
+#endif	/* USERDATA_PARSER */
+

+ 109 - 0
modules/registrar_scscf/usrloc_cb.c

@@ -0,0 +1,109 @@
+/*
+ * $Id$
+ *
+ * The initial version of this code was written by Dragos Vingarzan
+ * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
+ * Fruanhofer Institute. It was and still is maintained in a separate
+ * branch of the original SER. We are therefore migrating it to
+ * Kamailio/SR and look forward to maintaining it from here on out.
+ * 2011/2012 Smile Communications, Pty. Ltd.
+ * ported/maintained/improved by 
+ * Jason Penton (jason(dot)penton(at)smilecoms.com and
+ * Richard Good (richard(dot)good(at)smilecoms.com) as part of an 
+ * effort to add full IMS support to Kamailio/SR using a new and
+ * improved architecture
+ * 
+ * NB: Alot of this code was originally part of OpenIMSCore,
+ * FhG Fokus. 
+ * Copyright (C) 2004-2006 FhG Fokus
+ * Thanks for great work! This is an effort to 
+ * break apart the various CSCF functions into logically separate
+ * components. We hope this will drive wider use. We also feel
+ * that in this way the architecture is more complete and thereby easier
+ * to manage in the Kamailio/SR environment
+ *
+ * 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 "server_assignment.h"
+#include "reg_mod.h"
+#include "registrar_notify.h"
+#include "usrloc_cb.h"
+
+extern str scscf_name_str;
+
+void ul_impu_inserted(impurecord_t* r, ucontact_t* c, int type, void* param) {
+
+    LM_DBG("Received notification of UL IMPU insert for IMPU <%.*s>", r->public_identity.len, r->public_identity.s);
+
+    LM_DBG("Registering for callbacks on this IMPU for contact insert, update, delete or expire to send notifications if there are any subscriptions");
+    ul.register_ulcb(r, 0, UL_IMPU_NEW_CONTACT, ul_contact_changed, 0); //this allows us to receive cbs on new contact for IMPU
+    ul.register_ulcb(r, 0, UL_IMPU_UPDATE_CONTACT | UL_IMPU_EXPIRE_CONTACT | UL_IMPU_DELETE_CONTACT, ul_contact_changed, 0);
+
+    LM_DBG("Selectively asking for expire or no contact delete callbacks only on the anchor of the implicit set so that we only send one SAR per implicit set");
+    if (r->is_primary) {
+        //TODO only do this if a flag in the IMPU record identifies this as the implicit set anchor
+        if (ul.register_ulcb(r, 0, UL_IMPU_REG_NC_DELETE | UL_IMPU_UNREG_EXPIRED, ul_impu_removed, 0) < 0) {
+            LM_ERR("can not register callback for no contacts delete or IMPI expire\n");
+            return;
+        }
+    }
+}
+
+void ul_impu_removed(impurecord_t* r, ucontact_t* c, int type, void* param) {
+    int assignment_type = AVP_IMS_SAR_USER_DEREGISTRATION;
+    int data_available = AVP_IMS_SAR_USER_DATA_NOT_AVAILABLE;
+
+    //we only send SAR if the REGISTRATION state is (NOT) IMPU_NOT_REGISTERED
+    LM_DBG("Received notification of UL IMPU removed for IMPU <%.*s>", r->public_identity.len, r->public_identity.s);
+
+    if (r->reg_state != IMPU_NOT_REGISTERED) {
+        LM_DBG("Sending SAR to DeRegister [%.*s] (pvt: <%.*s>)\n",
+                r->public_identity.len, r->public_identity.s,
+                r->s->private_identity.len, r->s->private_identity.s);
+        LM_DBG("Sending SAR\n");
+        cxdx_send_sar(NULL, r->public_identity, r->s->private_identity, scscf_name_str, assignment_type, data_available, 0);
+    }
+}
+
+void ul_contact_changed(impurecord_t* r, ucontact_t* c, int type, void* param) {
+
+    LM_DBG("Received notification of type %d on contact Address <%.*s>", type, c->c.len, c->c.s);
+    
+    if(!r->shead){
+        LM_DBG("There are no subscriptions for this IMPU therefore breaking out now as nothing to do");
+        return;
+    }
+    
+    if (type == UL_IMPU_DELETE_CONTACT) {
+        LM_DBG("Received notification of UL CONTACT DELETE");
+        event_reg(0, r, c, IMS_REGISTRAR_CONTACT_UNREGISTERED, 0, 0);
+    } else if (type == UL_IMPU_EXPIRE_CONTACT) {
+        LM_DBG("Received notification of UL CONTACT EXPIRE");
+        event_reg(0, r, c, IMS_REGISTRAR_CONTACT_EXPIRED, 0, 0);
+    } else if (type == UL_IMPU_UPDATE_CONTACT) {
+        LM_DBG("Received notification of UL CONTACT UPDATE");
+        event_reg(0, r, c, IMS_REGISTRAR_CONTACT_REFRESHED, 0, 0);
+    } else if (type == UL_IMPU_NEW_CONTACT) {
+        LM_DBG("Received notification of UL IMPU CONTACT INSERT");
+        event_reg(0, r, c, IMS_REGISTRAR_CONTACT_REGISTERED, 0, 0);
+    } else {
+        LM_DBG("This type of callback not supported here");
+    }
+}

+ 55 - 0
modules/registrar_scscf/usrloc_cb.h

@@ -0,0 +1,55 @@
+/*
+ * $Id$
+ *
+ * The initial version of this code was written by Dragos Vingarzan
+ * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
+ * Fruanhofer Institute. It was and still is maintained in a separate
+ * branch of the original SER. We are therefore migrating it to
+ * Kamailio/SR and look forward to maintaining it from here on out.
+ * 2011/2012 Smile Communications, Pty. Ltd.
+ * ported/maintained/improved by 
+ * Jason Penton (jason(dot)penton(at)smilecoms.com and
+ * Richard Good (richard(dot)good(at)smilecoms.com) as part of an 
+ * effort to add full IMS support to Kamailio/SR using a new and
+ * improved architecture
+ * 
+ * NB: Alot of this code was originally part of OpenIMSCore,
+ * FhG Fokus. 
+ * Copyright (C) 2004-2006 FhG Fokus
+ * Thanks for great work! This is an effort to 
+ * break apart the various CSCF functions into logically separate
+ * components. We hope this will drive wider use. We also feel
+ * that in this way the architecture is more complete and thereby easier
+ * to manage in the Kamailio/SR environment
+ *
+ * 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 USRLOC_CB_H
+#define	USRLOC_CB_H
+
+#include "../usrloc_scscf/usrloc.h"
+
+void ul_impu_removed(impurecord_t* r, ucontact_t* c, int type, void* param);
+
+void ul_impu_inserted(impurecord_t* r, ucontact_t* c, int type, void* param);
+
+void ul_contact_changed(impurecord_t* r, ucontact_t* c, int type, void* param);
+
+#endif	/* USRLOC_CB_H */
+