소스 검색

presence: 4.4 improvements

adds presence:notify-reply event
implements $notify_reply for handling in presence:notify-reply event
adds flags, user_agent columns to active_watchers
extends $subs to other subscription properties
lazedo 9 년 전
부모
커밋
a308821893
6개의 변경된 파일408개의 추가작업 그리고 73개의 파일을 삭제
  1. 6 0
      modules/presence/hash.c
  2. 127 30
      modules/presence/notify.c
  3. 6 0
      modules/presence/notify.h
  4. 16 2
      modules/presence/presence.c
  5. 251 41
      modules/presence/subscribe.c
  6. 2 0
      modules/presence/subscribe.h

+ 6 - 0
modules/presence/hash.c

@@ -138,6 +138,7 @@ subs_t* mem_copy_subs(subs_t* s, int mem_type)
 		+ s->to_tag.len+ s->from_tag.len+s->sockinfo_str.len+s->event_id.len
 		+ s->local_contact.len+ s->contact.len+ s->record_route.len
 		+ s->reason.len+ s->watcher_user.len+ s->watcher_domain.len
+		+ s->user_agent.len
 		+ 1)*sizeof(char);
 
 	if(mem_type & PKG_MEM_TYPE)
@@ -166,6 +167,7 @@ subs_t* mem_copy_subs(subs_t* s, int mem_type)
 	CONT_COPY(dest, dest->local_contact, s->local_contact)
 	CONT_COPY(dest, dest->contact, s->contact)
 	CONT_COPY(dest, dest->record_route, s->record_route)
+	CONT_COPY(dest, dest->user_agent, s->user_agent)
 	if(s->event_id.s)
 		CONT_COPY(dest, dest->event_id, s->event_id)
 	if(s->reason.s)
@@ -179,6 +181,7 @@ subs_t* mem_copy_subs(subs_t* s, int mem_type)
 	dest->send_on_cback= s->send_on_cback;
 	dest->expires= s->expires;
 	dest->db_flag= s->db_flag;
+	dest->flags= s->flags;
 
 	return dest;
 
@@ -204,6 +207,7 @@ subs_t* mem_copy_subs_noc(subs_t* s)
 		+ s->to_tag.len+ s->from_tag.len+s->sockinfo_str.len+s->event_id.len
 		+ s->local_contact.len + s->record_route.len+
 		+ s->reason.len+ s->watcher_user.len+ s->watcher_domain.len
+		+ s->user_agent.len
 		+ 1)*sizeof(char);
 
 	dest= (subs_t*)shm_malloc(size);
@@ -227,6 +231,7 @@ subs_t* mem_copy_subs_noc(subs_t* s)
 	CONT_COPY(dest, dest->sockinfo_str, s->sockinfo_str)
 	CONT_COPY(dest, dest->local_contact, s->local_contact)
 	CONT_COPY(dest, dest->record_route, s->record_route)
+	CONT_COPY(dest, dest->user_agent, s->user_agent)
 	if(s->event_id.s)
 		CONT_COPY(dest, dest->event_id, s->event_id)
 	if(s->reason.s)
@@ -240,6 +245,7 @@ subs_t* mem_copy_subs_noc(subs_t* s)
 	dest->send_on_cback= s->send_on_cback;
 	dest->expires= s->expires;
 	dest->db_flag= s->db_flag;
+	dest->flags= s->flags;
 
 	dest->contact.s= (char*)shm_malloc(s->contact.len* sizeof(char));
 	if(dest->contact.s== NULL)

+ 127 - 30
modules/presence/notify.c

@@ -46,10 +46,13 @@
 #include "presence.h"
 #include "notify.h"
 #include "utils_func.h"
+#include "../../receive.h"
 
 #define ALLOC_SIZE 3000
 #define MAX_FORWARD 70
 
+int goto_on_notify_reply=-1;
+
 extern int pres_local_log_level;
 
 c_back_param* shm_dup_cbparam(subs_t*);
@@ -95,6 +98,8 @@ str str_sender_col = str_init("sender");
 str str_updated_col = str_init("updated");
 str str_updated_winfo_col = str_init("updated_winfo");
 str str_priority_col = str_init("priority");
+str str_flags_col = str_init("flags");
+str str_user_agent_col = str_init("user_agent");
 
 int subset=0;
 
@@ -984,7 +989,7 @@ int get_subs_db(str* pres_uri, pres_ev_t* event, str* sender,
 	db_key_t query_cols[7];
 	db_op_t  query_ops[7];
 	db_val_t query_vals[7];
-	db_key_t result_cols[19];
+	db_key_t result_cols[21];
 	int n_result_cols = 0, n_query_cols = 0;
 	db_row_t *row ;	
 	db_val_t *row_vals ;
@@ -995,6 +1000,7 @@ int get_subs_db(str* pres_uri, pres_ev_t* event, str* sender,
 	int version_col= 0, record_route_col = 0, contact_col = 0;
 	int sockinfo_col= 0, local_contact_col= 0, event_id_col = 0;
 	int watcher_user_col= 0, watcher_domain_col= 0;
+	int flags_col= 0, user_agent_col= 0;
 	subs_t s, *s_new;
 	int inc= 0;
 		
@@ -1058,6 +1064,8 @@ int get_subs_db(str* pres_uri, pres_ev_t* event, str* sender,
 	result_cols[sockinfo_col=n_result_cols++]     =   &str_socket_info_col;
 	result_cols[local_contact_col=n_result_cols++]=   &str_local_contact_col;
 	result_cols[version_col=n_result_cols++]      =   &str_version_col;
+	result_cols[flags_col=n_result_cols++]        =   &str_flags_col;
+	result_cols[user_agent_col=n_result_cols++]   =   &str_user_agent_col;
 
 	if (pa_dbf.query(pa_db, query_cols, query_ops, query_vals,result_cols,
 				n_query_cols, n_result_cols, 0, &result) < 0) 
@@ -1148,6 +1156,9 @@ int get_subs_db(str* pres_uri, pres_ev_t* event, str* sender,
 		else
 		    s.expires = row_vals[expires_col].val.int_val - (int)time(NULL);
 		s.version = row_vals[version_col].val.int_val +1;
+		s.flags = row_vals[flags_col].val.int_val;
+		s.user_agent.s=  (char*)row_vals[user_agent_col].val.string_val;
+		s.user_agent.len= (s.user_agent.s)?strlen(s.user_agent.s):0;
 
 		s_new= mem_copy_subs(&s, PKG_MEM_TYPE);
 		if(s_new== NULL)
@@ -1467,7 +1478,7 @@ int send_notify_request(subs_t* subs, subs_t * watcher_subs,
 	str str_hdr = {0, 0};
 	str* notify_body = NULL;
 	int result= 0;
-	c_back_param *cb_param= NULL;
+	subs_t *cb_param= NULL;
 	str* final_body= NULL;
 	uac_req_t uac_r;
 	str* aux_body = NULL;
@@ -1589,16 +1600,7 @@ jump_over_body:
 	}
 
 	LM_DBG("expires %d status %d\n", subs->expires, subs->status);
-	/* if status is TERMINATED_STATUS, the subscription will be deleted so no need to send a parameter */
-	if(subs->status != TERMINATED_STATUS)
-	{
-		cb_param = shm_dup_cbparam(subs);
-		if(cb_param == NULL)
-		{
-			LM_ERR("while duplicating cb_param in share memory\n");
-			goto error;
-		}
-	}
+	cb_param = mem_copy_subs(subs, SHM_MEM_TYPE);
 
 	set_uac_req(&uac_r, &met, &str_hdr, notify_body, td, TMCB_LOCAL_COMPLETED,
 			p_tm_callback, (void*)cb_param);
@@ -1607,7 +1609,7 @@ jump_over_body:
 	{
 		LM_ERR("in function tmb.t_request_within\n");
 		if(cb_param)
-			free_cbparam(cb_param);
+			shm_free(cb_param);
 		goto error;
 	}
 
@@ -1700,30 +1702,120 @@ int notify(subs_t* subs, subs_t * watcher_subs,str* n_body,int force_null_body)
 	return 0;
 }
 
-void p_tm_callback( struct cell *t, int type, struct tmcb_params *ps)
+extern subs_t* _pres_subs_last_sub;
+sip_msg_t* _pres_subs_notify_reply_msg = NULL;
+int _pres_subs_notify_reply_code = 0;
+
+int pv_parse_notify_reply_var_name(pv_spec_p sp, str *in)
+{
+	pv_spec_t *pv=NULL;
+	if(in->s==NULL || in->len<=0)
+		return -1;
+	pv = (pv_spec_t*)pkg_malloc(sizeof(pv_spec_t));
+	if(pv==NULL)
+		return -1;
+	memset(pv, 0, sizeof(pv_spec_t));
+	if(pv_parse_spec(in, pv)==NULL)
+		goto error;
+	sp->pvp.pvn.u.dname = (void*)pv;
+	sp->pvp.pvn.type = PV_NAME_PVAR;
+	return 0;
+
+error:
+	LM_ERR("invalid pv name [%.*s]\n", in->len, in->s);
+	if(pv!=NULL)
+		pkg_free(pv);
+	return -1;
+}
+
+int pv_get_notify_reply(struct sip_msg *msg,  pv_param_t *param, pv_value_t *res)
 {
-	c_back_param*  cb;
+	pv_spec_t *pv=NULL;
 
-	if(ps->param==NULL || *ps->param==NULL ||
-			((c_back_param*)(*ps->param))->callid.s == NULL ||
-			((c_back_param*)(*ps->param))->to_tag.s== NULL ||
-			((c_back_param*)(*ps->param))->from_tag.s== NULL)
-	{
-		LM_DBG("message id not received, probably a timeout notify\n");
-		if(ps->param != NULL && *ps->param !=NULL)
-			free_cbparam((c_back_param*)(*ps->param));
+	if(msg==NULL)
+		return 1;
+
+	pv = (pv_spec_t*)param->pvn.u.dname;
+	if(pv==NULL)
+		return pv_get_null(msg, param, res);
+
+	return pv_get_spec_value(_pres_subs_notify_reply_msg, pv, res);
+}
+
+#define FAKED_SIP_408_MSG_FORMAT "SIP/2.0 408 TIMEOUT\r\nVia: SIP/2.0/UDP 127.0.0.1\r\nFrom: invalid;\r\nTo: invalid\r\nCall-ID: invalid\r\nCSeq: 1 TIMEOUT\r\nContent-Length: 0\r\n\r\n"
+static sip_msg_t* _faked_msg = NULL;
+
+sip_msg_t* faked_msg() {
+	if(_faked_msg == NULL) {
+		_faked_msg = pkg_malloc(sizeof(sip_msg_t));
+		if(likely(build_sip_msg_from_buf(_faked_msg, FAKED_SIP_408_MSG_FORMAT, strlen(FAKED_SIP_408_MSG_FORMAT), inc_msg_no())<0)) {
+			LM_ERR("failed to parse msg buffer\n");
+			return NULL;
+		}
+	}
+	return _faked_msg;
+}
+
+void run_notify_reply_event(struct cell *t, struct tmcb_params *ps)
+{
+	int backup_route_type;
+	subs_t* backup_subs = NULL;
+	sip_msg_t msg;
+
+	if (goto_on_notify_reply==-1)
+		return;
+
+	if(likely(build_sip_msg_from_buf(&msg, t->uac->request.buffer, t->uac->request.buffer_len, inc_msg_no())<0)) {
+		LM_ERR("failed to parse msg buffer\n");
 		return;
 	}
 
-	cb= (c_back_param*)(*ps->param);
+	_pres_subs_notify_reply_code = ps->code;
+	if( ps->code == 408 || ps->rpl == NULL) {
+		_pres_subs_notify_reply_msg = faked_msg();
+	} else {
+		_pres_subs_notify_reply_msg = ps->rpl;
+	}
+
+	backup_subs = _pres_subs_last_sub;
+	_pres_subs_last_sub = mem_copy_subs((subs_t*)(*ps->param), PKG_MEM_TYPE);
+
+	backup_route_type = get_route_type();
+	set_route_type(LOCAL_ROUTE);
+	run_top_route(event_rt.rlist[goto_on_notify_reply], &msg, 0);
+	set_route_type(backup_route_type);
+
+	_pres_subs_notify_reply_msg = NULL;
+	_pres_subs_notify_reply_code = 0;
+	pkg_free(_pres_subs_last_sub);
+	_pres_subs_last_sub = backup_subs;
+	free_sip_msg(&msg);
+
+}
+
+void p_tm_callback( struct cell *t, int type, struct tmcb_params *ps)
+{
+	subs_t* subs;
+
+	if(ps->param == NULL || *ps->param == NULL) {
+                LM_ERR("weird shit happening\n");
+                if(ps->param != NULL && *ps->param !=NULL)
+                        shm_free((subs_t*)(*ps->param));
+                return;
+	}
+
+	subs= (subs_t*)(*ps->param);
 	LM_DBG("completed with status %d [to_tag:%.*s]\n",
-			ps->code, cb->to_tag.len, cb->to_tag.s);
+			ps->code, subs->to_tag.len, subs->to_tag.s);
+
+	run_notify_reply_event(t, ps);
 
-	if(ps->code == 481 || (ps->code == 408 && timeout_rm_subs))
-		delete_subs(&cb->pres_uri, &cb->ev_name,
-				&cb->to_tag, &cb->from_tag, &cb->callid);
+	if(ps->code == 404 || ps->code == 481 || (ps->code == 408 && timeout_rm_subs)) {
+		delete_subs(&subs->pres_uri, &subs->event->name,
+				&subs->to_tag, &subs->from_tag, &subs->callid);
+	}
 
-	free_cbparam(cb);
+	shm_free(subs);
 }
 
 void free_cbparam(c_back_param* cb_param)
@@ -2667,7 +2759,7 @@ error:
 
 int process_dialogs(int round, int presence_winfo)
 {
-	db_key_t query_cols[3], result_cols[18], update_cols[4];
+	db_key_t query_cols[3], result_cols[20], update_cols[4];
 	db_val_t query_vals[3], update_vals[4], *values, *dvalues;
 	db_op_t query_ops[2];
 	db_row_t *rows, *drows;
@@ -2678,6 +2770,7 @@ int process_dialogs(int round, int presence_winfo)
 	int wuser_col, wdomain_col, sockinfo_col, lcontact_col, contact_col;
 	int rroute_col, event_id_col, reason_col, event_col, lcseq_col;
 	int rcseq_col, status_col, version_col, updated_winfo_col, expires_col;
+	int flags_col, user_agent_col;
 	int i, notify_sent = 0, cached_updated_winfo, ret = -1;
 	int end_transaction = 0;
 	subs_t sub;
@@ -2818,6 +2911,8 @@ int process_dialogs(int round, int presence_winfo)
 		result_cols[version_col = n_result_cols++] = &str_version_col;
 		result_cols[updated_winfo_col = n_result_cols++] = &str_updated_winfo_col;
 		result_cols[expires_col = n_result_cols++] = &str_expires_col;
+		result_cols[flags_col = n_result_cols++] = &str_flags_col;
+		result_cols[user_agent_col = n_result_cols++] = &str_user_agent_col;
 
 		/* Need to redo this here as we might have switched to the
 		   presentity table during a previous iteration. */
@@ -2885,6 +2980,7 @@ int process_dialogs(int round, int presence_winfo)
 		EXTRACT_STRING(sub.record_route, VAL_STRING(&dvalues[rroute_col]));
 		EXTRACT_STRING(sub.event_id, VAL_STRING(&dvalues[event_id_col]));
 		EXTRACT_STRING(sub.reason, VAL_STRING(&dvalues[reason_col]));
+		EXTRACT_STRING(sub.user_agent, VAL_STRING(&dvalues[user_agent_col]));
 
 		sub.local_cseq = VAL_INT(&dvalues[lcseq_col]) + 1;
 		sub.remote_cseq = VAL_INT(&dvalues[rcseq_col]);
@@ -2897,6 +2993,7 @@ int process_dialogs(int round, int presence_winfo)
 			sub.expires = VAL_INT(&dvalues[expires_col]) - now;
 		else
 			sub.expires = 0;
+		sub.flags = VAL_INT(&dvalues[flags_col]);
 
 		sub.updated = round;
 

+ 6 - 0
modules/presence/notify.h

@@ -97,6 +97,12 @@ extern str str_sender_col;
 extern str str_updated_col;
 extern str str_updated_winfo_col;
 extern str str_priority_col;
+extern str str_flags_col;
+extern str str_user_agent_col;
+
+extern int goto_on_notify_reply;
+int pv_parse_notify_reply_var_name(pv_spec_p sp, str *in);
+int pv_get_notify_reply(struct sip_msg *msg,  pv_param_t *param, pv_value_t *res);
 
 void PRINT_DLG(FILE* out, dlg_t* _d);
 

+ 16 - 2
modules/presence/presence.c

@@ -77,7 +77,7 @@ MODULE_VERSION
 
 #define S_TABLE_VERSION  3
 #define P_TABLE_VERSION  4
-#define ACTWATCH_TABLE_VERSION 11
+#define ACTWATCH_TABLE_VERSION 12
 
 char *log_buf = NULL;
 static int clean_period=100;
@@ -230,6 +230,7 @@ static mi_export_t mi_cmds[] = {
 
 static pv_export_t pres_mod_pvs[] = {
 	{{"subs", (sizeof("subs")-1)}, PVT_OTHER, pv_get_subscription, 0, pv_parse_subscription_name, 0, 0, 0},
+	{{"notify_reply", (sizeof("notify_reply")-1)}, PVT_OTHER, pv_get_notify_reply, 0, pv_parse_notify_reply_var_name, 0, 0, 0},
 	{ {0, 0}, 0, 0, 0, 0, 0, 0, 0 }
 };
 
@@ -440,6 +441,10 @@ static int mod_init(void)
 	pa_dbf.close(pa_db);
 	pa_db = NULL;
 
+	goto_on_notify_reply=route_lookup(&event_rt, "presence:notify-reply");
+	if (goto_on_notify_reply>=0 && event_rt.rlist[goto_on_notify_reply]==0)
+		goto_on_notify_reply=-1; /* disable */
+
 	return 0;
 }
 
@@ -1220,7 +1225,7 @@ static int update_pw_dialogs_dbonlymode(subs_t* subs, subs_t** subs_array)
 {
 	db_key_t query_cols[5], db_cols[3];
 	db_val_t query_vals[5], db_vals[3];
-	db_key_t result_cols[24];
+	db_key_t result_cols[26];
 	int n_query_cols=0, n_result_cols=0, n_update_cols=0;
 	int event_col, pres_uri_col, watcher_user_col, watcher_domain_col;
 	int r_pres_uri_col,r_to_user_col,r_to_domain_col;
@@ -1231,6 +1236,7 @@ static int update_pw_dialogs_dbonlymode(subs_t* subs, subs_t** subs_array)
 	int r_event_col, r_local_cseq_col, r_remote_cseq_col;
 	int r_status_col, r_version_col;
 	int r_expires_col, r_watcher_user_col, r_watcher_domain_col;
+	int r_flags_col, r_user_agent_col;
 	db1_res_t *result= NULL;
  	db_val_t *row_vals;
 	db_row_t *rows;
@@ -1301,6 +1307,9 @@ static int update_pw_dialogs_dbonlymode(subs_t* subs, subs_t** subs_array)
 	result_cols[r_status_col=n_result_cols++] = &str_status_col;
 	/*********************************************/
 
+	result_cols[r_flags_col=n_result_cols++] = &str_flags_col;
+	result_cols[r_user_agent_col=n_result_cols++] = &str_user_agent_col;
+
 	if(pa_dbf.query(pa_db, query_cols, 0, query_vals, result_cols, 
 				n_query_cols, n_result_cols, 0, &result )< 0)
 	{
@@ -1399,6 +1408,11 @@ static int update_pw_dialogs_dbonlymode(subs_t* subs, subs_t** subs_array)
 
 		s.version = row_vals[r_version_col].val.int_val;
 
+		s.flags = row_vals[r_flags_col].val.int_val;
+		s.user_agent.s=  (char*)row_vals[r_user_agent_col].val.string_val;
+		s.user_agent.len= (s.user_agent.s)?strlen(s.user_agent.s):0;
+
+
 		cs = mem_copy_subs(&s, PKG_MEM_TYPE);
 		if (cs == NULL)
 		{

+ 251 - 41
modules/presence/subscribe.c

@@ -40,6 +40,7 @@
 #include "notify.h"
 #include "../pua/hash.h"
 #include "../../mod_fix.h"
+#include "../../dset.h"
 
 int get_stored_info(struct sip_msg* msg, subs_t* subs, int* error_ret,
 		str* reply_str);
@@ -181,14 +182,15 @@ int delete_db_subs(str* to_tag, str* from_tag, str* callid)
 
 int insert_subs_db(subs_t* s, int type)
 {
-	db_key_t query_cols[24];
-	db_val_t query_vals[24];
+	db_key_t query_cols[26];
+	db_val_t query_vals[26];
 	int n_query_cols = 0;
 	int pres_uri_col, to_user_col, to_domain_col, from_user_col, from_domain_col,
 		callid_col, totag_col, fromtag_col, event_col,status_col, event_id_col, 
 		local_cseq_col, remote_cseq_col, expires_col, record_route_col, 
 		contact_col, local_contact_col, version_col,socket_info_col,reason_col,
-		watcher_user_col, watcher_domain_col, updated_col, updated_winfo_col;
+		watcher_user_col, watcher_domain_col, updated_col, updated_winfo_col,
+		user_agent_col, flags_col;
 		
 	if(pa_dbf.use_table(pa_db, &active_watchers_table)< 0)
 	{
@@ -316,6 +318,16 @@ int insert_subs_db(subs_t* s, int type)
 	query_vals[updated_winfo_col].nul = 0;
 	n_query_cols++;
 
+	query_cols[flags_col= n_query_cols]=&str_flags_col;
+	query_vals[flags_col].type = DB1_INT;
+	query_vals[flags_col].nul = 0;
+	n_query_cols++;
+
+	query_cols[user_agent_col= n_query_cols]=&str_user_agent_col;
+	query_vals[user_agent_col].type = DB1_STR;
+	query_vals[user_agent_col].nul = 0;
+	n_query_cols++;
+
 	query_vals[pres_uri_col].val.str_val= s->pres_uri;
 	query_vals[callid_col].val.str_val= s->callid;
 	query_vals[totag_col].val.str_val= s->to_tag;
@@ -340,6 +352,8 @@ int insert_subs_db(subs_t* s, int type)
 	query_vals[socket_info_col].val.str_val= s->sockinfo_str;
 	query_vals[updated_col].val.int_val = s->updated;
 	query_vals[updated_winfo_col].val.int_val = s->updated_winfo;
+	query_vals[flags_col].val.int_val = s->flags;
+	query_vals[user_agent_col].val.str_val= s->user_agent;
 
 	if (pa_dbf.use_table(pa_db, &active_watchers_table) < 0)
 	{
@@ -755,7 +769,37 @@ void msg_watchers_clean(unsigned int ticks,void *param)
 		LM_ERR("cleaning pending subscriptions\n");
 }
 
-static char* _pres_subs_last_presentity = NULL;
+subs_t* _pres_subs_last_sub = NULL;
+
+/*
+ * Map between $subs(idxname) and subs_t
+ *
+ * uri (pres_uri) 1
+ * pres_uri 1
+ * to_user 2
+ * to_domain 3
+ * from_user 4
+ * from_domain 5
+ * watcher_username 6
+ * watcher_domain 7
+ * event (event->name) 8
+ * event_id 9
+ * to_tag 10
+ * from_tag 11
+ * callid 12
+ * remote_cseq 13
+ * local_cseq 14
+ * contact 15
+ * local_contact 16
+ * record_route 17
+ * expires 18
+ * status 19
+ * reason 20
+ * version 21
+ * flags 22
+ * user_agent 23
+ *
+ */
 
 int pv_parse_subscription_name(pv_spec_p sp, str *in)
 {
@@ -771,6 +815,119 @@ int pv_parse_subscription_name(pv_spec_p sp, str *in)
 				goto error;
 			};
 			break;
+
+		case 5:
+			if(strncmp(in->s, "event", 5)==0) {
+				sp->pvp.pvn.u.isname.name.n = 8;
+			} else if(strncmp(in->s, "flags", 5)==0) {
+				sp->pvp.pvn.u.isname.name.n = 22;
+			} else {
+				goto error;
+			};
+			break;
+
+		case 6:
+			if(strncmp(in->s, "to_tag", 6)==0) {
+				sp->pvp.pvn.u.isname.name.n = 10;
+			} else if(strncmp(in->s, "callid", 6)==0) {
+				sp->pvp.pvn.u.isname.name.n = 12;
+			} else if(strncmp(in->s, "status", 6)==0) {
+				sp->pvp.pvn.u.isname.name.n = 19;
+			} else if(strncmp(in->s, "reason", 6)==0) {
+				sp->pvp.pvn.u.isname.name.n = 20;
+			} else {
+				goto error;
+			};
+			break;
+
+		case 7:
+			if(strncmp(in->s, "to_user", 7)==0) {
+				sp->pvp.pvn.u.isname.name.n = 2;
+			} else if(strncmp(in->s, "contact", 7)==0) {
+				sp->pvp.pvn.u.isname.name.n = 15;
+			} else if(strncmp(in->s, "expires", 7)==0) {
+				sp->pvp.pvn.u.isname.name.n = 18;
+			} else if(strncmp(in->s, "version", 7)==0) {
+				sp->pvp.pvn.u.isname.name.n = 21;
+			} else {
+				goto error;
+			};
+			break;
+
+		case 8:
+			if(strncmp(in->s, "pres_uri", 8)==0) {
+				sp->pvp.pvn.u.isname.name.n = 1;
+			} else if(strncmp(in->s, "event_id", 8)==0) {
+				sp->pvp.pvn.u.isname.name.n = 9;
+			} else if(strncmp(in->s, "from_tag", 8)==0) {
+				sp->pvp.pvn.u.isname.name.n = 11;
+			} else {
+				goto error;
+			};
+			break;
+
+		case 9:
+			if(strncmp(in->s, "to_domain", 9)==0) {
+				sp->pvp.pvn.u.isname.name.n = 3;
+			} else if(strncmp(in->s, "from_user", 9)==0) {
+				sp->pvp.pvn.u.isname.name.n = 4;
+			} else {
+				goto error;
+			};
+			break;
+
+		case 10:
+			if(strncmp(in->s, "local_cseq", 10)==0) {
+				sp->pvp.pvn.u.isname.name.n = 14;
+			} else if(strncmp(in->s, "user_agent", 10)==0) {
+				sp->pvp.pvn.u.isname.name.n = 23;
+			} else {
+				goto error;
+			};
+			break;
+
+		case 11:
+			if(strncmp(in->s, "from_domain", 11)==0) {
+				sp->pvp.pvn.u.isname.name.n = 5;
+			} else if(strncmp(in->s, "remote_cseq", 11)==0) {
+				sp->pvp.pvn.u.isname.name.n = 13;
+			} else {
+				goto error;
+			};
+			break;
+
+		case 12:
+			if(strncmp(in->s, "record_route", 12)==0) {
+				sp->pvp.pvn.u.isname.name.n = 17;
+			} else {
+				goto error;
+			};
+			break;
+
+		case 13:
+			if(strncmp(in->s, "local_contact", 13)==0) {
+				sp->pvp.pvn.u.isname.name.n = 16;
+			} else {
+				goto error;
+			};
+			break;
+
+		case 14:
+			if(strncmp(in->s, "watcher_domain", 14)==0) {
+				sp->pvp.pvn.u.isname.name.n = 7;
+			} else {
+				goto error;
+			};
+			break;
+
+		case 16:
+			if(strncmp(in->s, "watcher_username", 16)==0) {
+				sp->pvp.pvn.u.isname.name.n = 6;
+			} else {
+				goto error;
+			};
+			break;
+
 		default:
 			goto error;
 	}
@@ -786,13 +943,60 @@ error:
 
 int pv_get_subscription(struct sip_msg *msg, pv_param_t *param,	pv_value_t *res)
 {
-	if(param->pvn.u.isname.name.n==1) /* presentity */
-		return (_pres_subs_last_presentity == NULL) ? pv_get_null(msg, param, res)
-						: pv_get_strzval(msg, param, res, _pres_subs_last_presentity);
+	if(_pres_subs_last_sub == NULL) {
+		return pv_get_null(msg, param, res);
+	}
+
+	if(param->pvn.u.isname.name.n==1) {
+		return pv_get_strval(msg, param, res, &_pres_subs_last_sub->pres_uri);
+	} else if(param->pvn.u.isname.name.n==2) {
+		return pv_get_strval(msg, param, res, &_pres_subs_last_sub->to_user);
+	} else if(param->pvn.u.isname.name.n==3) {
+		return pv_get_strval(msg, param, res, &_pres_subs_last_sub->to_domain);
+	} else if(param->pvn.u.isname.name.n==4) {
+		return pv_get_strval(msg, param, res, &_pres_subs_last_sub->from_user);
+	} else if(param->pvn.u.isname.name.n==5) {
+		return pv_get_strval(msg, param, res, &_pres_subs_last_sub->from_domain);
+	} else if(param->pvn.u.isname.name.n==6) {
+		return pv_get_strval(msg, param, res, &_pres_subs_last_sub->watcher_user);
+	} else if(param->pvn.u.isname.name.n==7) {
+		return pv_get_strval(msg, param, res, &_pres_subs_last_sub->watcher_domain);
+	} else if(param->pvn.u.isname.name.n==8) {
+		return pv_get_strval(msg, param, res, &_pres_subs_last_sub->event->name);
+	} else if(param->pvn.u.isname.name.n==9) {
+		return pv_get_strval(msg, param, res, &_pres_subs_last_sub->event_id);
+	} else if(param->pvn.u.isname.name.n==10) {
+		return pv_get_strval(msg, param, res, &_pres_subs_last_sub->to_tag);
+	} else if(param->pvn.u.isname.name.n==11) {
+		return pv_get_strval(msg, param, res, &_pres_subs_last_sub->from_tag);
+	} else if(param->pvn.u.isname.name.n==12) {
+		return pv_get_strval(msg, param, res, &_pres_subs_last_sub->callid);
+	} else if(param->pvn.u.isname.name.n==13) {
+		return pv_get_uintval(msg, param, res, _pres_subs_last_sub->remote_cseq);
+	} else if(param->pvn.u.isname.name.n==14) {
+		return pv_get_uintval(msg, param, res, _pres_subs_last_sub->local_cseq);
+	} else if(param->pvn.u.isname.name.n==15) {
+		return pv_get_strval(msg, param, res, &_pres_subs_last_sub->contact);
+	} else if(param->pvn.u.isname.name.n==16) {
+		return pv_get_strval(msg, param, res, &_pres_subs_last_sub->local_contact);
+	} else if(param->pvn.u.isname.name.n==17) {
+		return pv_get_strval(msg, param, res, &_pres_subs_last_sub->record_route);
+	} else if(param->pvn.u.isname.name.n==18) {
+		return pv_get_uintval(msg, param, res, _pres_subs_last_sub->expires);
+	} else if(param->pvn.u.isname.name.n==19) {
+		return pv_get_uintval(msg, param, res, _pres_subs_last_sub->status);
+	} else if(param->pvn.u.isname.name.n==20) {
+		return pv_get_strval(msg, param, res, &_pres_subs_last_sub->reason);
+	} else if(param->pvn.u.isname.name.n==21) {
+		return pv_get_sintval(msg, param, res, _pres_subs_last_sub->version);
+	} else if(param->pvn.u.isname.name.n==22) {
+		return pv_get_sintval(msg, param, res, _pres_subs_last_sub->flags);
+	} else if(param->pvn.u.isname.name.n==23) {
+		return pv_get_strval(msg, param, res, &_pres_subs_last_sub->user_agent);
+	}
 
 	LM_ERR("unknown specifier\n");
 	return pv_get_null(msg, param, res);
-
 }
 
 int handle_subscribe0(struct sip_msg* msg)
@@ -849,9 +1053,9 @@ int handle_subscribe(struct sip_msg* msg, str watcher_user, str watcher_domain)
 	str reply_str;
 	int sent_reply= 0;
 
-	if(_pres_subs_last_presentity) {
-		pkg_free(_pres_subs_last_presentity);
-		_pres_subs_last_presentity = NULL;
+	if(_pres_subs_last_sub) {
+		pkg_free(_pres_subs_last_sub);
+		_pres_subs_last_sub = NULL;
 	}
 
 	/* ??? rename to avoid collisions with other symbols */
@@ -952,13 +1156,6 @@ int handle_subscribe(struct sip_msg* msg, str watcher_user, str watcher_domain)
 		reason= subs.reason;
 	}
 
-	if(subs.pres_uri.len > 0 && subs.pres_uri.s) {
-		_pres_subs_last_presentity =
-				(char*)pkg_malloc((subs.pres_uri.len+1) * sizeof(char));
-		strncpy(_pres_subs_last_presentity, subs.pres_uri.s, subs.pres_uri.len);
-		_pres_subs_last_presentity[subs.pres_uri.len] = '\0';
-	}
-		
 	/* mark that the received event is a SUBSCRIBE message */
 	subs.recv_event = PRES_SUBSCRIBE_RECV;
 
@@ -1033,6 +1230,8 @@ int handle_subscribe(struct sip_msg* msg, str watcher_user, str watcher_domain)
 		}
 	}
 
+	_pres_subs_last_sub = mem_copy_subs(&subs, PKG_MEM_TYPE);
+
 	/* check if correct status */
 	if(get_status_str(subs.status)== NULL)
 	{
@@ -1378,6 +1577,12 @@ int extract_sdialog_info_ex(subs_t* subs,struct sip_msg* msg, int miexp,
 	else
 		subs->local_contact= scontact;
 
+	if (parse_headers(msg, HDR_USERAGENT_F, 0) != -1 && msg->user_agent &&
+			msg->user_agent->body.len>0 && msg->user_agent->body.len<MAX_UA_SIZE) {
+		subs->user_agent = msg->user_agent->body;
+	}
+	getbflagsval(0, &subs->flags);
+
 	free_to_params(&TO);
 	return 0;
 	
@@ -1534,7 +1739,7 @@ int get_database_info(struct sip_msg* msg, subs_t* subs, int* reply_code, str* r
 {
 	db_key_t query_cols[3];
 	db_val_t query_vals[3];
-	db_key_t result_cols[9];
+	db_key_t result_cols[10];
 	db1_res_t *result= NULL;
 	db_row_t *row ;	
 	db_val_t *row_vals ;
@@ -1543,6 +1748,7 @@ int get_database_info(struct sip_msg* msg, subs_t* subs, int* reply_code, str* r
 	int remote_cseq_col= 0, local_cseq_col= 0, status_col, reason_col;
 	int record_route_col, version_col, pres_uri_col;
 	int updated_col, updated_winfo_col;
+	int flags_col;
 	unsigned int remote_cseq;
 	str pres_uri, record_route;
 	str reason;
@@ -1575,6 +1781,7 @@ int get_database_info(struct sip_msg* msg, subs_t* subs, int* reply_code, str* r
 	result_cols[version_col=n_result_cols++] = &str_version_col;
 	result_cols[updated_col=n_result_cols++] = &str_updated_col;
 	result_cols[updated_winfo_col=n_result_cols++] = &str_updated_winfo_col;
+	result_cols[flags_col=n_result_cols++] = &str_flags_col;
 	
 	if (pa_dbf.use_table(pa_db, &active_watchers_table) < 0) 
 	{
@@ -1626,13 +1833,14 @@ int get_database_info(struct sip_msg* msg, subs_t* subs, int* reply_code, str* r
 	if(reason.s)
 	{
 		reason.len= strlen(reason.s);
-		subs->reason.s= (char*)pkg_malloc(reason.len* sizeof(char));
-		if(subs->reason.s== NULL)
-		{
-			ERR_MEM(PKG_MEM_STR);
+		if(reason.len > 0) {
+			subs->reason.s= (char*)pkg_malloc(reason.len* sizeof(char));
+			if(subs->reason.s == NULL) {
+				ERR_MEM(PKG_MEM_STR);
+			}
+			memcpy(subs->reason.s, reason.s, reason.len);
+			subs->reason.len= reason.len;
 		}
-		memcpy(subs->reason.s, reason.s, reason.len);
-		subs->reason.len= reason.len;
 	}
 
 	subs->local_cseq= row_vals[local_cseq_col].val.int_val + 1;
@@ -1642,32 +1850,34 @@ int get_database_info(struct sip_msg* msg, subs_t* subs, int* reply_code, str* r
 	{
 		pres_uri.s= (char*)row_vals[pres_uri_col].val.string_val;
 		pres_uri.len= strlen(pres_uri.s);
-		subs->pres_uri.s= (char*)pkg_malloc(pres_uri.len* sizeof(char));
-		if(subs->pres_uri.s== NULL)
-		{	
-			if(subs->reason.s)
-				pkg_free(subs->reason.s);
-			ERR_MEM(PKG_MEM_STR);
+		if(pres_uri.len > 0) {
+			subs->pres_uri.s= (char*)pkg_malloc(pres_uri.len* sizeof(char));
+			if(subs->pres_uri.s== NULL) {
+				if(subs->reason.s)
+					pkg_free(subs->reason.s);
+				ERR_MEM(PKG_MEM_STR);
+			}
+			memcpy(subs->pres_uri.s, pres_uri.s, pres_uri.len);
+			subs->pres_uri.len= pres_uri.len;
 		}
-		memcpy(subs->pres_uri.s, pres_uri.s, pres_uri.len);
-		subs->pres_uri.len= pres_uri.len;
 	}
 
 	record_route.s= (char*)row_vals[record_route_col].val.string_val;
-	if(record_route.s)
-	{
+	if(record_route.s) {
 		record_route.len= strlen(record_route.s);
-		subs->record_route.s= (char*)pkg_malloc(record_route.len*sizeof(char));
-		if(subs->record_route.s== NULL)
-		{
-			ERR_MEM(PKG_MEM_STR);
+		if( record_route.len > 0) {
+			subs->record_route.s= (char*)pkg_malloc(record_route.len*sizeof(char));
+			if(subs->record_route.s== NULL) {
+				ERR_MEM(PKG_MEM_STR);
+			}
+			memcpy(subs->record_route.s, record_route.s, record_route.len);
+			subs->record_route.len= record_route.len;
 		}
-		memcpy(subs->record_route.s, record_route.s, record_route.len);
-		subs->record_route.len= record_route.len;
 	}
 
 	subs->updated= row_vals[updated_col].val.int_val;
 	subs->updated_winfo= row_vals[updated_winfo_col].val.int_val;
+	subs->flags = row_vals[flags_col].val.int_val;
 
 	pa_dbf.free_result(pa_db, result);
 	result= NULL;

+ 2 - 0
modules/presence/subscribe.h

@@ -83,6 +83,8 @@ struct subscription
 	int internal_update_flag;
 	int updated;
 	int updated_winfo;
+	flag_t flags;
+	str user_agent;
 	struct subscription* next;
 
 };