diff options
author | bossiel <bossiel@yahoo.fr> | 2011-08-10 22:59:15 +0000 |
---|---|---|
committer | bossiel <bossiel@yahoo.fr> | 2011-08-10 22:59:15 +0000 |
commit | 9350baf5a3444cc035bd5eedcded04b1fe823228 (patch) | |
tree | fa358e21d292a0d71297803f8a4b6d8d681d3139 /branches/1.0/tinySIP/src/dialogs/tsip_dialog_layer.c | |
parent | b7cca8f4573552bc6950a5c289b9578511557d8f (diff) | |
download | doubango-9350baf5a3444cc035bd5eedcded04b1fe823228.zip doubango-9350baf5a3444cc035bd5eedcded04b1fe823228.tar.gz |
Move deprecated v1.0 from trunk to branches
Diffstat (limited to 'branches/1.0/tinySIP/src/dialogs/tsip_dialog_layer.c')
-rw-r--r-- | branches/1.0/tinySIP/src/dialogs/tsip_dialog_layer.c | 500 |
1 files changed, 500 insertions, 0 deletions
diff --git a/branches/1.0/tinySIP/src/dialogs/tsip_dialog_layer.c b/branches/1.0/tinySIP/src/dialogs/tsip_dialog_layer.c new file mode 100644 index 0000000..959f3dd --- /dev/null +++ b/branches/1.0/tinySIP/src/dialogs/tsip_dialog_layer.c @@ -0,0 +1,500 @@ +/* +* Copyright (C) 2009-2010 Mamadou Diop. +* +* Contact: Mamadou Diop <diopmamadou(at)doubango.org> +* +* This file is part of Open Source Doubango Framework. +* +* DOUBANGO 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 3 of the License, or +* (at your option) any later version. +* +* DOUBANGO 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 DOUBANGO. +* +*/ + +/**@file tsip_dialog_layer.c + * @brief SIP dialog layer. + * + * @author Mamadou Diop <diopmamadou(at)doubango.org> + * + * @date Created: Sat Nov 8 16:54:58 2009 mdiop + */ +#include "tinysip/dialogs/tsip_dialog_layer.h" + +#include "tinysip/dialogs/tsip_dialog_invite.h" +#include "tinysip/dialogs/tsip_dialog_message.h" +#include "tinysip/dialogs/tsip_dialog_options.h" +#include "tinysip/dialogs/tsip_dialog_publish.h" +#include "tinysip/dialogs/tsip_dialog_register.h" +#include "tinysip/dialogs/tsip_dialog_subscribe.h" + +#include "tinysip/transactions/tsip_transac_layer.h" +#include "tinysip/transports/tsip_transport_layer.h" + +#include "tsk_debug.h" + +extern tsip_ssession_handle_t *tsip_ssession_create_2(const tsip_stack_t* stack, const struct tsip_message_s* message); + +/*== Predicate function to find dialog by type */ +static int pred_find_dialog_by_type(const tsk_list_item_t *item, const void *type) +{ + if(item && item->data){ + tsip_dialog_t *dialog = item->data; + return (dialog->type - *((tsip_dialog_type_t*)type)); + } + return -1; +} + +/*== Predicate function to find dialog by not type */ +static int pred_find_dialog_by_not_type(const tsk_list_item_t *item, const void *type) +{ + if(item && item->data){ + tsip_dialog_t *dialog = item->data; + if(dialog->type != *((tsip_dialog_type_t*)type)){ + return 0; + } + } + return -1; +} + +tsip_dialog_layer_t* tsip_dialog_layer_create(tsip_stack_t* stack) +{ + return tsk_object_new(tsip_dialog_layer_def_t, stack); +} + +// it's up to the caller to release the returned object +tsip_dialog_t* tsip_dialog_layer_find_by_ss(tsip_dialog_layer_t *self, const tsip_ssession_handle_t *ss) +{ + tsip_dialog_t *ret = 0; + tsip_dialog_t *dialog; + tsk_list_item_t *item; + + tsk_safeobj_lock(self); + + tsk_list_foreach(item, self->dialogs){ + dialog = item->data; + if( tsip_ssession_get_id(dialog->ss) == tsip_ssession_get_id(ss) ){ + ret = dialog; + break; + } + } + + tsk_safeobj_unlock(self); + + return tsk_object_ref(ret); +} + +// it's up to the caller to release the returned object +tsip_dialog_t* tsip_dialog_layer_find(const tsip_dialog_layer_t *self, const char* callid, const char* to_tag, const char* from_tag, tsip_request_type_t type, tsk_bool_t *cid_matched) +{ + tsip_dialog_t *ret = tsk_null; + tsip_dialog_t *dialog; + tsk_list_item_t *item; + + *cid_matched = tsk_false; + + tsk_safeobj_lock(self); + + tsk_list_foreach(item, self->dialogs){ + dialog = item->data; + if(tsk_strequals(dialog->callid, callid)){ + tsk_bool_t is_cancel = (type == tsip_CANCEL); // Incoming CANCEL + tsk_bool_t is_register = (type == tsip_REGISTER); // Incoming REGISTER + *cid_matched = tsk_true; + /* CANCEL Request will have the same local tag than the INVITE request -> do not compare tags */ + if((is_cancel || tsk_strequals(dialog->tag_local, from_tag)) && tsk_strequals(dialog->tag_remote, to_tag)){ + ret = tsk_object_ref(dialog); + break; + } + /* REGISTER is dialogless which means that each reREGISTER or unREGISTER will have empty to tag */ + if(is_register /* Do not check tags */){ + ret = tsk_object_ref(dialog); + break; + } + } + } + + tsk_safeobj_unlock(self); + + return ret; +} + +/** Hangup all dialogs staring by REGISTER */ +int tsip_dialog_layer_shutdownAll(tsip_dialog_layer_t *self) +{ + if(self){ + tsk_bool_t wait = tsk_false; + tsk_list_item_t *item; + tsip_dialog_t *dialog; + tsip_dialog_type_t regtype = tsip_dialog_REGISTER; + + if(!self->shutdown.inprogress){ + self->shutdown.inprogress = tsk_true; + self->shutdown.condwait = tsk_condwait_create(); + } + + tsk_safeobj_lock(self); + if(tsk_list_count(self->dialogs, pred_find_dialog_by_not_type, ®type)){ + /* There are non-register dialogs ==> phase-1 */ + goto phase1; + } + else if(tsk_list_count(self->dialogs, pred_find_dialog_by_type, ®type)){ + /* There are one or more register dialogs ==> phase-2 */ + goto phase2; + } + else{ + tsk_safeobj_unlock(self); + goto done; + } + +phase1: + /* Phase 1 - shutdown all except register and silent_hangup */ + TSK_DEBUG_INFO("== Shutting down - Phase-1 started =="); +phase1_loop: + tsk_list_foreach(item, self->dialogs){ + dialog = item->data; + if(dialog->type != tsip_dialog_REGISTER && !dialog->ss->silent_hangup){ + item = tsk_object_ref(item); + if(!tsip_dialog_shutdown(dialog, tsk_null)){ + wait = tsk_true; + } + + // if "tsip_dialog_shutdown()" remove the dialog, then + // "self->dialogs" will be unsafe + if(!(item = tsk_object_unref(item))){ + goto phase1_loop; + } + } + } + tsk_safeobj_unlock(self); + + /* wait until phase-1 is completed */ + if(wait){ + tsk_condwait_timedwait(self->shutdown.condwait, TSIP_DIALOG_SHUTDOWN_TIMEOUT); + } + + /* lock and goto phase2 */ + tsk_safeobj_lock(self); + wait = tsk_false; + goto phase2; + +phase2: + /* Phase 2 - unregister */ + TSK_DEBUG_INFO("== Shutting down - Phase-2 started =="); + self->shutdown.phase2 = tsk_true; +phase2_loop: + tsk_list_foreach(item, self->dialogs){ + dialog = item->data; + if(dialog->type == tsip_dialog_REGISTER){ + item = tsk_object_ref(item); + if(!tsip_dialog_shutdown(dialog, tsk_null)){ + wait = tsk_true; + } + // if "tsip_dialog_shutdown()" remove the dialog, then + // "self->dialogs" will be unsafe + if(!(item = tsk_object_unref(item))){ + goto phase2_loop; + } + } + } + tsk_safeobj_unlock(self); + + /* wait until phase-2 is completed */ + if(wait){ + tsk_condwait_timedwait(self->shutdown.condwait, TSIP_DIALOG_SHUTDOWN_TIMEOUT); + } + + + /* Phase 3 - silenthangup (dialogs will be terminated immediately) */ + TSK_DEBUG_INFO("== Shutting down - Phase-3 =="); +phase3_loop: + tsk_list_foreach(item, self->dialogs){ + dialog = item->data; + if(dialog->ss->silent_hangup){ + item = tsk_object_ref(item); + tsip_dialog_shutdown(dialog, tsk_null); + + // if "tsip_dialog_shutdown()" remove the dialog, then + // "self->dialogs" will be unsafe + if(!(item = tsk_object_unref(item))){ + goto phase3_loop; + } + } + } + +done: + return 0; + } + return -1; +} + +/* the caller of this function must unref() the returned object */ +tsip_dialog_t* tsip_dialog_layer_new(tsip_dialog_layer_t *self, tsip_dialog_type_t type, const tsip_ssession_t *ss) +{ + tsip_dialog_t* ret = tsk_null; + tsip_dialog_t* dialog; + if(!self){ + goto bail; + } + + switch(type){ + case tsip_dialog_INVITE: + { + if((dialog = (tsip_dialog_t*)tsip_dialog_invite_create(ss, tsk_null))){ + ret = tsk_object_ref(dialog); + tsk_list_push_back_data(self->dialogs, (void**)&dialog); + } + break; + } + case tsip_dialog_MESSAGE: + { + if((dialog = (tsip_dialog_t*)tsip_dialog_message_create(ss))){ + ret = tsk_object_ref(dialog); + tsk_list_push_back_data(self->dialogs, (void**)&dialog); + } + break; + } + case tsip_dialog_OPTIONS: + { + if((dialog = (tsip_dialog_t*)tsip_dialog_options_create(ss))){ + ret = tsk_object_ref(dialog); + tsk_list_push_back_data(self->dialogs, (void**)&dialog); + } + break; + } + case tsip_dialog_PUBLISH: + { + if((dialog = (tsip_dialog_t*)tsip_dialog_publish_create(ss))){ + ret = tsk_object_ref(dialog); + tsk_list_push_back_data(self->dialogs, (void**)&dialog); + } + break; + } + case tsip_dialog_REGISTER: + { + if((dialog = (tsip_dialog_t*)tsip_dialog_register_create(ss, tsk_null))){ + ret = tsk_object_ref(dialog); + tsk_list_push_back_data(self->dialogs, (void**)&dialog); + } + break; + } + case tsip_dialog_SUBSCRIBE: + { + if((dialog = (tsip_dialog_t*)tsip_dialog_subscribe_create(ss))){ + ret = tsk_object_ref(dialog); + tsk_list_push_back_data(self->dialogs, (void**)&dialog); + } + break; + } + + default: + { + TSK_DEBUG_ERROR("Dialog type not supported."); + break; + } + } + +bail: + return ret; +} + +int tsip_dialog_layer_remove(tsip_dialog_layer_t *self, const tsip_dialog_t *dialog) +{ + if(dialog && self){ + tsip_dialog_type_t regtype = tsip_dialog_REGISTER; + tsk_safeobj_lock(self); + + /* remove the dialog */ + tsk_list_remove_item_by_data(self->dialogs, dialog); + + /* whether shutting down? */ + if(self->shutdown.inprogress){ + if(self->shutdown.phase2){ /* Phase 2 (all non-REGISTER and silent dialogs have been removed) */ + if(tsk_list_count(self->dialogs, pred_find_dialog_by_type, ®type) == 0){ + /* alert only if there is not REGISTER dialog (ignore silents) */ + TSK_DEBUG_INFO("== Shutting down - Phase-2 completed =="); + tsk_condwait_broadcast(self->shutdown.condwait); + } + } + else{ /* Phase 1 */ + if(tsk_list_count(self->dialogs, pred_find_dialog_by_not_type, ®type) == 0){ + /* alert only if all dialogs except REGISTER have been removed */ + TSK_DEBUG_INFO("== Shutting down - Phase-1 completed =="); + tsk_condwait_broadcast(self->shutdown.condwait); + } + } + } + + tsk_safeobj_unlock(self); + + return 0; + } + + return -1; +} + +int tsip_dialog_layer_handle_incoming_msg(const tsip_dialog_layer_t *self, const tsip_message_t* message) +{ + int ret = -1; + tsk_bool_t cid_matched; + tsip_dialog_t* dialog; + tsip_transac_t* transac = tsk_null; + const tsip_transac_layer_t *layer_transac = self->stack->layer_transac; + + if(!layer_transac){ + goto bail; + } + + //tsk_safeobj_lock(self); + dialog = tsip_dialog_layer_find(self, message->Call_ID->value, + TSIP_MESSAGE_IS_RESPONSE(message) ? message->To->tag : message->From->tag, + TSIP_MESSAGE_IS_RESPONSE(message) ? message->From->tag : message->To->tag, + TSIP_MESSAGE_IS_REQUEST(message) ? TSIP_MESSAGE_AS_REQUEST(message)->line.request.request_type : tsip_NONE, + &cid_matched); + //tsk_safeobj_unlock(self); + + if(dialog){ + if(TSIP_REQUEST_IS_CANCEL(message) || TSIP_REQUEST_IS_ACK(message)){ + ret = dialog->callback(dialog, tsip_dialog_i_msg, message); + tsk_object_unref(dialog); + goto bail; + } + else{ + transac = tsip_transac_layer_new(layer_transac, tsk_false, message, TSIP_DIALOG(dialog)); + tsk_object_unref(dialog); + } + } + else{ + if(TSIP_MESSAGE_IS_REQUEST(message)){ + tsip_ssession_t* ss = tsk_null; + tsip_dialog_t* newdialog = tsk_null; + + switch(message->line.request.request_type){ + case tsip_MESSAGE: + { /* Server incoming MESSAGE */ + if((ss = tsip_ssession_create_2(self->stack, message))){ + newdialog = (tsip_dialog_t*)tsip_dialog_message_create(ss); + } + break; + } + + case tsip_REGISTER: + { /* incoming REGISTER */ + if((ss = tsip_ssession_create_2(self->stack, message))){ + newdialog = (tsip_dialog_t*)tsip_dialog_register_create(ss, message->Call_ID ? message->Call_ID->value : tsk_null); + } + break; + } + + case tsip_INVITE: + { /* incoming INVITE */ + if((ss = tsip_ssession_create_2(self->stack, message))){ + newdialog = (tsip_dialog_t*)tsip_dialog_invite_create(ss, message->Call_ID ? message->Call_ID->value : tsk_null); + } + break; + } + + default: + { + break; + } + }//switch + + if(newdialog){ + transac = tsip_transac_layer_new(layer_transac, tsk_false, message, newdialog); + tsk_list_push_back_data(self->dialogs, (void**)&newdialog); /* add new dialog to the layer */ + } + + /* The dialog will become the owner of the SIP session + * => when destoyed => SIP session will be destroyed, unless the user-end takes ownership() */ + TSK_OBJECT_SAFE_FREE(ss); + } + } + + if(transac){ + ret = tsip_transac_start(transac, message); + tsk_object_unref(transac); + } + else if(TSIP_MESSAGE_IS_REQUEST(message)){ /* No transaction match for the SIP request */ + const tsip_transport_layer_t *layer; + tsip_response_t* response = tsk_null; + + if((layer = self->stack->layer_transport)){ + if(cid_matched){ /* We are receiving our own message. */ + response = tsip_response_new(482, "Loop Detected (Check your iFCs)", message); + if(response && !response->To->tag){/* Early dialog? */ + response->To->tag = tsk_strdup("doubango"); + } + } + else{ + response = tsip_response_new(481, "Dialog/Transaction Does Not Exist", message); + } + if(response){ + ret = tsip_transport_layer_send(layer, response->firstVia ? response->firstVia->branch : "no-branch", response); + TSK_OBJECT_SAFE_FREE(response); + } + } + } + +bail: + return ret; +} + + + + + +//======================================================== +// Dialog layer object definition +// +static tsk_object_t* tsip_dialog_layer_ctor(tsk_object_t * self, va_list * app) +{ + tsip_dialog_layer_t *layer = self; + if(layer){ + layer->stack = va_arg(*app, const tsip_stack_t *); + layer->dialogs = tsk_list_create(); + + tsk_safeobj_init(layer); + } + return self; +} + +static tsk_object_t* tsip_dialog_layer_dtor(tsk_object_t * self) +{ + tsip_dialog_layer_t *layer = self; + if(layer){ + TSK_OBJECT_SAFE_FREE(layer->dialogs); + + /* condwait */ + if(layer->shutdown.condwait){ + tsk_condwait_destroy(&layer->shutdown.condwait); + } + + tsk_safeobj_deinit(layer); + + TSK_DEBUG_INFO("*** Dialog Layer destroyed ***"); + } + return self; +} + +static int tsip_dialog_layer_cmp(const tsk_object_t *obj1, const tsk_object_t *obj2) +{ + return -1; +} + +static const tsk_object_def_t tsip_dialog_layer_def_s = +{ + sizeof(tsip_dialog_layer_t), + tsip_dialog_layer_ctor, + tsip_dialog_layer_dtor, + tsip_dialog_layer_cmp, +}; +const tsk_object_def_t *tsip_dialog_layer_def_t = &tsip_dialog_layer_def_s; |