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