/** * Drop all dialogs belonging to one AOR. * on deregistration for example. * @param aor - the public identity of the user * @returns the number of dialogs dropped */ int S_drop_all_dialogs(str aor) { s_dialog *d,*dn; int i,cnt=0;; LOG(L_DBG,"DBG:"M_NAME":S_drop_all_dialogs: Called for <%.*s>\n",aor.len,aor.s); for(i=0;i<s_dialogs_hash_size;i++){ d_lock(i); d = s_dialogs[i].head; while(d){ dn = d->next; if (d->direction == DLG_MOBILE_ORIGINATING && d->aor.len == aor.len && strncasecmp(d->aor.s,aor.s,aor.len)==0) { if (!terminate_s_dialog(d)) del_s_dialog(d); cnt++; } d = dn; } d_unlock(i); } //print_s_dialogs(L_INFO); return cnt; }
/** * Given an s_dialog, releases the call. * This function is already called with a lock in d * after returning d should be unlocked. * @param d - pointer to the s_dialog structure * @param reason - Reason header to include * @returns -1 if dialog the dialog is not confirmed, 0 on error or 1 on success */ int release_call_s(s_dialog *d,str reason) { enum s_dialog_direction odir; s_dialog *o; LOG(L_INFO,"DBG:"M_NAME":release_call_s(): Releasing call <%.*s> DIR[%d].\n", d->call_id.len,d->call_id.s,d->direction); /* As for now, i'm only releasing confirmed dialogs */ if (d->state < DLG_STATE_CONFIRMED){ LOG(L_INFO,"ERR:"M_NAME":release_call_s(): Unable to release a non-confirmed dialog\n"); return -1; } /* get the dialog in the other direction to see if something going on there and mark as in releasing */ switch (d->direction){ case DLG_MOBILE_ORIGINATING: odir = DLG_MOBILE_TERMINATING; break; case DLG_MOBILE_TERMINATING: odir = DLG_MOBILE_ORIGINATING; break; default: odir = d->direction; } o = get_s_dialog_dir_nolock(d->call_id,odir); if (o && !o->is_releasing) o->is_releasing = 1; d->is_releasing++; if (d->is_releasing>MAX_TIMES_TO_TRY_TO_RELEASE){ LOG(L_ERR,"ERR:"M_NAME":release_call_s(): had to delete silently dialog %.*s in direction %i\n",d->call_id.len,d->call_id.s,d->direction); del_s_dialog(d); return 0; } if (d->is_releasing==1) { /*Before generating a request, we have to generate * the route_set in the dlg , because the route set * in the dialog is for the UAC everything which was in the * Record-Routes (including local address)*/ alter_dialog_route_set(d->dialog_c,d->direction); /*first generate the bye for called user*/ /*then generate the bye for the calling user*/ send_bye(d->dialog_c,bye_response,d->direction,reason); send_bye(d->dialog_s,bye_response,d->direction,reason); /*the dialog is droped by the callback-function when recieves the two replies */ } return 1; }
/** * Terminates a dialog - called before del_s_dialog to send out terminatination messages. * @param d - the dialog to terminate * @returns - 1 if the requests were sent and the dialog will be deleted, 0 on error (you will have to delete the * dialog yourself!) */ int terminate_s_dialog(s_dialog *d) { if (!scscf_dialogs_enable_release) return 0; switch (d->method){ case DLG_METHOD_INVITE: if (release_call_s(d,Reason)<=0){ //dialog has expired and not confirmed // or error releasing dialog del_s_dialog(d); } return 1; break; case DLG_METHOD_SUBSCRIBE: if (!release_subscription(d)){ //error releasing the subscription - just drop silently del_s_dialog(d); } return 1; break; default: LOG(L_ERR,"ERR:"M_NAME":terminate_s_dialog(): Not implemented yet for method[%d]!\n",d->method); return 0; } }
/** * Drops and deletes a dialog. * @param msg - the request/response * @param str1 - direction - "orig" or "term" * @param str2 - not used * @returns #CSCF_RETURN_TRUE if ok, #CSCF_RETURN_FALSE if not or #CSCF_RETURN_BREAK on error */ int S_drop_dialog(struct sip_msg* msg, char* str1, char* str2) { str call_id; s_dialog *d; int hash; // struct sip_msg *req; enum s_dialog_direction dir = get_dialog_direction(str1); // if (!find_dialog_aor(msg,str1,&aor)){ // LOG(L_ERR,"ERR:"M_NAME":S_is_in_dialog(): Error retrieving %s contact\n",str1); // return CSCF_RETURN_BREAK; // } call_id = cscf_get_call_id(msg,0); if (!call_id.len) return CSCF_RETURN_FALSE; LOG(L_DBG,"DBG:"M_NAME":S_drop_dialog(%s): Call-ID <%.*s> DIR[%d]\n", str1,call_id.len,call_id.s, dir); d = get_s_dialog_dir(call_id,dir); // if (!d && msg->first_line.type==SIP_REPLY){ // /* Try to get the dialog from the request */ // req = cscf_get_request_from_reply(msg); // if (!find_dialog_aor(req,str1,&aor)){ // LOG(L_ERR,"ERR:"M_NAME":S_update_dialog(%s): Error retrieving %s contact\n",str1,str1); // return CSCF_RETURN_BREAK; // } // d = get_s_dialog(call_id,aor); // } if (!d){ LOG(L_ERR,"ERR:"M_NAME":S_drop_dialog: dialog does not exists!\n"); return CSCF_RETURN_FALSE; } hash = d->hash; del_s_dialog(d); d_unlock(hash); print_s_dialogs(L_INFO); return CSCF_RETURN_TRUE; }
/** * Terminates a dialog - called before del_s_dialog to send out terminatination messages. * @param d - the dialog to terminate * @returns - 1 if the requests were sent and the dialog will be deleted, 0 on error (you will have to delete the * dialog yourself!) */ int terminate_s_dialog(s_dialog *d) { switch (d->method){ case DLG_METHOD_INVITE: if (release_call_s(d)==-1){ //dialog has expired and not confirmed del_s_dialog(d); } return 1; break; case DLG_METHOD_SUBSCRIBE: LOG(L_ERR,"ERR:"M_NAME":terminate_s_dialog(): Not implemented yet for SUBSCRIBE dialogs!\n"); return 0; break; default: LOG(L_ERR,"ERR:"M_NAME":terminate_s_dialog(): Not implemented yet for method[%d]!\n",d->method); return 0; } }
/** * Callback function for BYE requests! * Identify the s_dialog, then see if one BYE has already been recieved * if yes drop it , if no, wait for the second */ void bye_response(struct cell *t,int type,struct tmcb_params *ps) { s_dialog *d; unsigned int hash; str call_id; enum s_dialog_direction dir; if (!ps->param) return; dir = *((enum s_dialog_direction *) *(ps->param)); shm_free(*ps->param); *ps->param = 0; call_id = t->callid; call_id.s+=9; call_id.len-=11; LOG(L_INFO,"DBG:"M_NAME":bye_response(): Received a %d response to BYE for a call release for <%.*s> DIR[%d].\n", ps->code, call_id.len,call_id.s,dir); d = get_s_dialog_dir(call_id,dir); if (!d) { LOG(L_ERR,"ERR:"M_NAME":bye_response(): Received a BYE response for a call release but there is no dialog for <%.*s> DIR[%d].\n", call_id.len,call_id.s,dir); return; } if (ps->code>=200){ if (d->state==DLG_STATE_TERMINATED_ONE_SIDE){ hash=d->hash; LOG(L_INFO,"INFO:"M_NAME":bye_response(): Received a response to second BYE. Dialog is dropped.\n"); del_s_dialog(d); d_unlock(hash); } else { hash=d->hash; d->state=DLG_STATE_TERMINATED_ONE_SIDE; d_unlock(hash); } } }
/** * The dialog timer looks for expires dialogs and removes them * @param ticks - the current time * @param param - pointer to the dialogs list */ void dialog_timer(unsigned int ticks, void* param) { s_dialog *d,*dn; int i; #ifdef WITH_IMS_PM int dialog_cnt[DLG_METHOD_MAX+1]; for(i=0;i<=DLG_METHOD_MAX;i++) dialog_cnt[i]=0; #endif LOG(L_DBG,"DBG:"M_NAME":dialog_timer: Called at %d\n",ticks); if (!s_dialogs) s_dialogs = (s_dialog_hash_slot*)param; d_act_time(); for(i=0;i<s_dialogs_hash_size;i++){ d_lock(i); d = s_dialogs[i].head; while(d){ dn = d->next; if (d->expires<=d_time_now) { if (!terminate_s_dialog(d)) del_s_dialog(d); } #ifdef WITH_IMS_PM else dialog_cnt[d->method]++; #endif d = dn; } d_unlock(i); } print_s_dialogs(L_INFO); #ifdef WITH_IMS_PM for(i=0;i<=DLG_METHOD_MAX;i++) IMS_PM_LOG11(RD_NbrDialogs,get_dialog_method_str(i),dialog_cnt[i]); #endif }
/** * The dialog timer looks for expires dialogs and removes them * @param ticks - the current time * @param param - pointer to the dialogs list */ void dialog_timer(unsigned int ticks, void* param) { s_dialog *d,*dn; int i; LOG(L_DBG,"DBG:"M_NAME":dialog_timer: Called at %d\n",ticks); if (!s_dialogs) s_dialogs = (s_dialog_hash_slot*)param; d_act_time(); for(i=0;i<s_dialogs_hash_size;i++){ d_lock(i); d = s_dialogs[i].head; while(d){ dn = d->next; if (d->expires<=d_time_now) { if (!terminate_s_dialog(d)) del_s_dialog(d); } d = dn; } d_unlock(i); } print_s_dialogs(L_INFO); }
/** * Saves a dialog. * @param msg - the initial request * @param str1 - direction - "orig" or "term" * @param str2 - not used * @returns #CSCF_RETURN_TRUE if ok, #CSCF_RETURN_FALSE if not or #CSCF_RETURN_BREAK on error */ int S_save_dialog(struct sip_msg* msg, char* str1, char* str2) { str call_id; s_dialog *d; str aor; char buf1[256],buf2[256]; str uri,tag,ruri,x; time_t t_time; str ses_exp = {0,0}; str refresher = {0,0}; str event = {0,0}; struct hdr_field *h; unsigned int hash; enum s_dialog_direction dir = get_dialog_direction(str1); if (!find_dialog_aor(msg,dir,&aor)){ LOG(L_ERR,"ERR:"M_NAME":S_save_dialog(): Error retrieving %s contact\n",str1); return CSCF_RETURN_BREAK; } call_id = cscf_get_call_id(msg,0); if (!call_id.len) return CSCF_RETURN_FALSE; LOG(L_INFO,"DBG:"M_NAME":S_save_dialog(%s): Call-ID <%.*s>\n",str1,call_id.len,call_id.s); if (is_s_dialog(call_id,aor,dir)){ LOG(L_ERR,"ERR:"M_NAME":S_save_dialog: dialog already exists!\n"); return CSCF_RETURN_TRUE; } d = add_s_dialog(call_id,aor,dir); if (!d) return CSCF_RETURN_FALSE; d->method = get_dialog_method(msg->first_line.u.request.method); STR_SHM_DUP(d->method_str,msg->first_line.u.request.method,"shm"); d->first_cseq = cscf_get_cseq(msg,0); d->last_cseq = d->first_cseq; d->state = DLG_STATE_INITIAL; d->uac_supp_timer = supports_extension(msg, &str_ext_timer); ses_exp = cscf_get_session_expires_body(msg, &h); t_time = cscf_get_session_expires(ses_exp, &refresher); if (!t_time) { d->expires = d_act_time() + 60; d->lr_session_expires = 0; } else { d->expires = d_act_time() + t_time; d->lr_session_expires = t_time; if (refresher.len) STR_SHM_DUP(d->refresher, refresher, "DIALOG_REFRESHER"); } cscf_get_from_tag(msg,&tag); cscf_get_from_uri(msg,&x); uri.len = snprintf(buf1,256,"<%.*s>",x.len,x.s); uri.s = buf1; cscf_get_to_uri(msg,&x); ruri.len = snprintf(buf2,256,"<%.*s>",x.len,x.s); ruri.s = buf2; tmb.new_dlg_uac(&call_id, &tag, d->first_cseq,&uri, &ruri, &d->dialog_c); tmb.new_dlg_uas(msg,99,&d->dialog_s); event = cscf_get_event(msg); if (event.len){ STR_SHM_DUP(d->event,event,"shm"); } else d->event = event; d_unlock(d->hash); print_s_dialogs(L_INFO); return CSCF_RETURN_TRUE; out_of_memory: if (d){ hash = d->hash; del_s_dialog(d); d_unlock(hash); } return CSCF_RETURN_ERROR; }