/* $Id$ | |
* | |
*/ | |
/* | |
* PJSIP - SIP Stack | |
* (C)2003-2005 Benny Prijono <bennylp@bulukucing.org> | |
* | |
* Author: | |
* Benny Prijono <bennylp@bulukucing.org> | |
* | |
* This library is free software; you can redistribute it and/or | |
* modify it under the terms of the GNU Lesser General Public | |
* License as published by the Free Software Foundation; either | |
* version 2.1 of the License, or (at your option) any later version. | |
* | |
* This library 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 | |
* Lesser General Public License for more details. | |
* | |
* You should have received a copy of the GNU Lesser General Public | |
* License along with this library; if not, write to the Free Software | |
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
* | |
*/ | |
#include <pjsip_simple/messaging.h> | |
#include <pjsip/sip_endpoint.h> | |
#include <pjsip/sip_parser.h> | |
#include <pjsip/sip_transaction.h> | |
#include <pjsip/sip_event.h> | |
#include <pjsip/sip_module.h> | |
#include <pjsip/sip_misc.h> | |
#include <pj/string.h> | |
#include <pj/pool.h> | |
#include <pj/guid.h> | |
#include <pj/string.h> | |
#include <pj/log.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#define THIS_FILE "messaging" | |
struct messaging_data | |
{ | |
void *token; | |
pjsip_messaging_cb cb; | |
}; | |
struct pjsip_messaging_session | |
{ | |
pj_pool_t *pool; | |
pjsip_endpoint *endpt; | |
pjsip_from_hdr *from; | |
pjsip_to_hdr *to; | |
pjsip_cid_hdr *call_id; | |
pjsip_cseq_hdr *cseq; | |
}; | |
static int module_id; | |
static pjsip_on_new_msg_cb incoming_cb; | |
static pjsip_method message_method; | |
/* | |
* Set global callback to receive incoming message. | |
*/ | |
PJ_DEF(pjsip_on_new_msg_cb) | |
pjsip_messaging_set_incoming_callback(pjsip_on_new_msg_cb cb) | |
{ | |
pjsip_on_new_msg_cb prev_cb = incoming_cb; | |
incoming_cb = cb; | |
return prev_cb; | |
} | |
/* | |
* Create an independent message (ie. not associated with a session). | |
*/ | |
PJ_DEF(pjsip_tx_data*) | |
pjsip_messaging_create_msg_from_hdr(pjsip_endpoint *endpt, | |
const pjsip_uri *target, | |
const pjsip_from_hdr *param_from, | |
const pjsip_to_hdr *param_to, | |
const pjsip_cid_hdr *param_call_id, | |
int param_cseq, | |
const pj_str_t *param_text) | |
{ | |
return pjsip_endpt_create_request_from_hdr( endpt, &message_method, | |
target, | |
param_from, param_to, | |
NULL, param_call_id, | |
param_cseq, param_text ); | |
} | |
/* | |
* Create independent message from string (instead of from header). | |
*/ | |
PJ_DEF(pjsip_tx_data*) | |
pjsip_messaging_create_msg( pjsip_endpoint *endpt, | |
const pj_str_t *target, | |
const pj_str_t *param_from, | |
const pj_str_t *param_to, | |
const pj_str_t *param_call_id, | |
int param_cseq, | |
const pj_str_t *param_text) | |
{ | |
return pjsip_endpt_create_request( endpt, &message_method, target, | |
param_from, param_to, NULL, param_call_id, | |
param_cseq, param_text); | |
} | |
/* | |
* Initiate transaction to send outgoing message. | |
*/ | |
PJ_DEF(pj_status_t) | |
pjsip_messaging_send_msg( pjsip_endpoint *endpt, pjsip_tx_data *tdata, | |
void *token, pjsip_messaging_cb cb ) | |
{ | |
pjsip_transaction *tsx; | |
struct messaging_data *msg_data; | |
/* Create transaction. */ | |
tsx = pjsip_endpt_create_tsx(endpt); | |
if (!tsx) { | |
pjsip_tx_data_dec_ref(tdata); | |
return -1; | |
} | |
/* Save parameters to messaging data and attach to tsx. */ | |
msg_data = pj_pool_calloc(tsx->pool, 1, sizeof(struct messaging_data)); | |
msg_data->cb = cb; | |
msg_data->token = token; | |
/* Init transaction. */ | |
tsx->module_data[module_id] = msg_data; | |
if (pjsip_tsx_init_uac(tsx, tdata) != 0) { | |
pjsip_tx_data_dec_ref(tdata); | |
pjsip_endpt_destroy_tsx(endpt, tsx); | |
return -1; | |
} | |
pjsip_endpt_register_tsx(endpt, tsx); | |
/* | |
* Instruct transaction to send message. | |
* Further events will be received via transaction's event. | |
*/ | |
pjsip_tsx_on_tx_msg(tsx, tdata); | |
/* Decrement reference counter. */ | |
pjsip_tx_data_dec_ref(tdata); | |
return 0; | |
} | |
/* | |
* Create 'IM session'. | |
*/ | |
PJ_DEF(pjsip_messaging_session*) | |
pjsip_messaging_create_session( pjsip_endpoint *endpt, const pj_str_t *param_from, | |
const pj_str_t *param_to ) | |
{ | |
pj_pool_t *pool; | |
pjsip_messaging_session *ses; | |
pj_str_t tmp, to; | |
pool = pjsip_endpt_create_pool(endpt, "imsess", 1024, 1024); | |
if (!pool) | |
return NULL; | |
ses = pj_pool_calloc(pool, 1, sizeof(pjsip_messaging_session)); | |
ses->pool = pool; | |
ses->endpt = endpt; | |
ses->call_id = pjsip_cid_hdr_create(pool); | |
pj_create_unique_string(pool, &ses->call_id->id); | |
ses->cseq = pjsip_cseq_hdr_create(pool); | |
ses->cseq->cseq = pj_rand(); | |
ses->cseq->method = message_method; | |
ses->from = pjsip_from_hdr_create(pool); | |
pj_strdup_with_null(pool, &tmp, param_from); | |
ses->from->uri = pjsip_parse_uri(pool, tmp.ptr, tmp.slen, PJSIP_PARSE_URI_AS_NAMEADDR); | |
if (ses->from->uri == NULL) { | |
pjsip_endpt_destroy_pool(endpt, pool); | |
return NULL; | |
} | |
pj_create_unique_string(pool, &ses->from->tag); | |
ses->to = pjsip_to_hdr_create(pool); | |
pj_strdup_with_null(pool, &to, param_from); | |
ses->to->uri = pjsip_parse_uri(pool, to.ptr, to.slen, PJSIP_PARSE_URI_AS_NAMEADDR); | |
if (ses->to->uri == NULL) { | |
pjsip_endpt_destroy_pool(endpt, pool); | |
return NULL; | |
} | |
PJ_LOG(4,(THIS_FILE, "IM session created: recipient=%s", to.ptr)); | |
return ses; | |
} | |
/* | |
* Send IM message using identification from 'IM session'. | |
*/ | |
PJ_DEF(pjsip_tx_data*) | |
pjsip_messaging_session_create_msg( pjsip_messaging_session *ses, const pj_str_t *text ) | |
{ | |
return pjsip_endpt_create_request_from_hdr( ses->endpt, | |
&message_method, | |
ses->to->uri, | |
ses->from, | |
ses->to, | |
NULL, | |
ses->call_id, | |
ses->cseq->cseq++, | |
text); | |
} | |
/* | |
* Destroy 'IM session'. | |
*/ | |
PJ_DEF(pj_status_t) | |
pjsip_messaging_destroy_session( pjsip_messaging_session *ses ) | |
{ | |
/* | |
* NOTE ABOUT POSSIBLE BUG HERE... | |
* | |
* We don't check number of pending transaction before destroying IM | |
* session. As the result, the headers in the txdata of pending transaction | |
* wil be INVALID once the IM session is deleted (because we only | |
* shallo_clone()-ed them). | |
* | |
* This normally should be okay, because once the message is | |
* submitted to transaction, the transaction (or rather the transport) | |
* will 'print' the message to a buffer, and once it is printed, it | |
* won't try to access the original message again. So even when the | |
* original message has a dangling pointer, we should be safe. | |
* | |
* However, it will cause a problem if: | |
* - resolving completes asynchronously and with a substantial delay, | |
* and before the resolver/transport finished its job the user | |
* destroy the IM session. | |
* - if the transmit data is invalidated after the IM session is | |
* destroyed. | |
*/ | |
pjsip_endpt_destroy_pool(ses->endpt, ses->pool); | |
return 0; | |
} | |
static pj_status_t messaging_init( pjsip_endpoint *endpt, | |
struct pjsip_module *mod, pj_uint32_t id ) | |
{ | |
PJ_UNUSED_ARG(endpt) | |
PJ_UNUSED_ARG(mod) | |
module_id = id; | |
return 0; | |
} | |
static pj_status_t messaging_start( struct pjsip_module *mod ) | |
{ | |
PJ_UNUSED_ARG(mod) | |
return 0; | |
} | |
static pj_status_t messaging_deinit( struct pjsip_module *mod ) | |
{ | |
PJ_UNUSED_ARG(mod) | |
return 0; | |
} | |
static void messaging_tsx_handler( struct pjsip_module *mod, pjsip_event *event ) | |
{ | |
pjsip_transaction *tsx = event->obj.tsx; | |
struct messaging_data *mod_data; | |
PJ_UNUSED_ARG(mod) | |
/* Ignore non transaction event */ | |
if (event->type != PJSIP_EVENT_TSX_STATE_CHANGED || tsx == NULL) | |
return; | |
/* If this is an incoming message, inform application. */ | |
if (tsx->role == PJSIP_ROLE_UAS) { | |
int status = 100; | |
pjsip_tx_data *tdata; | |
/* Check if we already answered this request. */ | |
if (tsx->status_code >= 200) | |
return; | |
/* Only handle MESSAGE requests!. */ | |
if (pjsip_method_cmp(&tsx->method, &message_method) != 0) | |
return; | |
/* Call application callback. */ | |
if (incoming_cb) | |
status = (*incoming_cb)(event->src.rdata); | |
if (status < 200 || status >= 700) | |
status = PJSIP_SC_INTERNAL_SERVER_ERROR; | |
/* Respond request. */ | |
tdata = pjsip_endpt_create_response(tsx->endpt, event->src.rdata, status ); | |
if (tdata) | |
pjsip_tsx_on_tx_msg(tsx, tdata); | |
return; | |
} | |
/* Ignore if it's not something that came from messaging module. */ | |
mod_data = tsx->module_data[ module_id ]; | |
if (mod_data == NULL) | |
return; | |
/* Ignore non final response. */ | |
if (tsx->status_code < 200) | |
return; | |
/* Don't want to call the callback more than once. */ | |
tsx->module_data[ module_id ] = NULL; | |
/* Now call the callback. */ | |
if (mod_data->cb) { | |
(*mod_data->cb)(mod_data->token, tsx->status_code); | |
} | |
} | |
static pjsip_module messaging_module = | |
{ | |
{ "Messaging", 9}, /* Name. */ | |
0, /* Flag */ | |
128, /* Priority */ | |
NULL, /* User agent instance, initialized by APP. */ | |
0, /* Number of methods supported (will be initialized later). */ | |
{ 0 }, /* Array of methods (will be initialized later) */ | |
&messaging_init, /* init_module() */ | |
&messaging_start, /* start_module() */ | |
&messaging_deinit, /* deinit_module() */ | |
&messaging_tsx_handler, /* tsx_handler() */ | |
}; | |
PJ_DEF(pjsip_module*) pjsip_messaging_get_module() | |
{ | |
static pj_str_t method_str = { "MESSAGE", 7 }; | |
pjsip_method_init_np( &message_method, &method_str); | |
messaging_module.method_cnt = 1; | |
messaging_module.methods[0] = &message_method; | |
return &messaging_module; | |
} | |