blob: 6e08af68fe98def974df90e7b74938ec79fa0876 [file] [log] [blame]
Benny Prijono5dcb38d2005-11-21 01:55:47 +00001/* $Id$ */
2/*
3 * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19#include <pjsip_simple/messaging.h>
20#include <pjsip/sip_endpoint.h>
21#include <pjsip/sip_parser.h>
22#include <pjsip/sip_transaction.h>
23#include <pjsip/sip_event.h>
24#include <pjsip/sip_module.h>
25#include <pjsip/sip_util.h>
26#include <pj/string.h>
27#include <pj/pool.h>
28#include <pj/guid.h>
29#include <pj/string.h>
30#include <pj/log.h>
31#include <stdio.h>
32#include <stdlib.h>
33
34#define THIS_FILE "messaging"
35
36struct messaging_data
37{
38 void *token;
39 pjsip_messaging_cb cb;
40};
41
42struct pjsip_messaging_session
43{
44 pj_pool_t *pool;
45 pjsip_endpoint *endpt;
46 pjsip_from_hdr *from;
47 pjsip_to_hdr *to;
48 pjsip_cid_hdr *call_id;
49 pjsip_cseq_hdr *cseq;
50};
51
52static int module_id;
53static pjsip_on_new_msg_cb incoming_cb;
54static pjsip_method message_method;
55
56
57/*
58 * Set global callback to receive incoming message.
59 */
60PJ_DEF(pjsip_on_new_msg_cb)
61pjsip_messaging_set_incoming_callback(pjsip_on_new_msg_cb cb)
62{
63 pjsip_on_new_msg_cb prev_cb = incoming_cb;
64 incoming_cb = cb;
65 return prev_cb;
66}
67
68
69/*
70 * Create an independent message (ie. not associated with a session).
71 */
72PJ_DEF(pjsip_tx_data*)
73pjsip_messaging_create_msg_from_hdr(pjsip_endpoint *endpt,
74 const pjsip_uri *target,
75 const pjsip_from_hdr *param_from,
76 const pjsip_to_hdr *param_to,
77 const pjsip_cid_hdr *param_call_id,
78 int param_cseq,
79 const pj_str_t *param_text)
80{
81 return pjsip_endpt_create_request_from_hdr( endpt, &message_method,
82 target,
83 param_from, param_to,
84 NULL, param_call_id,
85 param_cseq, param_text );
86}
87
88/*
89 * Create independent message from string (instead of from header).
90 */
91PJ_DEF(pjsip_tx_data*)
92pjsip_messaging_create_msg( pjsip_endpoint *endpt,
93 const pj_str_t *target,
94 const pj_str_t *param_from,
95 const pj_str_t *param_to,
96 const pj_str_t *param_call_id,
97 int param_cseq,
98 const pj_str_t *param_text)
99{
100 return pjsip_endpt_create_request( endpt, &message_method, target,
101 param_from, param_to, NULL, param_call_id,
102 param_cseq, param_text);
103}
104
105/*
106 * Initiate transaction to send outgoing message.
107 */
108PJ_DEF(pj_status_t)
109pjsip_messaging_send_msg( pjsip_endpoint *endpt, pjsip_tx_data *tdata,
110 void *token, pjsip_messaging_cb cb )
111{
112 pjsip_transaction *tsx;
113 struct messaging_data *msg_data;
114
115 /* Create transaction. */
116 tsx = pjsip_endpt_create_tsx(endpt);
117 if (!tsx) {
118 pjsip_tx_data_dec_ref(tdata);
119 return -1;
120 }
121
122 /* Save parameters to messaging data and attach to tsx. */
123 msg_data = pj_pool_calloc(tsx->pool, 1, sizeof(struct messaging_data));
124 msg_data->cb = cb;
125 msg_data->token = token;
126
127 /* Init transaction. */
128 tsx->module_data[module_id] = msg_data;
129 if (pjsip_tsx_init_uac(tsx, tdata) != 0) {
130 pjsip_tx_data_dec_ref(tdata);
131 pjsip_endpt_destroy_tsx(endpt, tsx);
132 return -1;
133 }
134
135 pjsip_endpt_register_tsx(endpt, tsx);
136
137 /*
138 * Instruct transaction to send message.
139 * Further events will be received via transaction's event.
140 */
141 pjsip_tsx_on_tx_msg(tsx, tdata);
142
143 /* Decrement reference counter. */
144 pjsip_tx_data_dec_ref(tdata);
145 return 0;
146}
147
148
149/*
150 * Create 'IM session'.
151 */
152PJ_DEF(pjsip_messaging_session*)
153pjsip_messaging_create_session( pjsip_endpoint *endpt, const pj_str_t *param_from,
154 const pj_str_t *param_to )
155{
156 pj_pool_t *pool;
157 pjsip_messaging_session *ses;
158 pj_str_t tmp, to;
159
160 pool = pjsip_endpt_create_pool(endpt, "imsess", 1024, 1024);
161 if (!pool)
162 return NULL;
163
164 ses = pj_pool_calloc(pool, 1, sizeof(pjsip_messaging_session));
165 ses->pool = pool;
166 ses->endpt = endpt;
167
168 ses->call_id = pjsip_cid_hdr_create(pool);
169 pj_create_unique_string(pool, &ses->call_id->id);
170
171 ses->cseq = pjsip_cseq_hdr_create(pool);
172 ses->cseq->cseq = pj_rand();
173 ses->cseq->method = message_method;
174
175 ses->from = pjsip_from_hdr_create(pool);
176 pj_strdup_with_null(pool, &tmp, param_from);
177 ses->from->uri = pjsip_parse_uri(pool, tmp.ptr, tmp.slen, PJSIP_PARSE_URI_AS_NAMEADDR);
178 if (ses->from->uri == NULL) {
179 pjsip_endpt_destroy_pool(endpt, pool);
180 return NULL;
181 }
182 pj_create_unique_string(pool, &ses->from->tag);
183
184 ses->to = pjsip_to_hdr_create(pool);
185 pj_strdup_with_null(pool, &to, param_from);
186 ses->to->uri = pjsip_parse_uri(pool, to.ptr, to.slen, PJSIP_PARSE_URI_AS_NAMEADDR);
187 if (ses->to->uri == NULL) {
188 pjsip_endpt_destroy_pool(endpt, pool);
189 return NULL;
190 }
191
192 PJ_LOG(4,(THIS_FILE, "IM session created: recipient=%s", to.ptr));
193 return ses;
194}
195
196
197/*
198 * Send IM message using identification from 'IM session'.
199 */
200PJ_DEF(pjsip_tx_data*)
201pjsip_messaging_session_create_msg( pjsip_messaging_session *ses, const pj_str_t *text )
202{
203 return pjsip_endpt_create_request_from_hdr( ses->endpt,
204 &message_method,
205 ses->to->uri,
206 ses->from,
207 ses->to,
208 NULL,
209 ses->call_id,
210 ses->cseq->cseq++,
211 text);
212}
213
214
215/*
216 * Destroy 'IM session'.
217 */
218PJ_DEF(pj_status_t)
219pjsip_messaging_destroy_session( pjsip_messaging_session *ses )
220{
221 /*
222 * NOTE ABOUT POSSIBLE BUG HERE...
223 *
224 * We don't check number of pending transaction before destroying IM
225 * session. As the result, the headers in the txdata of pending transaction
226 * wil be INVALID once the IM session is deleted (because we only
227 * shallo_clone()-ed them).
228 *
229 * This normally should be okay, because once the message is
230 * submitted to transaction, the transaction (or rather the transport)
231 * will 'print' the message to a buffer, and once it is printed, it
232 * won't try to access the original message again. So even when the
233 * original message has a dangling pointer, we should be safe.
234 *
235 * However, it will cause a problem if:
236 * - resolving completes asynchronously and with a substantial delay,
237 * and before the resolver/transport finished its job the user
238 * destroy the IM session.
239 * - if the transmit data is invalidated after the IM session is
240 * destroyed.
241 */
242
243 pjsip_endpt_destroy_pool(ses->endpt, ses->pool);
244 return 0;
245}
246
247
248static pj_status_t messaging_init( pjsip_endpoint *endpt,
249 struct pjsip_module *mod, pj_uint32_t id )
250{
251 PJ_UNUSED_ARG(endpt)
252 PJ_UNUSED_ARG(mod)
253
254 module_id = id;
255 return 0;
256}
257
258static pj_status_t messaging_start( struct pjsip_module *mod )
259{
260 PJ_UNUSED_ARG(mod)
261 return 0;
262}
263
264static pj_status_t messaging_deinit( struct pjsip_module *mod )
265{
266 PJ_UNUSED_ARG(mod)
267 return 0;
268}
269
270static void messaging_tsx_handler( struct pjsip_module *mod, pjsip_event *event )
271{
272 pjsip_transaction *tsx = event->obj.tsx;
273 struct messaging_data *mod_data;
274
275 PJ_UNUSED_ARG(mod)
276
277 /* Ignore non transaction event */
278 if (event->type != PJSIP_EVENT_TSX_STATE_CHANGED || tsx == NULL)
279 return;
280
281 /* If this is an incoming message, inform application. */
282 if (tsx->role == PJSIP_ROLE_UAS) {
283 int status = 100;
284 pjsip_tx_data *tdata;
285
286 /* Check if we already answered this request. */
287 if (tsx->status_code >= 200)
288 return;
289
290 /* Only handle MESSAGE requests!. */
291 if (pjsip_method_cmp(&tsx->method, &message_method) != 0)
292 return;
293
294 /* Call application callback. */
295 if (incoming_cb)
296 status = (*incoming_cb)(event->src.rdata);
297
298 if (status < 200 || status >= 700)
299 status = PJSIP_SC_INTERNAL_SERVER_ERROR;
300
301 /* Respond request. */
302 tdata = pjsip_endpt_create_response(tsx->endpt, event->src.rdata, status );
303 if (tdata)
304 pjsip_tsx_on_tx_msg(tsx, tdata);
305
306 return;
307 }
308
309 /* Ignore if it's not something that came from messaging module. */
310 mod_data = tsx->module_data[ module_id ];
311 if (mod_data == NULL)
312 return;
313
314 /* Ignore non final response. */
315 if (tsx->status_code < 200)
316 return;
317
318 /* Don't want to call the callback more than once. */
319 tsx->module_data[ module_id ] = NULL;
320
321 /* Now call the callback. */
322 if (mod_data->cb) {
323 (*mod_data->cb)(mod_data->token, tsx->status_code);
324 }
325}
326
327static pjsip_module messaging_module =
328{
329 { "Messaging", 9}, /* Name. */
330 0, /* Flag */
331 128, /* Priority */
332 NULL, /* User agent instance, initialized by APP. */
333 0, /* Number of methods supported (will be initialized later). */
334 { 0 }, /* Array of methods (will be initialized later) */
335 &messaging_init, /* init_module() */
336 &messaging_start, /* start_module() */
337 &messaging_deinit, /* deinit_module() */
338 &messaging_tsx_handler, /* tsx_handler() */
339};
340
341PJ_DEF(pjsip_module*) pjsip_messaging_get_module()
342{
343 static pj_str_t method_str = { "MESSAGE", 7 };
344
345 pjsip_method_init_np( &message_method, &method_str);
346
347 messaging_module.method_cnt = 1;
348 messaging_module.methods[0] = &message_method;
349
350 return &messaging_module;
351}
352