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