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