/* * pua module - presence user agent module * * Copyright (C) 2006 Voice Sistem S.R.L. * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #include <libxml/parser.h> #include "../../core/mem/mem.h" #include "../../core/dprint.h" #include "../../core/ut.h" #include "../../modules/tm/tm_load.h" #include "../../modules/tm/dlg.h" #include "../../core/parser/msg_parser.h" #include "../../core/parser/contact/parse_contact.h" #include "../../core/parser/parse_from.h" #include "../../core/parser/parse_expires.h" #include "../presence/hash.h" #include "../../core/hashes.h" #include "hash.h" #include "pua.h" #include "send_subscribe.h" #include "pua_callback.h" #include "event_list.h" #include "pua_db.h" #include "../presence/subscribe.h" extern db_locking_t db_table_lock; void print_subs(subs_info_t* subs) { LM_DBG("pres_uri= %.*s - len: %d\n", subs->pres_uri->len, subs->pres_uri->s, subs->pres_uri->len ); LM_DBG("watcher_uri= %.*s - len: %d\n", subs->watcher_uri->len, subs->watcher_uri->s, subs->watcher_uri->len); } str* subs_build_hdr(str* contact, int expires, int event, str* extra_headers) { str* str_hdr= NULL; static char buf[3000]; char* subs_expires= NULL; int len= 1; pua_event_t* ev; str_hdr= (str*)pkg_malloc(sizeof(str)); if(str_hdr== NULL) { PKG_MEM_ERROR; return NULL; } memset(str_hdr, 0, sizeof(str)); str_hdr->s= buf; ev= get_event(event); if(ev== NULL) { LM_ERR("getting event from list\n"); goto error; } memcpy(str_hdr->s ,"Max-Forwards: ", 14); str_hdr->len = 14; str_hdr->len+= sprintf(str_hdr->s+ str_hdr->len,"%d", MAX_FORWARD); memcpy(str_hdr->s+str_hdr->len, CRLF, CRLF_LEN); str_hdr->len += CRLF_LEN; memcpy(str_hdr->s+ str_hdr->len ,"Event: ", 7); str_hdr->len+= 7; memcpy(str_hdr->s+ str_hdr->len, ev->name.s, ev->name.len); str_hdr->len+= ev->name.len; memcpy(str_hdr->s+str_hdr->len, CRLF, CRLF_LEN); str_hdr->len += CRLF_LEN; memcpy(str_hdr->s+ str_hdr->len ,"Contact: <", 10); str_hdr->len += 10; memcpy(str_hdr->s +str_hdr->len, contact->s, contact->len); str_hdr->len+= contact->len; memcpy(str_hdr->s+ str_hdr->len, ">", 1); str_hdr->len+= 1; memcpy(str_hdr->s+str_hdr->len, CRLF, CRLF_LEN); str_hdr->len += CRLF_LEN; memcpy(str_hdr->s+ str_hdr->len ,"Expires: ", 9); str_hdr->len += 9; if( expires<= min_expires) subs_expires= int2str(min_expires, &len); else subs_expires= int2str(expires+ 10, &len); if(subs_expires == NULL || len == 0) { LM_ERR("while converting int to str\n"); pkg_free(str_hdr); return NULL; } memcpy(str_hdr->s+str_hdr->len, subs_expires, len); str_hdr->len += len; memcpy(str_hdr->s+str_hdr->len, CRLF, CRLF_LEN); str_hdr->len += CRLF_LEN; if(extra_headers && extra_headers->len) { memcpy(str_hdr->s+str_hdr->len, extra_headers->s, extra_headers->len); str_hdr->len += extra_headers->len; } str_hdr->s[str_hdr->len]= '\0'; return str_hdr; error: if(str_hdr) pkg_free(str_hdr); return NULL; } dlg_t* pua_build_dlg_t(ua_pres_t* presentity) { dlg_t* td =NULL; int size; size= sizeof(dlg_t)+ presentity->call_id.len+ presentity->to_tag.len+ presentity->from_tag.len+ presentity->watcher_uri->len+ presentity->pres_uri->len+ presentity->remote_contact.len; td = (dlg_t*)pkg_malloc(size); if(td == NULL) { PKG_MEM_ERROR; return NULL; } memset(td, 0, size); size= sizeof(dlg_t); td->id.call_id.s = (char*)td+ size; memcpy(td->id.call_id.s, presentity->call_id.s, presentity->call_id.len); td->id.call_id.len= presentity->call_id.len; size+= presentity->call_id.len; td->id.rem_tag.s = (char*)td+ size; memcpy(td->id.rem_tag.s, presentity->to_tag.s, presentity->to_tag.len); td->id.rem_tag.len = presentity->to_tag.len; size+= presentity->to_tag.len; td->id.loc_tag.s = (char*)td+ size; memcpy(td->id.loc_tag.s, presentity->from_tag.s, presentity->from_tag.len); td->id.loc_tag.len =presentity->from_tag.len; size+= presentity->from_tag.len; td->loc_uri.s = (char*)td+ size; memcpy(td->loc_uri.s, presentity->watcher_uri->s, presentity->watcher_uri->len) ; td->loc_uri.len = presentity->watcher_uri->len; size+= td->loc_uri.len; td->rem_uri.s = (char*)td+ size; memcpy(td->rem_uri.s, presentity->pres_uri->s, presentity->pres_uri->len) ; td->rem_uri.len = presentity->pres_uri->len; size+= td->rem_uri.len; td->rem_target.s = (char*)td+ size; memcpy(td->rem_target.s, presentity->remote_contact.s, presentity->remote_contact.len) ; td->rem_target.len = presentity->remote_contact.len; size+= td->rem_target.len; if(presentity->record_route.s && presentity->record_route.len) { if(parse_rr_body(presentity->record_route.s, presentity->record_route.len, &td->route_set)< 0) { LM_ERR("ERROR in function parse_rr_body\n"); pkg_free(td); return NULL; } } td->loc_seq.value = presentity->cseq; td->loc_seq.is_set = 1; td->state= DLG_CONFIRMED ; LM_DBG("size = %d\n", size); return td; } /** * free the field in dlg_t as filled/allocated by this module */ static int pua_free_tm_dlg(dlg_t *td) { if(td) { if(td->route_set) free_rr(&td->route_set); pkg_free(td); } return 0; } static void find_and_delete_dialog(ua_pres_t *dialog, int hash_code) { ua_pres_t *presentity; if (dbmode == PUA_DB_ONLY) { delete_dialog_puadb(dialog); } else { lock_get(&HashT->p_records[hash_code].lock); presentity= get_dialog(dialog, hash_code); if (presentity == NULL) { presentity = get_temporary_dialog(dialog, hash_code); if(presentity== NULL) { LM_ERR("no record found\n"); lock_release(&HashT->p_records[hash_code].lock); return; } } delete_htable(presentity, hash_code); lock_release(&HashT->p_records[hash_code].lock); } } static void find_and_update_dialog(ua_pres_t *dialog, int hash_code, int lexpire, str *contact) { ua_pres_t *presentity; if (dbmode == PUA_DB_ONLY) { update_dialog_puadb(dialog, lexpire, contact); } else { lock_get(&HashT->p_records[hash_code].lock); presentity= get_dialog(dialog, hash_code); if (presentity == NULL) { LM_ERR("no record found\n"); lock_release(&HashT->p_records[hash_code].lock); return; } update_htable(presentity, dialog->desired_expires, lexpire, NULL, hash_code, contact); lock_release(&HashT->p_records[hash_code].lock); } } void subs_cback_func(struct cell *t, int cb_type, struct tmcb_params *ps) { struct sip_msg* msg= NULL; int lexpire= 0; unsigned int cseq; ua_pres_t* presentity= NULL, *hentity= NULL; struct to_body *pto = NULL, TO = {0}, *pfrom = NULL; int size= 0; unsigned int hash_code; int flag ; str record_route= {0, 0}; int rt; str contact; int initial_request = 0; int end_transaction = 1; if( ps->param== NULL || *ps->param== NULL ) { LM_ERR("null callback parameter\n"); return; } if (dbmode == PUA_DB_ONLY && pua_dbf.start_transaction) { if (pua_dbf.start_transaction(pua_db, db_table_lock) < 0) { LM_ERR("in start_transaction\n"); goto error; } } LM_DBG("completed with status %d\n",ps->code) ; hentity= (ua_pres_t*)(*ps->param); hash_code= core_hash(hentity->pres_uri,hentity->watcher_uri, HASH_SIZE); flag= hentity->flag; if(hentity->flag & XMPP_INITIAL_SUBS) hentity->flag= XMPP_SUBSCRIBE; /* get dialog information from reply message: callid, to_tag, from_tag */ msg= ps->rpl; if(msg == NULL) { LM_ERR("no reply message found\n "); goto error; } if(msg== FAKED_REPLY) { struct hdr_field *callid = NULL, *from = NULL; struct to_body FROM = {0}; callid = (struct hdr_field *) pkg_malloc(sizeof(struct hdr_field)); if (callid == NULL) { PKG_MEM_ERROR; goto faked_error; } memset(callid, 0, sizeof(struct hdr_field)); get_hdr_field(t->callid.s, t->callid.s + t->callid.len, callid); hentity->call_id = callid->body; from = (struct hdr_field *) pkg_malloc(sizeof(struct hdr_field)); if (from == NULL) { PKG_MEM_ERROR; goto faked_error; } memset(from, 0, sizeof(struct hdr_field)); get_hdr_field(t->from.s, t->from.s + t->from.len, from); parse_to(from->body.s, from->body.s + from->body.len + 1, &FROM); if(FROM.uri.len <= 0) { LM_ERR("'From' header NOT parsed\n"); goto faked_error; } hentity->call_id = callid->body; hentity->from_tag = (&FROM)->tag_value; hentity->to_tag.s = NULL; hentity->to_tag.len = 0; find_and_delete_dialog(hentity, hash_code); faked_error: if (callid) pkg_free(callid); free_to_params(&FROM); if (from) pkg_free(from); goto done; } if ( parse_headers(msg,HDR_EOH_F, 0)==-1 ) { LM_ERR("when parsing headers\n"); goto error; } if(ps->rpl->expires && msg->expires->body.len > 0) { if (!msg->expires->parsed && (parse_expires(msg->expires) < 0)) { LM_ERR("cannot parse Expires header\n"); goto error; } lexpire = ((exp_body_t*)msg->expires->parsed)->val; LM_DBG("lexpire= %d\n", lexpire); } /*if initial request */ if(hentity->call_id.s== NULL) { initial_request = 1; if( msg->callid==NULL || msg->callid->body.s==NULL) { LM_ERR("cannot parse callid header\n"); goto error; } if (!msg->from || !msg->from->body.s) { LM_ERR("cannot find 'from' header!\n"); goto error; } if (msg->from->parsed == NULL) { if ( parse_from_header( msg )<0 ) { LM_ERR("cannot parse From header\n"); goto error; } } pfrom = (struct to_body*)msg->from->parsed; if( pfrom->tag_value.s ==NULL || pfrom->tag_value.len == 0) { LM_ERR("no from tag value present\n"); goto error; } hentity->call_id= msg->callid->body; hentity->from_tag= pfrom->tag_value; if(ps->code >= 300 || lexpire == 0) { hentity->to_tag.s = NULL; hentity->to_tag.len = 0; find_and_delete_dialog(hentity, hash_code); goto done; } if( msg->to==NULL || msg->to->body.s==NULL) { LM_ERR("cannot parse TO header\n"); goto error; } if(msg->to->parsed != NULL) { pto = (struct to_body*)msg->to->parsed; LM_DBG("'To' header ALREADY PARSED: <%.*s>\n",pto->uri.len,pto->uri.s); } else { parse_to(msg->to->body.s,msg->to->body.s + msg->to->body.len + 1, &TO); if(TO.uri.len <= 0) { LM_ERR("'To' header NOT parsed\n"); goto error; } pto = &TO; } if( pto->tag_value.s ==NULL || pto->tag_value.len == 0) { LM_ERR("no to tag value present\n"); goto error; } hentity->to_tag= pto->tag_value; } if(ps->code >= 300 ) { /* if an error code and a stored dialog delete it and try to send a subscription with type= INSERT_TYPE, else return*/ subs_info_t subs; hentity->to_tag.s = NULL; hentity->to_tag.len = 0; find_and_delete_dialog(hentity, hash_code); if (dbmode == PUA_DB_ONLY && pua_dbf.end_transaction) { if (pua_dbf.end_transaction(pua_db) < 0) { LM_ERR("in end_transaction\n"); goto error; } } end_transaction = 0; /* Redirect if the response 3XX */ memset(&subs, 0, sizeof(subs_info_t)); subs.pres_uri= hentity->pres_uri; subs.watcher_uri= hentity->watcher_uri; subs.contact= &hentity->contact; if(hentity->remote_contact.s) subs.remote_target= &hentity->remote_contact; if(hentity->desired_expires== 0) subs.expires= -1; else if(hentity->desired_expires< (int)time(NULL)) subs.expires= 0; else subs.expires= hentity->desired_expires- (int)time(NULL)+ 3; subs.flag= INSERT_TYPE; subs.source_flag= flag; subs.event= hentity->event; subs.id= hentity->id; subs.outbound_proxy= hentity->outbound_proxy; subs.extra_headers= hentity->extra_headers; subs.cb_param= hentity->cb_param; if(send_subscribe(&subs)< 0) { LM_ERR("when trying to send SUBSCRIBE\n"); goto error; } goto done; } if(lexpire== 0 ) { LM_DBG("lexpire= 0 Delete from hash table"); find_and_delete_dialog(hentity, hash_code); goto done; } /* extract the contact */ if(msg->contact== NULL || msg->contact->body.s== NULL) { LM_ERR("no contact header found"); goto error; } if( parse_contact(msg->contact) <0 ) { LM_ERR(" cannot parse contact header\n"); goto error; } if(msg->contact->parsed == NULL) { LM_ERR("cannot parse contact header\n"); goto error; } contact = ((contact_body_t* )msg->contact->parsed)->contacts->uri; if( msg->cseq==NULL || msg->cseq->body.s==NULL) { LM_ERR("cannot parse cseq header\n"); goto error; } if( str2int( &(get_cseq(msg)->number), &cseq)< 0) { LM_ERR("while converting str to int\n"); goto error; } if(initial_request == 0) { hentity->cseq = cseq; find_and_update_dialog(hentity, hash_code, lexpire, &contact); goto done; } /*process record route and add it to a string*/ if (msg->record_route!=NULL) { rt = print_rr_body(msg->record_route, &record_route, 1, 0); if(rt != 0) { LM_ERR("parsing record route [%d]\n", rt); record_route.s=NULL; record_route.len=0; } } size= sizeof(ua_pres_t)+ 2*sizeof(str)+( pto->uri.len+ pfrom->uri.len+ pto->tag_value.len+ pfrom->tag_value.len +msg->callid->body.len+ record_route.len+ hentity->contact.len+ hentity->id.len )*sizeof(char); if(hentity->extra_headers) size+= sizeof(str)+ hentity->extra_headers->len*sizeof(char); presentity= (ua_pres_t*)shm_malloc(size); if(presentity== NULL) { SHM_MEM_ERROR; goto error; } memset(presentity, 0, size); size= sizeof(ua_pres_t); presentity->pres_uri= (str*)( (char*)presentity+ size); size+= sizeof(str); presentity->pres_uri->s= (char*)presentity+ size; memcpy(presentity->pres_uri->s, pto->uri.s, pto->uri.len); presentity->pres_uri->len= pto->uri.len; size+= pto->uri.len; presentity->watcher_uri= (str*)( (char*)presentity+ size); size+= sizeof(str); presentity->watcher_uri->s= (char*)presentity+ size; memcpy(presentity->watcher_uri->s, pfrom->uri.s, pfrom->uri.len); presentity->watcher_uri->len= pfrom->uri.len; size+= pfrom->uri.len; presentity->call_id.s= (char*)presentity + size; memcpy(presentity->call_id.s,msg->callid->body.s, msg->callid->body.len); presentity->call_id.len= msg->callid->body.len; size+= presentity->call_id.len; presentity->to_tag.s= (char*)presentity + size; memcpy(presentity->to_tag.s,pto->tag_value.s, pto->tag_value.len); presentity->to_tag.len= pto->tag_value.len; size+= pto->tag_value.len; presentity->from_tag.s= (char*)presentity + size; memcpy(presentity->from_tag.s,pfrom->tag_value.s, pfrom->tag_value.len); presentity->from_tag.len= pfrom->tag_value.len; size+= pfrom->tag_value.len; if(record_route.len && record_route.s) { presentity->record_route.s= (char*)presentity + size; memcpy(presentity->record_route.s, record_route.s, record_route.len); presentity->record_route.len= record_route.len; size+= record_route.len; pkg_free(record_route.s); record_route.s = NULL; } presentity->contact.s= (char*)presentity + size; memcpy(presentity->contact.s, hentity->contact.s, hentity->contact.len); presentity->contact.len= hentity->contact.len; size+= hentity->contact.len; if(hentity->id.s) { presentity->id.s=(char*)presentity+ size; memcpy(presentity->id.s, hentity->id.s, hentity->id.len); presentity->id.len= hentity->id.len; size+= presentity->id.len; } if(hentity->extra_headers) { presentity->extra_headers= (str*)((char*)presentity+ size); size+= sizeof(str); presentity->extra_headers->s=(char*)presentity+ size; memcpy(presentity->extra_headers->s, hentity->extra_headers->s, hentity->extra_headers->len); presentity->extra_headers->len= hentity->extra_headers->len; size+= hentity->extra_headers->len; } /* write the remote contact filed */ presentity->remote_contact.s= (char*)shm_malloc(contact.len* sizeof(char)); if(presentity->remote_contact.s==NULL) { ERR_MEM(SHARE_MEM); } memcpy(presentity->remote_contact.s, contact.s, contact.len); presentity->remote_contact.len= contact.len; presentity->event|= hentity->event; presentity->flag= hentity->flag; presentity->etag.s= NULL; presentity->cseq= cseq; presentity->desired_expires= hentity->desired_expires; presentity->expires= lexpire+ (int)time(NULL); if(BLA_SUBSCRIBE & presentity->flag) { LM_DBG("BLA_SUBSCRIBE FLAG inserted\n"); } LM_DBG("record for subscribe from %.*s to %.*s inserted in database\n", presentity->watcher_uri->len, presentity->watcher_uri->s, presentity->pres_uri->len, presentity->pres_uri->s); if (dbmode==PUA_DB_ONLY) { if (pua_dbf.end_transaction) { if (pua_dbf.end_transaction(pua_db) < 0) { LM_ERR("in end_transaction\n"); goto error; } } if (pua_dbf.start_transaction) { if (pua_dbf.start_transaction(pua_db, db_table_lock) < 0) { LM_ERR("in start_transaction\n"); goto error; } } if (convert_temporary_dialog_puadb(presentity) < 0) { LM_ERR("Could not convert temporary dialog into a dialog\n"); goto error; } } else { if (convert_temporary_dialog(presentity) < 0) { LM_ERR("Could not convert temporary dialog into a dialog\n"); goto error; } } done: if(hentity->ua_flag == REQ_OTHER) { hentity->flag= flag; run_pua_callbacks( hentity, msg); } if (dbmode == PUA_DB_ONLY && pua_dbf.end_transaction && end_transaction) { if (pua_dbf.end_transaction(pua_db) < 0) { LM_ERR("in end_transaction\n"); goto error; } } goto end; error: if (presentity) { if (presentity->remote_contact.s) shm_free(presentity->remote_contact.s); shm_free(presentity); } if(record_route.s) pkg_free(record_route.s); if (dbmode == PUA_DB_ONLY && pua_dbf.abort_transaction) { if (pua_dbf.abort_transaction(pua_db) < 0) LM_ERR("in abort_transaction\n"); } end: if(hentity) { shm_free(hentity); hentity= NULL; } free_to_params(&TO); return; } ua_pres_t* subscribe_cbparam(subs_info_t* subs, int ua_flag) { ua_pres_t* hentity= NULL; int size; size= sizeof(ua_pres_t)+ 2*sizeof(str)+(subs->pres_uri->len+ subs->watcher_uri->len+ subs->contact->len+ subs->id.len+ 1)* sizeof(char); if(subs->outbound_proxy && subs->outbound_proxy->len && subs->outbound_proxy->s ) size+= sizeof(str)+ subs->outbound_proxy->len* sizeof(char); if(subs->extra_headers && subs->extra_headers->s) size+= sizeof(str)+ subs->extra_headers->len* sizeof(char); hentity= (ua_pres_t*)shm_malloc(size); if(hentity== NULL) { SHM_MEM_ERROR; return NULL; } memset(hentity, 0, size); size= sizeof(ua_pres_t); hentity->pres_uri = (str*)((char*)hentity + size); size+= sizeof(str); hentity->pres_uri->s = (char*)hentity+ size; memcpy(hentity->pres_uri->s, subs->pres_uri->s , subs->pres_uri->len ) ; hentity->pres_uri->len= subs->pres_uri->len; size+= subs->pres_uri->len; hentity->watcher_uri = (str*)((char*)hentity + size); size+= sizeof(str); hentity->watcher_uri->s = (char*)hentity+ size; memcpy(hentity->watcher_uri->s, subs->watcher_uri->s , subs->watcher_uri->len ) ; hentity->watcher_uri->len= subs->watcher_uri->len; size+= subs->watcher_uri->len; hentity->contact.s = (char*)hentity+ size; memcpy(hentity->contact.s, subs->contact->s , subs->contact->len ); hentity->contact.len= subs->contact->len; size+= subs->contact->len; if(subs->outbound_proxy && subs->outbound_proxy->s) { hentity->outbound_proxy= (str*)((char*)hentity+ size); size+= sizeof(str); hentity->outbound_proxy->s= (char*)hentity+ size; memcpy(hentity->outbound_proxy->s, subs->outbound_proxy->s, subs->outbound_proxy->len); hentity->outbound_proxy->len= subs->outbound_proxy->len; size+= subs->outbound_proxy->len; } if(subs->expires< 0) hentity->desired_expires= 0; else hentity->desired_expires=subs->expires+ (int)time(NULL); if(subs->id.s) { CONT_COPY(hentity, hentity->id, subs->id); } if(subs->extra_headers && subs->extra_headers->s) { hentity->extra_headers= (str*)((char*)hentity+ size); size+= sizeof(str); hentity->extra_headers->s= (char*)hentity+ size; memcpy(hentity->extra_headers->s, subs->extra_headers->s, subs->extra_headers->len); hentity->extra_headers->len= subs->extra_headers->len; size+= subs->extra_headers->len; } hentity->flag= subs->source_flag; hentity->event= subs->event; hentity->ua_flag= ua_flag; hentity->cb_param= subs->cb_param; return hentity; } ua_pres_t* subs_cbparam_indlg(ua_pres_t* subs, int expires, int ua_flag) { ua_pres_t* hentity= NULL; int size; size= sizeof(ua_pres_t)+ 2*sizeof(str)+subs->pres_uri->len+ subs->watcher_uri->len+ subs->contact.len+ subs->id.len+ subs->to_tag.len+ subs->call_id.len+ subs->from_tag.len+ 1; if(subs->outbound_proxy && subs->outbound_proxy->len && subs->outbound_proxy->s ) size+= sizeof(str)+ subs->outbound_proxy->len; if(subs->extra_headers && subs->extra_headers->s) size+= sizeof(str)+ subs->extra_headers->len; if(subs->remote_contact.s) size+= subs->remote_contact.len; hentity= (ua_pres_t*)shm_malloc(size); if(hentity== NULL) { SHM_MEM_ERROR; return NULL; } memset(hentity, 0, size); size= sizeof(ua_pres_t); hentity->pres_uri = (str*)((char*)hentity + size); size+= sizeof(str); hentity->pres_uri->s = (char*)hentity+ size; memcpy(hentity->pres_uri->s, subs->pres_uri->s , subs->pres_uri->len ) ; hentity->pres_uri->len= subs->pres_uri->len; size+= subs->pres_uri->len; hentity->watcher_uri = (str*)((char*)hentity + size); size+= sizeof(str); hentity->watcher_uri->s = (char*)hentity+ size; memcpy(hentity->watcher_uri->s, subs->watcher_uri->s , subs->watcher_uri->len ) ; hentity->watcher_uri->len= subs->watcher_uri->len; size+= subs->watcher_uri->len; CONT_COPY(hentity, hentity->contact, subs->contact); if(subs->outbound_proxy && subs->outbound_proxy->len && subs->outbound_proxy->s) { hentity->outbound_proxy= (str*)((char*)hentity+ size); size+= sizeof(str); hentity->outbound_proxy->s= (char*)hentity+ size; memcpy(hentity->outbound_proxy->s, subs->outbound_proxy->s, subs->outbound_proxy->len); hentity->outbound_proxy->len= subs->outbound_proxy->len; size+= subs->outbound_proxy->len; } if(subs->id.s) { CONT_COPY(hentity, hentity->id, subs->id); } if(subs->remote_contact.s) { CONT_COPY(hentity, hentity->remote_contact, subs->remote_contact); } if(subs->extra_headers && subs->extra_headers->s) { hentity->extra_headers= (str*)((char*)hentity+ size); size+= sizeof(str); hentity->extra_headers->s= (char*)hentity+ size; memcpy(hentity->extra_headers->s, subs->extra_headers->s, subs->extra_headers->len); hentity->extra_headers->len= subs->extra_headers->len; size+= subs->extra_headers->len; } /* copy dialog information */ CONT_COPY(hentity, hentity->to_tag, subs->to_tag); CONT_COPY(hentity, hentity->from_tag, subs->from_tag); CONT_COPY(hentity, hentity->call_id, subs->call_id); if(expires< 0) hentity->desired_expires= 0; else hentity->desired_expires=expires+ (int)time(NULL); hentity->flag= subs->flag; hentity->event= subs->event; hentity->ua_flag= ua_flag; hentity->cb_param= subs->cb_param; return hentity; } /** * */ int send_subscribe(subs_info_t* subs) { ua_pres_t* presentity= NULL; str met= {"SUBSCRIBE", 9}; str* str_hdr= NULL; int ret= -1; unsigned int hash_code=0; ua_pres_t* hentity= NULL; int expires; int flag; int result; uac_req_t uac_r; db1_res_t *res=NULL; ua_pres_t dbpres; str pres_uri={0,0}, watcher_uri={0,0}, extra_headers={0,0}; dlg_t* td= NULL; memset(&dbpres, 0, sizeof(dbpres)); dbpres.pres_uri = &pres_uri; dbpres.watcher_uri = &watcher_uri; dbpres.extra_headers = &extra_headers; print_subs(subs); flag= subs->source_flag; if(subs->source_flag & XMPP_INITIAL_SUBS) subs->source_flag= XMPP_SUBSCRIBE; if(subs->expires< 0) expires= pua_default_expires; else expires= subs->expires; str_hdr= subs_build_hdr(subs->contact, expires, subs->event, subs->extra_headers); if(str_hdr== NULL || str_hdr->s== NULL) { LM_ERR("while building extra headers\n"); if(str_hdr) pkg_free(str_hdr); return -1; } if (dbmode == PUA_DB_ONLY && pua_dbf.start_transaction) { if (pua_dbf.start_transaction(pua_db, db_table_lock) < 0) { LM_ERR("in start_transaction\n"); goto error; } } /* generation of hash and getting lock moved from here to further down */ if (dbmode==PUA_DB_ONLY) { presentity = get_dialog_puadb(subs->id, subs->pres_uri, &dbpres, &res); } else { ua_pres_t pres; memset(&pres, 0, sizeof(ua_pres_t)); pres.pres_uri = subs->pres_uri; pres.watcher_uri = subs->watcher_uri; pres.flag = subs->source_flag; pres.id = subs->id; pres.event = subs->event; if (subs->remote_target) pres.remote_contact = *subs->remote_target; hash_code=core_hash(subs->pres_uri, subs->watcher_uri, HASH_SIZE); lock_get(&HashT->p_records[hash_code].lock); presentity= search_htable(&pres, hash_code); } /* if flag == INSERT_TYPE insert no matter what the search result is */ if(subs->flag & INSERT_TYPE) { LM_DBG("A subscription request with insert type\n"); goto insert; } if(presentity== NULL ) { int size; insert: if (subs->expires == 0) { /* Don't create a new dialog when expires == 0 */ if (dbmode != PUA_DB_ONLY) lock_release(&HashT->p_records[hash_code].lock); goto done; } if(subs->flag & UPDATE_TYPE) { LM_DBG("request for a subscription with update type" " and no record found\n"); subs->flag= INSERT_TYPE; } hentity= subscribe_cbparam(subs, REQ_OTHER); if(hentity== NULL) { LM_ERR("while building callback" " param\n"); if (dbmode != PUA_DB_ONLY) lock_release(&HashT->p_records[hash_code].lock); goto error; } hentity->flag= flag; set_uac_req(&uac_r, &met, str_hdr, 0, 0, TMCB_LOCAL_COMPLETED, subs_cback_func, (void*)hentity); result= tmb.t_request_outside (&uac_r, /* Type of the message */ subs->remote_target?subs->remote_target:subs->pres_uri,/* Request-URI*/ subs->pres_uri, /* To */ subs->watcher_uri, /* From */ subs->outbound_proxy /* Outbound_proxy */ ); if(result< 0) { LM_ERR("while sending request with t_request\n"); if (uac_r.dialog != NULL) { uac_r.dialog->rem_target.s = 0; uac_r.dialog->dst_uri.s = 0; tmb.free_dlg(uac_r.dialog); uac_r.dialog = 0; } shm_free(hentity); if (dbmode != PUA_DB_ONLY) lock_release(&HashT->p_records[hash_code].lock); /* Although this is an error must not return -1 as the calling function must continue processing. */ ret = 0; goto error; } /* Now create a temporary hash table entry. This is needed to deal with the race-hazard when NOTIFYs arrive before the 2xx response to the SUBSCRIBE. */ size = sizeof(ua_pres_t)+ 2 * sizeof(str) + ( subs->pres_uri->len + subs->watcher_uri->len + uac_r.dialog->id.loc_tag.len + uac_r.dialog->id.call_id.len + subs->id.len) * sizeof(char); presentity= (ua_pres_t*)shm_malloc(size); if(presentity== NULL) { SHM_MEM_ERROR; if (dbmode != PUA_DB_ONLY) lock_release(&HashT->p_records[hash_code].lock); goto error; } memset(presentity, 0, size); size= sizeof(ua_pres_t); presentity->pres_uri = (str *) ((char *) presentity + size); size += sizeof(str); presentity->pres_uri->s= (char *) presentity + size; memcpy(presentity->pres_uri->s, subs->pres_uri->s, subs->pres_uri->len); presentity->pres_uri->len= subs->pres_uri->len; size+= subs->pres_uri->len; presentity->watcher_uri= (str *) ((char *) presentity + size); size += sizeof(str); presentity->watcher_uri->s= (char *) presentity + size; memcpy(presentity->watcher_uri->s, subs->watcher_uri->s, subs->watcher_uri->len); presentity->watcher_uri->len = subs->watcher_uri->len; size += subs->watcher_uri->len; presentity->call_id.s = (char *) presentity + size; memcpy(presentity->call_id.s, uac_r.dialog->id.call_id.s, uac_r.dialog->id.call_id.len); presentity->call_id.len = uac_r.dialog->id.call_id.len; size += uac_r.dialog->id.call_id.len; presentity->from_tag.s = (char *) presentity + size; memcpy(presentity->from_tag.s, uac_r.dialog->id.loc_tag.s, uac_r.dialog->id.loc_tag.len); presentity->from_tag.len= uac_r.dialog->id.loc_tag.len; size += uac_r.dialog->id.loc_tag.len; presentity->id.s = (char *) presentity+ size; memcpy(presentity->id.s, subs->id.s, subs->id.len); presentity->id.len = subs->id.len; size += subs->id.len; presentity->event = subs->event; presentity->flag = subs->source_flag; presentity->cseq = uac_r.dialog->loc_seq.value; /* Set the temporary record expiry for 2 * 64T1 seconds from now */ presentity->expires= (int)time(NULL) + 64; presentity->desired_expires= presentity->expires; if (dbmode==PUA_DB_ONLY) { insert_dialog_puadb(presentity); shm_free(presentity); } else { insert_htable(presentity, hash_code); lock_release(&HashT->p_records[hash_code].lock); } uac_r.dialog->rem_target.s = 0; uac_r.dialog->dst_uri.s = 0; tmb.free_dlg(uac_r.dialog); uac_r.dialog = 0; } else { if (subs->internal_update_flag == INTERNAL_UPDATE_TRUE) { LM_INFO("attempting to re-SUBSCRIBE on internal (rls_update_subs()) update - skipping\n"); if (dbmode != PUA_DB_ONLY) lock_release(&HashT->p_records[hash_code].lock); goto done; } if (presentity->to_tag.len == 0) { if (subs->expires > 0) LM_WARN("attempting to re-SUBSCRIBE to a temporary (non-established) dialog - skipping\n"); else { LM_WARN("attempting to un-SUBSCRIBE from a temporary (non-established) dialog - skipping and deleting dialog\n"); if (dbmode==PUA_DB_ONLY) delete_dialog_puadb(presentity); else delete_htable(presentity, hash_code); } if (dbmode != PUA_DB_ONLY) lock_release(&HashT->p_records[hash_code].lock); goto done; } td= pua_build_dlg_t(presentity); if(td== NULL) { LM_ERR("while building tm dlg_t structure"); if (dbmode!=PUA_DB_ONLY) lock_release(&HashT->p_records[hash_code].lock); goto error; } hentity= subs_cbparam_indlg(presentity, expires, REQ_OTHER); if(hentity== NULL) { LM_ERR("while building callback param\n"); if (dbmode!=PUA_DB_ONLY) lock_release(&HashT->p_records[hash_code].lock); goto error; } if (dbmode!=PUA_DB_ONLY) lock_release(&HashT->p_records[hash_code].lock); LM_DBG("event parameter: %d\n", hentity->event); set_uac_req(&uac_r, &met, str_hdr, 0, td, TMCB_LOCAL_COMPLETED, subs_cback_func, (void*)hentity); result= tmb.t_request_within(&uac_r); if(result< 0) { shm_free(hentity); hentity= NULL; LM_ERR("while sending request with t_request\n"); goto error; } } done: if (dbmode == PUA_DB_ONLY && pua_dbf.end_transaction) { if (pua_dbf.end_transaction(pua_db) < 0) { LM_ERR("in end_transaction\n"); goto error; } } ret = 0; error: pua_free_tm_dlg(td); pkg_free(str_hdr); free_results_puadb(res); if (dbmode == PUA_DB_ONLY && pua_dbf.abort_transaction) { if (pua_dbf.abort_transaction(pua_db) < 0) LM_ERR("in abort_transaction\n"); } return ret; }