#include "ims_charging_mod.h"

#include <math.h>
#include "../../core/parser/msg_parser.h"
#include "../../core/parser/parse_uri.h"
#include "../../core/sr_module.h"
#include "../../core/socket_info.h"
#include "../../core/timer.h"
#include "../../core/locking.h"
#include "../../core/kemi.h"
#include "../../modules/tm/tm_load.h"

#include "../../modules/ims_dialog/dlg_hash.h"
#include "../../modules/ims_dialog/dlg_load.h"


#include "../cdp/cdp_load.h"
#include "../../core/mod_fix.h"

#include "../../core/parser/parse_from.h"
#include "../../core/parser/parse_to.h"

#include "../../lib/ims/ims_getters.h"
#include "../../core/parser/sdp/sdp.h"

#include "diameter_ro.h"
#include "ims_ro.h"
#include "Ro_data.h"
#include "dialog.h"

#include "ccr.h"
#include "config.h"
#include "ro_session_hash.h"
#include "ro_avp.h"
#include "ro_db_handler.h"
#include "ims_charging_stats.h"
#include "../../core/str.h"

static pv_spec_t *custom_user_avp; /*!< AVP for custom_user setting */
static pv_spec_t
		*app_provided_party_avp; /*!< AVP for app_provided_party setting */

extern struct tm_binds tmb;
extern struct cdp_binds cdpb;
extern client_ro_cfg cfg;
extern ims_dlg_api_t dlgb;
extern cdp_avp_bind_t *cdp_avp;
extern str ro_forced_peer;
extern int ro_db_mode;
extern struct ims_charging_counters_h ims_charging_cnts_h;
extern int vendor_specific_id;
extern int vendor_specific_chargeinfo;

struct session_setup_data
{
	struct ro_session *ro_session;
	void *action;
	unsigned int tindex;
	unsigned int tlabel;
};

extern struct dlg_binds *dlgb_p;
extern struct tm_binds tmb;

extern int interim_request_credits;

extern int voice_service_identifier;
extern int voice_rating_group;
extern int video_service_identifier;
extern int video_rating_group;

static int create_cca_return_code(int result);
static int create_cca_result_code(int result);
static int create_cca_fui_avps(int action, str *redirecturi);
static void resume_on_initial_ccr(
		int is_timeout, void *param, AAAMessage *cca, long elapsed_msecs);
static void resume_on_interim_ccr(
		int is_timeout, void *param, AAAMessage *cca, long elapsed_msecs);
static void resume_on_termination_ccr(
		int is_timeout, void *param, AAAMessage *cca, long elapsed_msecs);
static int get_mac_avp_value(struct sip_msg *msg, str *value);

void init_custom_user(pv_spec_t *custom_user_avp_p)
{
	custom_user_avp = custom_user_avp_p;
}

void init_app_provided_party(pv_spec_t *app_provided_party_avp_p)
{
	app_provided_party_avp = app_provided_party_avp_p;
}

/*!
 * \brief Return the custom_user for a record route
 * \param req SIP message
 * \param custom_user to be returned
 * \return <0 for failure
 */
static int get_custom_user(struct sip_msg *req, str *custom_user)
{
	pv_value_t pv_val;

	if(custom_user_avp) {
		if((pv_get_spec_value(req, custom_user_avp, &pv_val) == 0)
				&& (pv_val.flags & PV_VAL_STR) && (pv_val.rs.len > 0)) {
			custom_user->s = pv_val.rs.s;
			custom_user->len = pv_val.rs.len;
			return 0;
		}
		LM_DBG("invalid AVP value, using default user from "
			   "P-Asserted-Identity/From-Header\n");
	}

	return -1;
}

/*!
 * \brief Return the Application-Provided-Called-Party-Address
 * \param req SIP message
 * \param address to be returned
 * \return <0 for failure
 */
static int get_app_provided_party(struct sip_msg *req, str *address)
{
	pv_value_t pv_val;

	if(app_provided_party_avp) {
		if((pv_get_spec_value(req, app_provided_party_avp, &pv_val) == 0)
				&& (pv_val.flags & PV_VAL_STR) && (pv_val.rs.len > 0)) {
			address->s = pv_val.rs.s;
			address->len = pv_val.rs.len;
			return 0;
		}
		LM_DBG("invalid AVP value, no "
			   "Application-Provided-Called-Party-Address\n");
	}

	return -1;
}

/*!
 * \brief Format subscription id according to settings
 * \param subscription_id subscription id
 * \param subscription_id_type format according to subscription id value
 */
static void format_subscription_id(
		str *subscription_id, int *subscription_id_type)
{
	if(strncasecmp(subscription_id->s, "tel:", 4) == 0) {
		*subscription_id_type = Subscription_Type_MSISDN;
		subscription_id->s += 4;
		subscription_id->len -= 4;
		/*if stripping is not done the format should actually be Subscription_Type_IMPU/END_USER_SIP_URI,
		but that could be a breaking change */
		if(cfg.strip_plus_from_e164
				&& strncasecmp(subscription_id->s, "+", 1) == 0) {
			//subscription should be purely digits when using MSISDN/E164 format
			subscription_id->s += 1;
			subscription_id->len -= 1;
		}
	} else {
		*subscription_id_type =
				Subscription_Type_IMPU; //default is END_USER_SIP_URI
	}
}


void credit_control_session_callback(int event, void *session)
{
	switch(event) {
		case AUTH_EV_SESSION_DROP:
			LM_DBG("Received notification of CC App session drop - we must "
				   "free the generic data\n");
			break;
		default:
			LM_DBG("Received unhandled event [%d] in credit control session "
				   "callback from CDP\n",
					event);
	}
}

/**
 * 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
 */
int Ro_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;
}

int Ro_add_cc_request(AAAMessage *msg, unsigned int cc_request_type,
		unsigned int cc_request_number)
{
	char x[4];
	LM_DBG("add cc request %d\n", cc_request_type);
	set_4bytes(x, cc_request_type);
	int success = Ro_add_avp(msg, x, 4, AVP_CC_Request_Type,
			AAA_AVP_FLAG_MANDATORY, 0, AVP_DUPLICATE_DATA, __FUNCTION__);

	char y[4];
	set_4bytes(y, cc_request_number);

	return success
		   && Ro_add_avp(msg, y, 4, AVP_CC_Request_Number,
				   AAA_AVP_FLAG_MANDATORY, 0, AVP_DUPLICATE_DATA, __FUNCTION__);
}

int Ro_add_event_timestamp(AAAMessage *msg, time_t now)
{
	char x[4];
	str s = {x, 4};
	LM_DBG("add Event-Timestamp\n");
	uint32_t ntime = htonl(now + EPOCH_UNIX_TO_EPOCH_NTP);
	memcpy(x, &ntime, sizeof(uint32_t));

	return Ro_add_avp(msg, s.s, s.len, AVP_Event_Timestamp, AAA_AVP_FLAG_NONE,
			0, AVP_DUPLICATE_DATA, __FUNCTION__);
}

int Ro_add_user_equipment_info(AAAMessage *msg, unsigned int type, str value)
{
	AAA_AVP_LIST list;
	str group;
	char x[4];

	list.head = 0;
	list.tail = 0;

	set_4bytes(x, type);
	Ro_add_avp_list(&list, x, 4, AVP_User_Equipment_Info_Type,
			AAA_AVP_FLAG_MANDATORY, 0, AVP_DUPLICATE_DATA, __FUNCTION__);

	Ro_add_avp_list(&list, value.s, value.len, AVP_User_Equipment_Info_Value,
			AAA_AVP_FLAG_MANDATORY, 0, AVP_DUPLICATE_DATA, __FUNCTION__);

	group = cdpb.AAAGroupAVPS(list);

	cdpb.AAAFreeAVPList(&list);

	return Ro_add_avp(msg, group.s, group.len, AVP_User_Equipment_Info,
			AAA_AVP_FLAG_MANDATORY, 0, AVP_FREE_DATA, __FUNCTION__);
}

int Ro_add_termination_cause(AAAMessage *msg, unsigned int term_code)
{
	char x[4];
	str s = {x, 4};
	LM_DBG("add termination cause %d\n", term_code);
	uint32_t code = htonl(term_code);
	memcpy(x, &code, sizeof(uint32_t));

	return Ro_add_avp(msg, s.s, s.len, AVP_Termination_Cause,
			AAA_AVP_FLAG_MANDATORY, 0, AVP_DUPLICATE_DATA, __FUNCTION__);
}

int Ro_add_vendor_specific_termination_cause(
		AAAMessage *msg, unsigned int term_code)
{
	char x[4];
	str s = {x, 4};
	LM_DBG("add vendor specific termination cause %d\n", term_code);
	uint32_t code = htonl(term_code);
	memcpy(x, &code, sizeof(uint32_t));

	return Ro_add_avp(msg, s.s, s.len, VS_TERMCODE,
			AAA_AVP_FLAG_VENDOR_SPECIFIC, 10, AVP_DUPLICATE_DATA, __FUNCTION__);
}

int Ro_add_vendor_specific_termination_reason(AAAMessage *msg, str *reason)
{
	LM_DBG("add vendor specific termination reason: %.*s\n", reason->len,
			reason->s);
	return Ro_add_avp(msg, reason->s, reason->len, VS_TERMREASON,
			AAA_AVP_FLAG_VENDOR_SPECIFIC, 10, AVP_DUPLICATE_DATA, __FUNCTION__);
}


/* called only when building stop record AVPS */
int Ro_add_multiple_service_credit_Control_stop(AAAMessage *msg, int used_unit,
		int active_rating_group, int active_service_identifier)
{
	char x[4];
	AAA_AVP_LIST used_list, mscc_list;
	str used_group;

	LM_DBG("add multiple service credit control stop, used unit %d\n",
			used_unit);
	// Add Multiple-Services AVP Indicator
	set_4bytes(x, 1);
	Ro_add_avp(msg, x, 4, AVP_Multiple_Services_Indicator,
			AAA_AVP_FLAG_MANDATORY, 0, AVP_DUPLICATE_DATA, __FUNCTION__);

	//unsigned int service_id = 1000; //Removed these are now configurable config file params

	//unsigned int rating_group = 500; //Removed these are now configurable config file params

	used_list.head = 0;
	used_list.tail = 0;
	mscc_list.head = 0;
	mscc_list.tail = 0;

	/* if we must Used-Service-Unit */
	if(used_unit >= 0) {
		set_4bytes(x, used_unit);
		Ro_add_avp_list(&used_list, x, 4, AVP_CC_Time, AAA_AVP_FLAG_MANDATORY,
				0, AVP_DUPLICATE_DATA, __FUNCTION__);
		used_group = cdpb.AAAGroupAVPS(used_list);
		cdpb.AAAFreeAVPList(&used_list);
		Ro_add_avp_list(&mscc_list, used_group.s, used_group.len,
				AVP_Used_Service_Unit, AAA_AVP_FLAG_MANDATORY, 0, AVP_FREE_DATA,
				__FUNCTION__);
	}

	set_4bytes(x, active_service_identifier);
	Ro_add_avp_list(&mscc_list, x, 4, AVP_Service_Identifier,
			AAA_AVP_FLAG_MANDATORY, 0, AVP_DUPLICATE_DATA, __FUNCTION__);

	// Rating Group = -1 => omit Rating group
	if(active_rating_group >= 0) {
		set_4bytes(x, active_rating_group);
		Ro_add_avp_list(&mscc_list, x, 4, AVP_Rating_Group,
				AAA_AVP_FLAG_MANDATORY, 0, AVP_DUPLICATE_DATA, __FUNCTION__);
	}

	used_group = cdpb.AAAGroupAVPS(mscc_list);
	cdpb.AAAFreeAVPList(&mscc_list);

	return Ro_add_avp(msg, used_group.s, used_group.len,
			AVP_Multiple_Services_Credit_Control, AAA_AVP_FLAG_MANDATORY, 0,
			AVP_FREE_DATA, __FUNCTION__);
}

int Ro_add_multiple_service_credit_Control(AAAMessage *msg,
		unsigned int requested_unit, int used_unit, int active_rating_group,
		int active_service_identifier)
{
	// Add Multiple-Services AVP Indicator
	char x[4];
	set_4bytes(x, 1);
	Ro_add_avp(msg, x, 4, AVP_Multiple_Services_Indicator,
			AAA_AVP_FLAG_MANDATORY, 0, AVP_DUPLICATE_DATA, __FUNCTION__);

	AAA_AVP_LIST list, used_list, mscc_list;
	str group, used_group;

	list.head = 0;
	list.tail = 0;
	used_list.head = 0;
	used_list.tail = 0;
	mscc_list.head = 0;
	mscc_list.tail = 0;

	LM_DBG("add multiple service credit control, requested unit %d\n",
			requested_unit);
	set_4bytes(x, requested_unit);
	Ro_add_avp_list(&list, x, 4, AVP_CC_Time, AAA_AVP_FLAG_MANDATORY, 0,
			AVP_DUPLICATE_DATA, __FUNCTION__);
	group = cdpb.AAAGroupAVPS(list);
	cdpb.AAAFreeAVPList(&list);

	Ro_add_avp_list(&mscc_list, group.s, group.len, AVP_Requested_Service_Unit,
			AAA_AVP_FLAG_MANDATORY, 0, AVP_FREE_DATA, __FUNCTION__);

	set_4bytes(x, active_service_identifier);
	Ro_add_avp_list(&mscc_list, x, 4, AVP_Service_Identifier,
			AAA_AVP_FLAG_MANDATORY, 0, AVP_DUPLICATE_DATA, __FUNCTION__);

	// Rating Group = -1 => omit Rating group
	if(active_rating_group >= 0) {
		set_4bytes(x, active_rating_group);
		Ro_add_avp_list(&mscc_list, x, 4, AVP_Rating_Group,
				AAA_AVP_FLAG_MANDATORY, 0, AVP_DUPLICATE_DATA, __FUNCTION__);
	}

	/* if we must Used-Service-Unit */
	if(used_unit >= 0) {
		set_4bytes(x, used_unit);
		Ro_add_avp_list(&used_list, x, 4, AVP_CC_Time, AAA_AVP_FLAG_MANDATORY,
				0, AVP_DUPLICATE_DATA, __FUNCTION__);
		used_group = cdpb.AAAGroupAVPS(used_list);
		cdpb.AAAFreeAVPList(&used_list);
		Ro_add_avp_list(&mscc_list, used_group.s, used_group.len,
				AVP_Used_Service_Unit, AAA_AVP_FLAG_MANDATORY, 0, AVP_FREE_DATA,
				__FUNCTION__);
	}

	group = cdpb.AAAGroupAVPS(mscc_list);
	cdpb.AAAFreeAVPList(&mscc_list);

	return Ro_add_avp(msg, group.s, group.len,
			AVP_Multiple_Services_Credit_Control, AAA_AVP_FLAG_MANDATORY, 0,
			AVP_FREE_DATA, __FUNCTION__);
}

int Ro_add_subscription_id(AAAMessage *msg, unsigned int type,
		str *subscription_id) //, struct sip_msg* sip_msg)
{
	AAA_AVP_LIST list;
	str group;
	char x[4];

	list.head = 0;
	list.tail = 0;

	LM_DBG("add Subscription-Id type %d, id %.*s\n", type, subscription_id->len,
			subscription_id->s);

	set_4bytes(x, type);
	Ro_add_avp_list(&list, x, 4, AVP_Subscription_Id_Type,
			AAA_AVP_FLAG_MANDATORY, 0, AVP_DUPLICATE_DATA, __FUNCTION__);

	Ro_add_avp_list(&list, subscription_id->s, subscription_id->len,
			AVP_Subscription_Id_Data, AAA_AVP_FLAG_MANDATORY, 0,
			AVP_DUPLICATE_DATA, __FUNCTION__);

	group = cdpb.AAAGroupAVPS(list);

	cdpb.AAAFreeAVPList(&list);

	return Ro_add_avp(msg, group.s, group.len, AVP_Subscription_Id,
			AAA_AVP_FLAG_MANDATORY, 0, AVP_FREE_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
 */
int Ro_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;

	LM_DBG("add Vendor-Specific-Application-Id %d\n", vendor_id);

	set_4bytes(x, vendor_id);
	Ro_add_avp_list(&list, x, 4, AVP_Vendor_Id, AAA_AVP_FLAG_MANDATORY, 0,
			AVP_DUPLICATE_DATA, __FUNCTION__);

	if(auth_id) {
		LM_DBG("adding Auth-Application-Id %d\n", auth_id);
		set_4bytes(x, auth_id);
		Ro_add_avp_list(&list, x, 4, AVP_Auth_Application_Id,
				AAA_AVP_FLAG_MANDATORY, 0, AVP_DUPLICATE_DATA, __FUNCTION__);
	}
	if(acct_id) {
		LM_DBG("adding Acct-Application-Id %d\n", acct_id);
		set_4bytes(x, acct_id);
		Ro_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 Ro_add_avp(msg, group.s, group.len,
			AVP_Vendor_Specific_Application_Id, AAA_AVP_FLAG_MANDATORY, 0,
			AVP_FREE_DATA, __FUNCTION__);
}

int get_sip_header_info(struct sip_msg *req, struct sip_msg *reply,
		int32_t *acc_record_type, str *sip_method, str *event,
		uint32_t *expires, str *callid, str *asserted_id_uri, str *to_uri)
{
	int expires_header;
	sip_method->s = req->first_line.u.request.method.s;
	sip_method->len = req->first_line.u.request.method.len;

	if(strncmp(sip_method->s, "INVITE", 6) == 0)
		*acc_record_type = AAA_ACCT_START;
	else if(strncmp(sip_method->s, "BYE", 3) == 0)
		*acc_record_type = AAA_ACCT_STOP;
	else
		*acc_record_type = AAA_ACCT_EVENT;

	*event = cscf_get_event(req);
	/* return value is signed int, while *expires is uint32_t.
	when -1 is returned (header not found) and assigned directly to *expires,
	this is actually added as an AVP in the outgoing diameter request */
	expires_header = cscf_get_expires_hdr(req, 0);
	if(expires_header > 0)
		*expires = expires_header;
	*callid = cscf_get_call_id(req, NULL);

	if(get_custom_user(req, asserted_id_uri) == -1) {
		if((*asserted_id_uri = cscf_get_asserted_identity(req, 0)).len == 0) {
			LM_DBG("No P-Asserted-Identity hdr found. Using From hdr\n");

			if(!cscf_get_from_uri(req, asserted_id_uri)) {
				LM_ERR("Error assigning P-Asserted-Identity using From hdr\n");
				goto error;
			}
		}
	}

	*to_uri = req->first_line.u.request.uri;

	LM_DBG("retrieved sip info : sip_method %.*s acc_record_type %i, event "
		   "%.*s expires %u "
		   "call_id %.*s from_uri %.*s to_uri %.*s\n",
			sip_method->len, sip_method->s, *acc_record_type, event->len,
			event->s, *expires, callid->len, callid->s, asserted_id_uri->len,
			asserted_id_uri->s, to_uri->len, to_uri->s);

	return 1;
error:
	return 0;
}

int get_ims_charging_info(struct sip_msg *req, struct sip_msg *reply, str *icid,
		str *orig_ioi, str *term_ioi)
{

	LM_DBG("get ims charging info\n");
	if(req)
		cscf_get_p_charging_vector(req, icid, orig_ioi, term_ioi);
	if(reply)
		cscf_get_p_charging_vector(reply, icid, orig_ioi, term_ioi);

	return 1;
}

int get_timestamps(struct sip_msg *req, struct sip_msg *reply,
		time_t *req_timestamp, time_t *reply_timestamp)
{

	if(reply)
		*reply_timestamp = time(NULL);
	if(req)
		*req_timestamp = time(NULL);
	return 1;
}

/*
 * creates the ro session for a session establishment
 *
 */

Ro_CCR_t *dlg_create_ro_session(struct sip_msg *req, struct sip_msg *reply,
		AAASession **authp, int dir, str asserted_identity,
		str called_asserted_identity, str subscription_id,
		int subscription_id_type, str *incoming_trunk_id,
		str *outgoing_trunk_id, str *pani, str *app_provided_party)
{

	Ro_CCR_t *ro_ccr_data = 0;
	AAASession *auth = NULL;
	str user_name /* ={0,0}*/, sip_method = {0, 0}, event = {0, 0};
	uint32_t expires = 0;
	str callid = {0, 0}, to_uri = {0, 0}, from_uri = {0, 0}, icid = {0, 0},
		orig_ioi = {0, 0}, term_ioi = {0, 0};
	event_type_t *event_type = 0;
	ims_information_t *ims_info = 0;
	time_stamps_t *time_stamps = 0;
	time_t req_timestamp = 0, reply_timestamp = 0;
	int32_t acc_record_type;
	subscription_id_t subscr;

	*authp = 0;

	if(!get_sip_header_info(req, reply, &acc_record_type, &sip_method, &event,
			   &expires, &callid, &from_uri, &to_uri))
		goto error;
	user_name.s = subscription_id.s;
	user_name.len = subscription_id.len;

	/*	if(!get_ims_charging_info(req, reply, &icid, &orig_ioi, &term_ioi))
                    goto error;
     */
	LM_DBG("retrieved ims charging info icid:[%.*s] orig_ioi:[%.*s] "
		   "term_ioi:[%.*s]\n",
			icid.len, icid.s, orig_ioi.len, orig_ioi.s, term_ioi.len,
			term_ioi.s);

	if(!get_timestamps(req, reply, &req_timestamp, &reply_timestamp))
		goto error;

	if(!(event_type = new_event_type(&sip_method, &event, &expires)))
		goto error;

	if(!(time_stamps = new_time_stamps(
				 &req_timestamp, NULL, &reply_timestamp, NULL)))
		goto error;

	if(!(ims_info = new_ims_information(event_type, time_stamps, &callid,
				 &callid, &asserted_identity, &called_asserted_identity, &icid,
				 &orig_ioi, &term_ioi, dir, incoming_trunk_id,
				 outgoing_trunk_id, pani, app_provided_party)))
		goto error;
	LM_DBG("created IMS information\n");
	event_type = 0;
	time_stamps = 0;

	subscr.id.s = subscription_id.s;
	subscr.id.len = subscription_id.len;
	subscr.type = subscription_id_type;

	ro_ccr_data = new_Ro_CCR(acc_record_type, &user_name, ims_info, &subscr, 0);
	if(!ro_ccr_data) {
		LM_ERR("dlg_create_ro_session: no memory left for generic\n");
		goto out_of_memory;
	}
	ims_info = 0;

	if(strncmp(req->first_line.u.request.method.s, "INVITE", 6) == 0) {
		//create CDP CC Accounting session
		auth = cdpb.AAACreateCCAccSession(credit_control_session_callback,
				1 /*is_session*/, NULL); //must unlock session hash when done
		LM_DBG("Created Ro Session with id Session ID [%.*s]\n", auth->id.len,
				auth->id.s);
		//save_session = auth->id;
	}
	/*if (strncmp(req->first_line.u.request.method.s, "BYE", 3) == 0) {
        auth = cdp_avp->cdp->AAAGetAuthSession(save_session);
    }*/


	if(!auth) {
		LM_ERR("unable to create the Ro Session\n");
		goto error;
	}

	*authp = auth;
	return ro_ccr_data;

out_of_memory:
error:
	time_stamps_free(time_stamps);
	event_type_free(event_type);
	ims_information_free(ims_info);
	Ro_free_CCR(ro_ccr_data);

	return NULL;
}

int sip_create_ro_ccr_data(struct sip_msg *msg, int dir, Ro_CCR_t **ro_ccr_data,
		AAASession **auth, str asserted_identity, str called_asserted_identity,
		str subscription_id, int subscription_id_type, str *incoming_trunk_id,
		str *outgoing_trunk_id, str *pani, str *app_provided_party)
{

	if(msg->first_line.type == SIP_REQUEST) {
		/*end of session*/
		if(strncmp(msg->first_line.u.request.method.s, "INVITE", 6) == 0) {
			if(!(*ro_ccr_data = dlg_create_ro_session(msg, NULL, auth, dir,
						 asserted_identity, called_asserted_identity,
						 subscription_id, subscription_id_type,
						 incoming_trunk_id, outgoing_trunk_id, pani,
						 app_provided_party)))
				goto error;
		}
	} else {
		goto error; //We only support Request (INVITE) messages on this interface
	}

	return 1;
error:
	return 0;
}

/* must be called with lock on ro_session */
void send_ccr_interim(
		struct ro_session *ro_session, unsigned int used, unsigned int reserve)
{
	AAASession *auth = 0;

	AAAMessage *ccr = 0;
	Ro_CCR_t *ro_ccr_data = 0;
	ims_information_t *ims_info = 0;
	int32_t acc_record_type;
	subscription_id_t subscr;
	time_stamps_t *time_stamps;
	struct interim_ccr *i_req = shm_malloc(sizeof(struct interim_ccr));
	int ret = 0;
	event_type_t *event_type;

	memset(i_req, 0, sizeof(sizeof(struct interim_ccr)));
	i_req->ro_session = ro_session;

	str sip_method = str_init("dummy");
	str sip_event = str_init("dummy");

	str user_name = {0, 0};

	time_t req_timestamp;

	event_type = new_event_type(&sip_method, &sip_event, 0);

	LM_DBG("Sending interim CCR request for (usage:new) [%i:%i] seconds for "
		   "user [%.*s] using session id [%.*s] active rating group [%d] "
		   "active service identifier [%d] incoming_trunk_id [%.*s] "
		   "outgoing_trunk_id [%.*s]\n",
			used, reserve, ro_session->asserted_identity.len,
			ro_session->asserted_identity.s, ro_session->ro_session_id.len,
			ro_session->ro_session_id.s, ro_session->rating_group,
			ro_session->service_identifier, ro_session->incoming_trunk_id.len,
			ro_session->incoming_trunk_id.s, ro_session->outgoing_trunk_id.len,
			ro_session->outgoing_trunk_id.s);

	req_timestamp = time(0);

	if(!(time_stamps = new_time_stamps(&req_timestamp, NULL, NULL, NULL)))
		goto error;

	if(!(ims_info = new_ims_information(event_type, time_stamps,
				 &ro_session->callid, &ro_session->callid,
				 &ro_session->asserted_identity,
				 &ro_session->called_asserted_identity, 0, 0, 0,
				 ro_session->direction, &ro_session->incoming_trunk_id,
				 &ro_session->outgoing_trunk_id, &ro_session->pani,
				 &ro_session->app_provided_party)))
		goto error;

	LM_DBG("created IMS information\n");

	event_type = 0;

	if(ro_session->direction == RO_ORIG_DIRECTION) {
		subscr.id = ro_session->asserted_identity;


	} else if(ro_session->direction == RO_TERM_DIRECTION) {
		subscr.id = ro_session->called_asserted_identity;
	} else {
		LM_CRIT("don't know what to do in unknown mode - should we even get "
				"here\n");
		goto error;
	}

	format_subscription_id(&subscr.id, &subscr.type);

	user_name.s = subscr.id.s;
	user_name.len = subscr.id.len;

	acc_record_type = AAA_ACCT_INTERIM;

	ro_ccr_data = new_Ro_CCR(acc_record_type, &user_name, ims_info, &subscr,
			&ro_session->origin_host);
	if(!ro_ccr_data) {
		LM_ERR("no memory left for generic\n");
		goto error;
	}
	ims_info = NULL;

	auth = cdpb.AAAGetCCAccSession(ro_session->ro_session_id);
	if(!auth) {
		LM_DBG("Diameter Auth Session has timed out.... creating a new one.\n");
		/* lets try and recreate this session */
		//TODO: make a CC App session auth = cdpb.AAASession(ro_session->auth_appid, ro_session->auth_session_type, ro_session->ro_session_id); //TODO: would like this session to last longer (see session timeout in cdp
		//BUG("Oh shit, session timed out and I don't know how to create a new one.");

		auth = cdpb.AAAMakeSession(ro_session->auth_appid,
				ro_session->auth_session_type,
				ro_session
						->ro_session_id); //TODO: would like this session to last longer (see session timeout in cdp
		if(!auth)
			goto error;
	}

	//don't send INTERIM record if session is not in OPEN state (it could already be waiting for a previous response, etc)
	if(auth->u.cc_acc.state != ACC_CC_ST_OPEN) {
		LM_WARN("ignoring interim update on CC session not in correct state, "
				"currently in state [%d]\n",
				auth->u.cc_acc.state);
		goto error;
	}

	if(!(ccr = Ro_new_ccr(auth, ro_ccr_data)))
		goto error;

	if(!Ro_add_vendor_specific_appid(
			   ccr, IMS_vendor_id_3GPP, IMS_Ro, 0 /*acct id*/)) {
		LM_ERR("Problem adding Vendor specific ID\n");
	}
	ro_session->hop_by_hop += 1;
	if(!Ro_add_cc_request(ccr, RO_CC_INTERIM, ro_session->hop_by_hop)) {
		LM_ERR("Problem adding CC-Request data\n");
	}
	if(!Ro_add_event_timestamp(ccr, time(NULL))) {
		LM_ERR("Problem adding Event-Timestamp data\n");
	}

	if(!Ro_add_user_equipment_info(
			   ccr, AVP_EPC_User_Equipment_Info_Type_MAC, ro_session->mac)) {
		LM_ERR("Problem adding User-Equipment data\n");
	}

	if(!Ro_add_subscription_id(ccr, subscr.type, &(subscr.id))) {
		LM_ERR("Problem adding Subscription ID data\n");
	}

	if(!Ro_add_multiple_service_credit_Control(ccr,
			   interim_request_credits /*INTERIM_CREDIT_REQ_AMOUNT*/, used,
			   ro_session->rating_group, ro_session->service_identifier)) {
		LM_ERR("Problem adding Multiple Service Credit Control data\n");
	}

	LM_DBG("Sending CCR Diameter message.\n");
	ro_session->last_event_timestamp_backup = ro_session->last_event_timestamp;
	ro_session->last_event_timestamp =
			get_current_time_micro(); /*this is to make sure that if we get a term request now that we don't double bill for this time we are about to bill for in the interim */

	cdpb.AAASessionsUnlock(auth->hash);

	if(ro_forced_peer.len > 0) {
		ret = cdpb.AAASendMessageToPeer(
				ccr, &ro_forced_peer, resume_on_interim_ccr, (void *)i_req);
	} else {
		ret = cdpb.AAASendMessage(ccr, resume_on_interim_ccr, (void *)i_req);
	}

	if(ret != 1) {
		ccr = 0; // If an error is returned from cdp AAASendMessage, the message has been freed there
		goto error;
	}
	//    cdpb.AAASessionsUnlock(auth->hash);

	Ro_free_CCR(ro_ccr_data);

	counter_inc(ims_charging_cnts_h.interim_ccrs);
	return;
error:
	LM_ERR("error trying to reserve interim credit\n");

	if(ro_ccr_data)
		Ro_free_CCR(ro_ccr_data);

	if(ccr)
		cdpb.AAAFreeMessage(&ccr);

	if(auth) {
		cdpb.AAASessionsUnlock(auth->hash);
		/*we're no longer using cdpb.AAADropCCAccSession() here, since this
		session might be restarted by a re-auth request from the server or by call end*/
	}

	shm_free(i_req);
	//
	// since callback function will be never called because of the error, we need to release the lock on the session
	// to it can be reused later.
	//
	unref_ro_session(ro_session, 1, 1);

	return;
}

static void resume_on_interim_ccr(
		int is_timeout, void *param, AAAMessage *cca, long elapsed_msecs)
{
	struct interim_ccr *i_req = (struct interim_ccr *)param;
	Ro_CCA_t *ro_cca_data = NULL;
	int error_or_timeout = 0;

	if(is_timeout) {
		counter_inc(ims_charging_cnts_h.ccr_timeouts);
		LM_ERR("Transaction timeout - did not get CCA\n");
		error_or_timeout = 1;
		goto error;
	}

	counter_add(ims_charging_cnts_h.ccr_response_time, elapsed_msecs);
	counter_inc(ims_charging_cnts_h.ccr_replies_received);

	if(!i_req) {
		LM_ERR("This is so wrong: ro session is NULL\n");
		error_or_timeout = 1;
		goto error;
	}

	if(cca == NULL) {
		LM_ERR("Error reserving credit for CCA.\n");
		error_or_timeout = 1;
		goto error;
	}

	ro_cca_data = Ro_parse_CCA_avps(cca);

	if(ro_cca_data == NULL) {
		LM_ERR("Could not parse CCA message response.\n");
		error_or_timeout = 1;
		goto error;
	}

	if(ro_cca_data->resultcode != 2001) {
		LM_ERR("Got bad CCA result code [%d] - reservation failed\n",
				ro_cca_data->resultcode);
		error_or_timeout = 1;
		goto error;
	} else {
		LM_DBG("Valid CCA response with time chunk of [%i] and validity "
			   "[%i].\n",
				ro_cca_data->mscc->granted_service_unit->cc_time,
				ro_cca_data->mscc->validity_time);
	}

	i_req->new_credit = ro_cca_data->mscc->granted_service_unit->cc_time;
	i_req->credit_valid_for = ro_cca_data->mscc->validity_time;
	i_req->is_final_allocation = 0;
	if(ro_cca_data->mscc->time_quota_threshold > 0) {
		LM_DBG("updating session ro_timer_buffer to %i from "
			   "time_quota_threshold in server response\n",
				ro_cca_data->mscc->time_quota_threshold);
		i_req->ro_session->ro_timer_buffer =
				ro_cca_data->mscc->time_quota_threshold;
	}

	if(ro_cca_data->mscc->final_unit_action
			&& (ro_cca_data->mscc->final_unit_action->action == 0))
		i_req->is_final_allocation = 1;

	if(ro_cca_data->origin_host.s && ro_cca_data->origin_host.len > 0) {
		if(i_req->ro_session->origin_host.s
				&& i_req->ro_session->origin_host.len > 0
				&& (!strncasecmp(i_req->ro_session->origin_host.s,
							ro_cca_data->origin_host.s,
							ro_cca_data->origin_host.len)
						|| i_req->ro_session->origin_host.len
								   != ro_cca_data->origin_host.len)) {
			LM_DBG("origin host for session has changed\n");
			i_req->ro_session->origin_host.s =
					(char *)shm_realloc(i_req->ro_session->origin_host.s,
							ro_cca_data->origin_host.len);
			if(!i_req->ro_session->origin_host.s) {
				LM_ERR("no more shm mem\n");
				goto error;
			}

			i_req->ro_session->origin_host.len = ro_cca_data->origin_host.len;
			memcpy(i_req->ro_session->origin_host.s, ro_cca_data->origin_host.s,
					ro_cca_data->origin_host.len);
		}
	}

	Ro_free_CCA(ro_cca_data);
	cdpb.AAAFreeMessage(&cca);

	counter_inc(ims_charging_cnts_h.successful_interim_ccrs);
	goto success;

error:
	counter_inc(ims_charging_cnts_h.failed_interim_ccr);
	if(ro_cca_data)
		Ro_free_CCA(ro_cca_data);

	if(!is_timeout && cca) {
		cdpb.AAAFreeMessage(&cca);
	}

	if(i_req) {
		i_req->credit_valid_for = 0;
		i_req->new_credit = 0;
	}

success:
	resume_ro_session_ontimeout(i_req, error_or_timeout);
}

long get_current_time_micro()
{
	struct timeval tv;

	gettimeofday(&tv, 0);
	return tv.tv_sec * 1000000 + tv.tv_usec;
}

void send_ccr_stop_with_param(
		struct ro_session *ro_session, unsigned int code, str *reason)
{
	AAASession *auth = 0;
	Ro_CCR_t *ro_ccr_data = 0;
	AAAMessage *ccr = 0;
	ims_information_t *ims_info = 0;
	int32_t acc_record_type;
	subscription_id_t subscr;
	time_stamps_t *time_stamps;
	long used = 0;
	str user_name = {0, 0};
	int ret = 0;
	time_t stop_time;
	time_t actual_time_micros;
	int actual_time_seconds;

	stop_time = get_current_time_micro();

	if(ro_session->start_time == 0)
		actual_time_micros = 0;
	else
		actual_time_micros = stop_time - ro_session->start_time;

	actual_time_seconds = (actual_time_micros + (1000000 - 1)) / (float)1000000;

	if(ro_session->event_type != pending) {
		used = rint((stop_time - ro_session->last_event_timestamp)
					/ (float)1000000);
		LM_DBG("Final used number of seconds for session is %ld\n", used);
	}

	LM_DBG("Call started at %" TIME_T_FMT " and ended at %" TIME_T_FMT
		   " and lasted %d seconds and so far we have billed for %ld seconds\n",
			TIME_T_CAST(ro_session->start_time), TIME_T_CAST(stop_time),
			actual_time_seconds, ro_session->billed + used);
	if(ro_session->billed + used < actual_time_seconds) {
		LM_DBG("Making adjustment by adding %ld seconds\n",
				actual_time_seconds - (ro_session->billed + used));
		used += actual_time_seconds - (ro_session->billed + used);
	}

	counter_add(ims_charging_cnts_h.billed_secs, (int)used);

	event_type_t *event_type;

	str sip_method = str_init("dummy");
	str sip_event = str_init("dummy");

	time_t req_timestamp;

	event_type = new_event_type(&sip_method, &sip_event, 0);

	LM_DBG("Sending stop CCR request for (usage) [%i] seconds for user [%.*s] "
		   "using session id [%.*s] active rating group [%d] active service "
		   "identifier [%d] incoming_trunk_id [%.*s] outgoing_trunk_id [%.*s] "
		   "pani [%.*s]\n",
			(int)used, ro_session->asserted_identity.len,
			ro_session->asserted_identity.s, ro_session->ro_session_id.len,
			ro_session->ro_session_id.s, ro_session->rating_group,
			ro_session->service_identifier, ro_session->incoming_trunk_id.len,
			ro_session->incoming_trunk_id.s, ro_session->outgoing_trunk_id.len,
			ro_session->outgoing_trunk_id.s, ro_session->pani.len,
			ro_session->pani.s);

	req_timestamp = get_current_time_micro();

	if(!(time_stamps = new_time_stamps(&req_timestamp, NULL, NULL, NULL)))
		goto error0;

	if(!(ims_info = new_ims_information(event_type, time_stamps,
				 &ro_session->callid, &ro_session->callid,
				 &ro_session->asserted_identity,
				 &ro_session->called_asserted_identity, 0, 0, 0,
				 ro_session->direction, &ro_session->incoming_trunk_id,
				 &ro_session->outgoing_trunk_id, &ro_session->pani,
				 &ro_session->app_provided_party)))
		goto error0;

	event_type = 0;

	if(ro_session->direction == RO_ORIG_DIRECTION) {
		subscr.id = ro_session->asserted_identity;


	} else if(ro_session->direction == RO_TERM_DIRECTION) {
		subscr.id = ro_session->called_asserted_identity;
	} else {
		LM_CRIT("don't know what to do in unknown mode - should we even get "
				"here\n");
		goto error0;
	}

	format_subscription_id(&subscr.id, &subscr.type);

	user_name.s = subscr.id.s;
	user_name.len = subscr.id.len;


	acc_record_type = AAA_ACCT_STOP;

	ro_ccr_data = new_Ro_CCR(acc_record_type, &user_name, ims_info, &subscr,
			&ro_session->origin_host);
	if(!ro_ccr_data) {
		LM_ERR("send_ccr_stop: no memory left for generic\n");
		goto error0;
	}
	ims_info = 0;

	LM_DBG("Created Ro data\n");

	auth = cdpb.AAAGetCCAccSession(ro_session->ro_session_id);

	if(!auth) {
		LM_DBG("Diameter Auth Session has timed out.... creating a new one.\n");
		/* lets try and recreate this session */
		auth = cdpb.AAAMakeSession(ro_session->auth_appid,
				ro_session->auth_session_type,
				ro_session
						->ro_session_id); //TODO: would like this session to last longer (see session timeout in cdp
		if(!auth)
			goto error1;
	}


	if(!(ccr = Ro_new_ccr(auth, ro_ccr_data)))
		goto error1;

	LM_DBG("Created new CCR\n");

	if(!Ro_add_vendor_specific_appid(ccr, IMS_vendor_id_3GPP, IMS_Ro, 0)) {
		LM_ERR("Problem adding Vendor specific ID\n");
	}

	ro_session->hop_by_hop += 1;
	if(!Ro_add_cc_request(ccr, RO_CC_STOP, ro_session->hop_by_hop)) {
		LM_ERR("Problem adding CC-Request data\n");
	}

	if(!Ro_add_event_timestamp(ccr, time(NULL))) {
		LM_ERR("Problem adding Event-Timestamp data\n");
	}

	if(!Ro_add_user_equipment_info(
			   ccr, AVP_EPC_User_Equipment_Info_Type_MAC, ro_session->mac)) {
		LM_ERR("Problem adding User-Equipment data\n");
	}

	if(!Ro_add_subscription_id(ccr, subscr.type, &subscr.id)) {
		LM_ERR("Problem adding Subscription ID data\n");
	}

	if(!Ro_add_multiple_service_credit_Control_stop(ccr, used,
			   ro_session->rating_group, ro_session->service_identifier)) {
		LM_ERR("Problem adding Multiple Service Credit Control data\n");
	}

	if(!Ro_add_termination_cause(ccr, TERM_CAUSE_LOGOUT)) {
		LM_ERR("problem add Termination cause AVP to STOP record.\n");
	}

	if(vendor_specific_chargeinfo) {
		if(!Ro_add_vendor_specific_termination_cause(ccr, code)) {
			LM_ERR("problem add Termination cause AVP to STOP record.\n");
		}

		if(!Ro_add_vendor_specific_termination_reason(ccr, reason)) {
			LM_ERR("problem add Termination cause AVP to STOP record.\n");
		}
	}

	cdpb.AAASessionsUnlock(auth->hash);

	if(ro_forced_peer.len > 0) {
		ret = cdpb.AAASendMessageToPeer(
				ccr, &ro_forced_peer, resume_on_termination_ccr, NULL);
	} else {
		ret = cdpb.AAASendMessage(ccr, resume_on_termination_ccr, NULL);
	}

	if(ret != 1) {
		goto error1;
	}

	Ro_free_CCR(ro_ccr_data);

	counter_inc(ims_charging_cnts_h.final_ccrs);
	//    counter_add(ims_charging_cnts_h.active_ro_sessions, -1);
	return;

error1:
	LM_ERR("error on Ro STOP record\n");
	Ro_free_CCR(ro_ccr_data);

	if(auth) {
		cdpb.AAASessionsUnlock(auth->hash);
		cdpb.AAADropCCAccSession(auth);
	}

error0:
	return;
}

static void resume_on_termination_ccr(
		int is_timeout, void *param, AAAMessage *cca, long elapsed_msecs)
{
	Ro_CCA_t *ro_cca_data = NULL;

	if(is_timeout) {
		counter_inc(ims_charging_cnts_h.ccr_timeouts);
		LM_ERR("Transaction timeout - did not get CCA\n");
		goto error;
	}

	counter_inc(ims_charging_cnts_h.ccr_replies_received);
	counter_add(ims_charging_cnts_h.ccr_response_time, elapsed_msecs);

	if(!cca) {
		LM_ERR("Error in termination CCR.\n");
		counter_inc(ims_charging_cnts_h.failed_final_ccrs);
		return;
	}

	ro_cca_data = Ro_parse_CCA_avps(cca);

	if(ro_cca_data == NULL) {
		LM_DBG("Could not parse CCA message response.\n");
		counter_inc(ims_charging_cnts_h.failed_final_ccrs);
		return;
	}

	if(ro_cca_data->resultcode != 2001) {
		LM_ERR("Got bad CCA result code for STOP record - [%d]\n",
				ro_cca_data->resultcode);
		goto error;
	} else {
		LM_DBG("Valid CCA response for STOP record\n");
	}

	counter_inc(ims_charging_cnts_h.successful_final_ccrs);
	Ro_free_CCA(ro_cca_data);
	if(!is_timeout && cca) {
		cdpb.AAAFreeMessage(&cca);
	}
	return;

error:
	counter_inc(ims_charging_cnts_h.failed_final_ccrs);
	Ro_free_CCA(ro_cca_data);
	if(!is_timeout && cca) {
		cdpb.AAAFreeMessage(&cca);
	}
}

/**
 * Send a CCR to the OCS based on the SIP message (INVITE ONLY)
 * @param msg - SIP message
 * @param direction - orig|term
 * @param reservation_units - units to try to reserve
 * @param reservation_units - config route to call when receiving a CCA
 * @param tindex - transaction index
 * @param tindex - transaction label
 *
 * @returns #CSCF_RETURN_BREAK if OK, #CSCF_RETURN_ERROR on error
 */
int Ro_Send_CCR(struct sip_msg *msg, struct dlg_cell *dlg, int dir,
		int reservation_units, str *incoming_trunk_id, str *outgoing_trunk_id,
		str *pani, void *action, unsigned int tindex, unsigned int tlabel)
{
	str session_id = {0, 0}, called_asserted_identity = {0, 0},
		subscription_id = {0, 0}, asserted_identity = {0, 0},
		app_provided_party = {0, 0};
	int subscription_id_type = AVP_EPC_Subscription_Id_Type_End_User_SIP_URI;
	AAASession *cc_acc_session = NULL;
	Ro_CCR_t *ro_ccr_data = 0;
	AAAMessage *ccr = 0;
	struct ro_session *new_session = 0;
	struct session_setup_data *ssd;
	int ret = 0;
	struct hdr_field *h = 0;
	char *p;

	int cc_event_number = 0; //According to IOT tests this should start at 0
	int cc_event_type = RO_CC_START;
	int free_called_asserted_identity = 0;

	sdp_session_cell_t *msg_sdp_session;
	sdp_stream_cell_t *msg_sdp_stream;

	int active_service_identifier;
	int active_rating_group;

	int sdp_stream_num = 0;

	LM_DBG("Sending initial CCR request (%c) for reservation_units [%d] "
		   "incoming_trunk_id [%.*s] outgoing_trunk_id [%.*s]\n",
			dir == RO_ORIG_DIRECTION ? 'O' : 'T', reservation_units,
			incoming_trunk_id->len, incoming_trunk_id->s,
			outgoing_trunk_id->len, outgoing_trunk_id->s);

	ssd = shm_malloc(sizeof(struct
			session_setup_data)); // lookup structure used to load session info from cdp callback on CCA
	if(!ssd) {
		LM_ERR("no more shm mem\n");
		goto error;
	}

	//getting asserted identity
	if(get_custom_user(msg, &asserted_identity) == -1) {
		if((asserted_identity = cscf_get_asserted_identity(msg, 0)).len == 0) {
			LM_DBG("No P-Asserted-Identity hdr found. Using From hdr for "
				   "asserted_identity\n");
			asserted_identity = dlg->from_uri;
			if(asserted_identity.len > 0 && asserted_identity.s) {
				p = (char *)memchr(
						asserted_identity.s, ';', asserted_identity.len);
				if(p)
					asserted_identity.len = (p - asserted_identity.s);
			}
		}
	}

	//getting called asserted identity
	if((called_asserted_identity =
					   cscf_get_public_identity_from_called_party_id(msg, &h))
					.len
			== 0) {
		LM_DBG("No P-Called-Identity hdr found. Using request URI for "
			   "called_asserted_identity\n");
		called_asserted_identity = cscf_get_public_identity_from_requri(msg);
		free_called_asserted_identity = 1;
	}

	if(dir == RO_ORIG_DIRECTION) {
		subscription_id.s = asserted_identity.s;
		subscription_id.len = asserted_identity.len;

	} else if(dir == RO_TERM_DIRECTION) {
		subscription_id.s = called_asserted_identity.s;
		subscription_id.len = called_asserted_identity.len;
	} else {
		LM_CRIT("don't know what to do in unknown mode - should we even get "
				"here\n");
		goto error;
	}

	format_subscription_id(&subscription_id, &subscription_id_type);

	str mac = {0, 0};
	if(get_mac_avp_value(msg, &mac) != 0)
		LM_DBG(RO_MAC_AVP_NAME " was not set. Using default.\n");

	//by default we use voice service id and rate group
	//then we check SDP - if we find video then we use video service id and rate group
	LM_DBG("Setting default SID to %d and RG to %d for voice\n",
			voice_service_identifier, voice_rating_group);
	active_service_identifier = voice_service_identifier;
	active_rating_group = voice_rating_group;

	//check SDP - if there is video then set default to video, if not set it to audio
	if(parse_sdp(msg) < 0) {
		LM_ERR("Unable to parse req SDP\n");
		goto error;
	}

	msg_sdp_session = get_sdp_session(msg, 0);
	if(!msg_sdp_session) {
		LM_ERR("Missing SDP session information from rpl\n");
	} else {
		for(;;) {
			msg_sdp_stream = get_sdp_stream(msg, 0, sdp_stream_num);
			if(!msg_sdp_stream) {
				//LM_ERR("Missing SDP stream information\n");
				break;
			}

			int intportA = atoi(msg_sdp_stream->port.s);
			if(intportA != 0
					&& strncasecmp(msg_sdp_stream->media.s, "video", 5) == 0) {
				LM_DBG("This SDP has a video component and src ports not equal "
					   "to 0 - so we set default SID to %d and RG to %d for "
					   "video\n",
						video_service_identifier, video_rating_group);
				active_service_identifier = video_service_identifier;
				active_rating_group = video_rating_group;
				break;
			}

			sdp_stream_num++;
		}
	}

	free_sdp((sdp_info_t **)(void *)&msg->body);

	if(get_app_provided_party(msg, &app_provided_party) == -1) {
		LM_DBG("no valid Application-Provided-Called-Party-Address AVP "
			   "provided\n");
	}

	//create a session object without auth and diameter session id - we will add this later.
	new_session = build_new_ro_session(dir, 0, 0, &session_id, &dlg->callid,
			&asserted_identity, &called_asserted_identity, &mac, dlg->h_entry,
			dlg->h_id, reservation_units, 0, active_rating_group,
			active_service_identifier, incoming_trunk_id, outgoing_trunk_id,
			pani, &app_provided_party, ro_timer_buffer);

	if(!new_session) {
		LM_ERR("Couldn't create new Ro Session - this is BAD!\n");
		goto error;
	}
	LM_DBG("new session created\n");

	if(sr_kemi_eng_get()) {
		str *tmp = shm_malloc(sizeof(str));
		if(tmp == NULL) {
			SHM_MEM_ERROR;
			goto error;
		}
		if(shm_str_dup(tmp, (str *)action) != 0) {
			SHM_MEM_ERROR;
			shm_free(tmp);
			goto error;
		} else {
			ssd->action = tmp;
		}
	} else {
		ssd->action = action;
	}

	ssd->tindex = tindex;
	ssd->tlabel = tlabel;
	ssd->ro_session = new_session;

	if(!sip_create_ro_ccr_data(msg, dir, &ro_ccr_data, &cc_acc_session,
			   asserted_identity, called_asserted_identity, subscription_id,
			   subscription_id_type, incoming_trunk_id, outgoing_trunk_id, pani,
			   &app_provided_party))
		goto error;

	if(!ro_ccr_data)
		goto error;

	if(!cc_acc_session)
		goto error;

	if(!(ccr = Ro_new_ccr(cc_acc_session, ro_ccr_data)))
		goto error;

	if(!Ro_add_vendor_specific_appid(ccr, IMS_vendor_id_3GPP, IMS_Ro, 0)) {
		LM_ERR("Problem adding Vendor specific ID\n");
		goto error;
	}

	if(!Ro_add_cc_request(ccr, cc_event_type, cc_event_number)) {
		LM_ERR("Problem adding CC-Request data\n");
		goto error;
	}

	if(!Ro_add_event_timestamp(ccr, time(NULL))) {
		LM_ERR("Problem adding Event-Timestamp data\n");
		goto error;
	}

	if(!Ro_add_user_equipment_info(
			   ccr, AVP_EPC_User_Equipment_Info_Type_MAC, mac)) {
		LM_ERR("Problem adding User-Equipment data\n");
		goto error;
	}

	if(!Ro_add_subscription_id(ccr, subscription_id_type, &subscription_id)) {
		LM_ERR("Problem adding Subscription ID data\n");
		goto error;
	}
	if(!Ro_add_multiple_service_credit_Control(ccr, reservation_units, -1,
			   active_rating_group, active_service_identifier)) {
		LM_ERR("Problem adding Multiple Service Credit Control data\n");
		goto error;
	}

	/* before we send, update our session object with CC App session ID and data */
	new_session->auth_appid = cc_acc_session->application_id;
	new_session->auth_session_type = cc_acc_session->type;
	new_session->ro_session_id.s = (char *)shm_malloc(cc_acc_session->id.len);
	if(!new_session->ro_session_id.s) {
		LM_ERR("no more shm mem\n");
		goto error;
	}

	new_session->ro_session_id.len = cc_acc_session->id.len;
	memcpy(new_session->ro_session_id.s, cc_acc_session->id.s,
			cc_acc_session->id.len);

	LM_DBG("new CC Ro Session ID: [%.*s] stored in shared memory address "
		   "[%p]\n",
			cc_acc_session->id.len, cc_acc_session->id.s, new_session);

	LM_DBG("Sending CCR Diameter message.\n");
	//    new_session->ccr_sent = 1;      //assume we will send successfully
	cdpb.AAASessionsUnlock(cc_acc_session->hash);

	if(ro_forced_peer.len > 0) {
		LM_DBG("Sending message with Peer\n");
		ret = cdpb.AAASendMessageToPeer(
				ccr, &ro_forced_peer, resume_on_initial_ccr, (void *)ssd);
	} else {
		LM_DBG("Sending message without Peer and realm is [%.*s]\n",
				ccr->dest_realm->data.len, ccr->dest_realm->data.s);
		ret = cdpb.AAASendMessage(ccr, resume_on_initial_ccr, (void *)ssd);
	}

	if(ret != 1) {
		LM_ERR("Failed to send Diameter CCR\n");
		//        new_session->ccr_sent = 0;
		goto error;
	}

	Ro_free_CCR(ro_ccr_data);

	LM_DBG("Registering for callbacks on Dialog [%p] and charging session "
		   "[%p]\n",
			dlg, new_session);

	//TODO: if the following fail, we should clean up the Ro session.......
	if(dlgb.register_dlgcb(dlg,
			   DLGCB_TERMINATED | DLGCB_FAILED | DLGCB_EXPIRED
					   | DLGCB_CONFIRMED,
			   dlg_callback_received, (void *)new_session, NULL)
			!= 0) {
		LM_CRIT("cannot register callback for dialog confirmation\n");
		goto error;
	}

	counter_inc(ims_charging_cnts_h.initial_ccrs);
	counter_inc(ims_charging_cnts_h.active_ro_sessions);

	if(free_called_asserted_identity)
		shm_free(
				called_asserted_identity
						.s); // shm_malloc in cscf_get_public_identity_from_requri
	return RO_RETURN_BREAK;

error:
	LM_DBG("Trying to reserve credit on initial INVITE failed.\n");

	if(free_called_asserted_identity)
		shm_free(
				called_asserted_identity
						.s); // shm_malloc in cscf_get_public_identity_from_requri
	Ro_free_CCR(ro_ccr_data);
	if(cc_acc_session) {
		cdpb.AAASessionsUnlock(cc_acc_session->hash);
		cdpb.AAADropSession(cc_acc_session);
	}

	if(ssd)
		shm_free(ssd);

	return RO_RETURN_ERROR;
}

static void resume_on_initial_ccr(
		int is_timeout, void *param, AAAMessage *cca, long elapsed_msecs)
{
	Ro_CCA_t *ro_cca_data = NULL;
	struct cell *t = NULL;
	struct session_setup_data *ssd = (struct session_setup_data *)param;
	int error_code = RO_RETURN_ERROR;
	str *redirecturi = 0;
	int fui_action = 0;

	if(is_timeout) {
		counter_inc(ims_charging_cnts_h.ccr_timeouts);
		LM_ERR("Transaction timeout - did not get CCA\n");
		error_code = RO_RETURN_ERROR;
		goto error0;
	}

	counter_inc(ims_charging_cnts_h.ccr_replies_received);
	counter_add(ims_charging_cnts_h.ccr_response_time, elapsed_msecs);

	if(!cca) {
		LM_ERR("Error reserving credit for CCA.\n");
		error_code = RO_RETURN_ERROR;
		goto error0;
	}

	if(!ssd) {
		LM_ERR("Session lookup data is NULL.\n");
		error_code = RO_RETURN_ERROR;
		goto error0;
	}

	// we make sure the transaction exists
	if(tmb.t_lookup_ident(&t, ssd->tindex, ssd->tlabel) < 0) {
		LM_ERR("t_continue: transaction not found\n");
		error_code = RO_RETURN_ERROR;
		goto error0;
	}

	// we bring the list of AVPs of the transaction to the current context
	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);

	ro_cca_data = Ro_parse_CCA_avps(cca);

	if(!ro_cca_data) {
		LM_ERR("Could not parse CCA message response.\n");
		error_code = RO_RETURN_ERROR;
		create_cca_result_code(0);
		goto error0;
	}

	LM_DBG("Ro result code is [%d]\n", (int)ro_cca_data->resultcode);
	create_cca_result_code((int)ro_cca_data->resultcode);
	if(ro_cca_data->resultcode != 2001) {
		if(ro_cca_data->resultcode != 4012)
			LM_ERR("Got bad CCA result code [%d] - reservation failed\n",
					(int)ro_cca_data->resultcode);
		error_code = RO_RETURN_FALSE;
		goto error1;
	}

	if(ro_cca_data->mscc->final_unit_action) {
		fui_action = ro_cca_data->mscc->final_unit_action->action;

		if(fui_action == AVP_Final_Unit_Action_Redirect) {
			if(ro_cca_data->mscc->final_unit_action->redirect_server) {
				LM_DBG("FUI with action: [%d]\n",
						ro_cca_data->mscc->final_unit_action->action);

				if(ro_cca_data->mscc->final_unit_action->action
						== AVP_Final_Unit_Action_Redirect) {
					LM_DBG("Have REDIRECT action with address type of [%d]\n",
							ro_cca_data->mscc->final_unit_action
									->redirect_server->address_type);
					if(ro_cca_data->mscc->final_unit_action->redirect_server
									->address_type
							== AVP_Redirect_Address_Type_SIP_URI) {
						LM_DBG("SIP URI for redirect is [%.*s] with len of "
							   "%d\n",
								ro_cca_data->mscc->final_unit_action
										->redirect_server->server_address->len,
								ro_cca_data->mscc->final_unit_action
										->redirect_server->server_address->s,
								ro_cca_data->mscc->final_unit_action
										->redirect_server->server_address->len);
						redirecturi = ro_cca_data->mscc->final_unit_action
											  ->redirect_server->server_address;
					} else {
						LM_DBG("we don't cater for any redirect action which "
							   "is not a SIP URI... ignoring [%d]\n",
								ro_cca_data->mscc->final_unit_action
										->redirect_server->address_type);
					}
				} else {
					LM_DBG("ignoring final unit action which is not REDIRECT - "
						   "[%d]\n",
							fui_action);
				}
			}
		}
	}

	/* create the AVPs cca_redirect_uri and cca_fui_action  for export to cfg file */
	create_cca_fui_avps(fui_action, redirecturi);

	/* check result code at mscc level */
	if(ro_cca_data->mscc->resultcode && ro_cca_data->mscc->resultcode != 2001) {
		LM_DBG("CCA failure at MSCC level with resultcode [%d]\n",
				ro_cca_data->mscc->resultcode);
		error_code = RO_RETURN_FALSE;
		goto error1;
	}

	LM_DBG("Valid CCA response with time chunk of [%i] and validity [%i]\n",
			ro_cca_data->mscc->granted_service_unit->cc_time,
			ro_cca_data->mscc->validity_time);

	if(ro_cca_data->mscc->granted_service_unit->cc_time <= 0) {
		LM_DBG("got zero GSU.... reservation failed\n");
		error_code = RO_RETURN_FALSE;
		goto error1;
	}

	ssd->ro_session->last_event_timestamp = get_current_time_micro();
	ssd->ro_session->event_type = pending;
	ssd->ro_session->reserved_secs =
			ro_cca_data->mscc->granted_service_unit->cc_time;
	ssd->ro_session->valid_for = ro_cca_data->mscc->validity_time;
	ssd->ro_session->is_final_allocation = 0;
	if(ro_cca_data->mscc->time_quota_threshold > 0) {
		LM_DBG("setting session ro_timer_buffer to %i from "
			   "time_quota_threshold in server response\n",
				ro_cca_data->mscc->time_quota_threshold);
		ssd->ro_session->ro_timer_buffer =
				ro_cca_data->mscc->time_quota_threshold;
	}

	if(ro_cca_data->mscc->final_unit_action
			&& (ro_cca_data->mscc->final_unit_action->action == 0))
		ssd->ro_session->is_final_allocation = 1;

	if(ro_cca_data->origin_host.s && ro_cca_data->origin_host.len > 0) {
		ssd->ro_session->origin_host.s =
				(char *)shm_malloc(ro_cca_data->origin_host.len);
		if(!ssd->ro_session->origin_host.s) {
			LM_ERR("no more shm mem\n");
			goto error0;
		}

		ssd->ro_session->origin_host.len = ro_cca_data->origin_host.len;
		memcpy(ssd->ro_session->origin_host.s, ro_cca_data->origin_host.s,
				ro_cca_data->origin_host.len);
	}

	Ro_free_CCA(ro_cca_data);
	ro_cca_data = NULL;

	LM_DBG("Freeing CCA message\n");
	cdpb.AAAFreeMessage(&cca);

	link_ro_session(ssd->ro_session, 0);

	ssd->ro_session->flags |= RO_SESSION_FLAG_NEW;
	if(ro_db_mode == DB_MODE_REALTIME) {
		if(update_ro_dbinfo(ssd->ro_session) != 0) {
			LM_ERR("Failed to update ro_session in database... continuing\n");
		};
	}

	unref_ro_session(ssd->ro_session, 1, 1); /* release our reference */

	create_cca_return_code(RO_RETURN_TRUE);

	if(t)
		tmb.unref_cell(t);

	if(sr_kemi_eng_get()) {
		str cb_param = str_init("ccr:continue");
		tmb.t_continue_cb(ssd->tindex, ssd->tlabel, ssd->action, &cb_param);
		str_free(*((str *)ssd->action), shm);
		shm_free(ssd->action);
	} else {
		tmb.t_continue(ssd->tindex, ssd->tlabel, ssd->action);
	}

	shm_free(ssd);

	counter_inc(ims_charging_cnts_h.successful_initial_ccrs);

	return;

error1:
error0:
	if(ro_cca_data != NULL) {
		Ro_free_CCA(ro_cca_data);
	}

	LM_DBG("Trying to reserve credit on initial INVITE failed on cdp "
		   "callback\n");
	//    counter_add(ims_charging_cnts_h.active_ro_sessions, -1); /*we bumped active on the original initial ccr sent */
	counter_inc(
			ims_charging_cnts_h
					.failed_initial_ccrs); /* drop by one as theoretically this is failed initial ccr */
	create_cca_return_code(error_code);

	if(!is_timeout && cca) {
		cdpb.AAAFreeMessage(&cca);
	}

	if(t)
		tmb.unref_cell(t);

	tmb.t_continue(ssd->tindex, ssd->tlabel, ssd->action);
	shm_free(ssd);
}

void remove_aaa_session(str *session_id)
{
	AAASession *session;

	if((session = cdpb.AAAGetCCAccSession(*session_id))) {
		LM_DBG("Found AAA CC App Auth session to delete.\n");
		cdpb.AAASessionsUnlock(session->hash);
		cdpb.AAADropCCAccSession(session);
	}
}

int get_direction_as_int(str *direction)
{
	char *p = direction->s;

	if(direction->len > 0 && p) {
		if(p[0] == 'O' || p[0] == 'o') {
			return RO_ORIG_DIRECTION;
		} else if(p[0] == 'T' || p[0] == 't') {
			return RO_TERM_DIRECTION;
		}
	}
	return RO_UNKNOWN_DIRECTION;
}

static int create_cca_return_code(int result)
{
	int rc;
	int_str avp_val, avp_name;
	avp_name.s.s = RO_AVP_CCA_RETURN_CODE;
	avp_name.s.len = RO_AVP_CCA_RETURN_CODE_LENGTH;

	avp_val.n = result;
	avp_val.s.s = RO_RETURN_TRUE_STR; //assume true
	avp_val.s.len = 1;

	switch(result) {
		case RO_RETURN_FALSE:
			avp_val.s.s = RO_RETURN_FALSE_STR;
			break;
		case RO_RETURN_ERROR:
			avp_val.s.s = RO_RETURN_ERROR_STR;
			break;
		default:
			if(result >= 0)
				break;

			LM_ERR("Unknown result code: %d\n", result);
			avp_val.s.s = "??";
	}

	if(result < 0)
		avp_val.s.len = 2;

	rc = add_avp(AVP_NAME_STR | AVP_VAL_STR, avp_name, avp_val);

	if(rc < 0)
		LM_ERR("Couldn't create [" RO_AVP_CCA_RETURN_CODE "] AVP\n");
	else
		LM_DBG("Created AVP [" RO_AVP_CCA_RETURN_CODE
			   "] successfully: value=[%d]\n",
				result);

	return 1;
}

static int create_cca_result_code(int result)
{
	int rc;
	int_str avp_val, avp_name;
	avp_name.s.s = RO_AVP_CCA_RESULT_CODE;
	avp_name.s.len = RO_AVP_CCA_RESULT_CODE_LENGTH;
	char buf[10];

	avp_val.n = result;
	avp_val.s.len = snprintf(buf, 10, "%i", result);
	avp_val.s.s = buf;

	rc = add_avp(AVP_NAME_STR | AVP_VAL_STR, avp_name, avp_val);

	if(rc < 0)
		LM_ERR("Couldn't create [" RO_AVP_CCA_RESULT_CODE "] AVP\n");
	else
		LM_DBG("Created AVP [" RO_AVP_CCA_RESULT_CODE
			   "] successfully: value=[%d]\n",
				result);

	return 1;
}

static int create_cca_fui_avps(int action, str *redirecturi)
{
	int_str action_avp_val, action_avp_name, redirecturi_avp_val,
			redirecturi_avp_name;
	action_avp_name.s.s = RO_AVP_CCA_FUI_ACTION;
	action_avp_name.s.len = RO_AVP_CCA_FUI_ACTION_LENGTH;
	redirecturi_avp_name.s.s = RO_AVP_CCA_FUI_REDIRECT_URI;
	redirecturi_avp_name.s.len = RO_AVP_CCA_FUI_REDIRECT_URI_LENGTH;
	char buf[10];
	int rc;

	action_avp_val.n = action;
	action_avp_val.s.len = snprintf(buf, 10, "%i", action);
	action_avp_val.s.s = buf;

	rc = add_avp(AVP_NAME_STR | AVP_VAL_STR, action_avp_name, action_avp_val);

	if(rc < 0)
		LM_ERR("Couldn't create [" RO_AVP_CCA_FUI_ACTION "] AVP\n");
	else
		LM_DBG("Created AVP [" RO_AVP_CCA_FUI_ACTION
			   "] successfully: value=[%d]\n",
				action);

	if(redirecturi && redirecturi->len > 0 && redirecturi->s) {
		redirecturi_avp_val.s.len = redirecturi->len;
		redirecturi_avp_val.s.s = redirecturi->s;

		rc = add_avp(AVP_NAME_STR | AVP_VAL_STR, redirecturi_avp_name,
				redirecturi_avp_val);

		if(rc < 0)
			LM_ERR("Couldn't create [" RO_AVP_CCA_FUI_REDIRECT_URI "] AVP\n");
		else
			LM_DBG("Created AVP [" RO_AVP_CCA_FUI_REDIRECT_URI
				   "] successfully: value=[%.*s]\n",
					redirecturi->len, redirecturi->s);
	}

	return 1;
}

static int get_mac_avp_value(struct sip_msg *msg, str *value)
{
	str mac_avp_name_str = str_init(RO_MAC_AVP_NAME);
	pv_spec_t avp_spec;
	pv_value_t val;

	pv_parse_spec2(&mac_avp_name_str, &avp_spec, 1);
	if(pv_get_spec_value(msg, &avp_spec, &val) != 0 || val.rs.len == 0) {
		value->s = "00-00-00-00-00-00";
		value->len = strlen(value->s);
		return -1;
	}

	*value = val.rs;
	return 0;
}

/*!
 * \brief Process re-auth request
 * \param request Diameter request
 * \return diameter response
 */
AAAMessage *ro_process_rar(AAAMessage *request)
{
	AAAMessage *response;
	struct ro_session *ro_session = NULL;
	struct ro_session_entry *ro_session_entry;
	unsigned int h_entry;
	int unref = 0;
	int result_code = DIAMETER_LIMITED_SUCCESS;
	char x[4];

	if(request->sessionId && request->sessionId->data.s) {
		LM_INFO("Received an IMS_RAR for session id %.*s\n",
				request->sessionId->data.len, request->sessionId->data.s);

		ro_session = lookup_ro_session_by_session_id(&request->sessionId->data);
		if(ro_session == NULL) {
			LM_WARN("no active ro_session with id %.*s - ignoring\n",
					request->sessionId->data.len, request->sessionId->data.s);
			result_code = DIAMETER_UNKNOWN_SESSION_ID;
			goto end;
		}

		h_entry = ro_session->h_entry;
		ro_session_entry = &(ro_session_table->entries[h_entry]);

		ro_session_lock(ro_session_table, ro_session_entry);

		/*we might receive a re-auth request with either timer active or inactive.
		first remove any existing. if success, decrement ref count.*/
		if(remove_ro_timer(&ro_session->ro_tl) == 0) {
			unref++;
		}
		if(insert_ro_timer(&ro_session->ro_tl, 1) == 0) {
			/*increment ref count on timer create success */
			ref_ro_session(ro_session, 1, 0);
		}

		/*finally, unref session returned by lookup */
		unref_ro_session(ro_session, unref + 1, 0);
		ro_session_unlock(ro_session_table, ro_session_entry);
	} else {
		LM_WARN("Received an IMS_RAR without session id\n");
		result_code = DIAMETER_UNABLE_TO_COMPLY;
	}
end:
	response = cdpb.AAACreateResponse(request);
	if(!response)
		return 0;
	set_4bytes(x, result_code);
	Ro_add_avp(response, x, 4, AVP_Result_Code, AAA_AVP_FLAG_MANDATORY, 0,
			AVP_DUPLICATE_DATA, __FUNCTION__);
	return response;
}

/*!
 * \brief Process session-abort request
 * \param request Diameter request
 * \return diameter response
 */
AAAMessage *ro_process_asr(AAAMessage *request)
{
	AAAMessage *response;
	struct ro_session *ro_session = NULL;
	int result_code = DIAMETER_LIMITED_SUCCESS;
	char x[4];

	if(request->sessionId && request->sessionId->data.s) {
		LM_INFO("Received an IMS_ASR for session id %.*s\n",
				request->sessionId->data.len, request->sessionId->data.s);

		ro_session = lookup_ro_session_by_session_id(&request->sessionId->data);
		if(ro_session == NULL) {
			LM_WARN("no active ro_session with id %.*s - ignoring\n",
					request->sessionId->data.len, request->sessionId->data.s);
			result_code = DIAMETER_UNKNOWN_SESSION_ID;
			goto end;
		}

		if(dlgb.lookup_terminate_dlg(
				   ro_session->dlg_h_entry, ro_session->dlg_h_id, NULL)
				< 0) {
			result_code = DIAMETER_UNABLE_TO_COMPLY;
		}

		unref_ro_session(ro_session, 1, 0);
	} else {
		LM_WARN("Received an IMS_ASR without session id\n");
		result_code = DIAMETER_UNABLE_TO_COMPLY;
	}
end:
	response = cdpb.AAACreateResponse(request);
	if(!response)
		return 0;
	set_4bytes(x, result_code);
	Ro_add_avp(response, x, 4, AVP_Result_Code, AAA_AVP_FLAG_MANDATORY, 0,
			AVP_DUPLICATE_DATA, __FUNCTION__);

	return response;
}
