blob: aa56e348f23c721323cbfa2b27b8f0b9b986d3a8 [file] [log] [blame]
Benny Prijonob0808372006-03-02 21:18:58 +00001/* $Id$ */
2/*
Benny Prijonoa771a512007-02-19 01:13:53 +00003 * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
Benny Prijonob0808372006-03-02 21:18:58 +00004 *
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 <pjsua-lib/pjsua.h>
Benny Prijonoeebe9af2006-06-13 22:57:13 +000020#include <pjsua-lib/pjsua_internal.h>
Benny Prijonob0808372006-03-02 21:18:58 +000021
Benny Prijonob0808372006-03-02 21:18:58 +000022
Benny Prijonoeebe9af2006-06-13 22:57:13 +000023#define THIS_FILE "pjsua_im.h"
Benny Prijonob0808372006-03-02 21:18:58 +000024
25
26/* Declare MESSAGE method */
27/* We put PJSIP_MESSAGE_METHOD as the enum here, so that when
28 * somebody add that method into pjsip_method_e in sip_msg.h we
29 * will get an error here.
30 */
31enum
32{
33 PJSIP_MESSAGE_METHOD = PJSIP_OTHER_METHOD
34};
35
36const pjsip_method pjsip_message_method =
37{
38 PJSIP_MESSAGE_METHOD,
39 { "MESSAGE", 7 }
40};
41
42
43/* Proto */
44static pj_bool_t im_on_rx_request(pjsip_rx_data *rdata);
45
Benny Prijonoeebe9af2006-06-13 22:57:13 +000046
Benny Prijonob0808372006-03-02 21:18:58 +000047/* The module instance. */
48static pjsip_module mod_pjsua_im =
49{
50 NULL, NULL, /* prev, next. */
51 { "mod-pjsua-im", 12 }, /* Name. */
52 -1, /* Id */
53 PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */
54 NULL, /* load() */
55 NULL, /* start() */
56 NULL, /* stop() */
57 NULL, /* unload() */
58 &im_on_rx_request, /* on_rx_request() */
59 NULL, /* on_rx_response() */
60 NULL, /* on_tx_request. */
61 NULL, /* on_tx_response() */
62 NULL, /* on_tsx_state() */
63
64};
65
66
67/* MIME constants. */
68static const pj_str_t STR_MIME_APP = { "application", 11 };
69static const pj_str_t STR_MIME_ISCOMPOSING = { "im-iscomposing+xml", 18 };
70static const pj_str_t STR_MIME_TEXT = { "text", 4 };
71static const pj_str_t STR_MIME_PLAIN = { "plain", 5 };
72
73
74/* Check if content type is acceptable */
75static pj_bool_t acceptable_message(const pjsip_media_type *mime)
76{
77 return (pj_stricmp(&mime->type, &STR_MIME_TEXT)==0 &&
78 pj_stricmp(&mime->subtype, &STR_MIME_PLAIN)==0)
79 ||
80 (pj_stricmp(&mime->type, &STR_MIME_APP)==0 &&
81 pj_stricmp(&mime->subtype, &STR_MIME_ISCOMPOSING)==0);
82}
83
84
85/**
86 * Create Accept header for MESSAGE.
87 */
88pjsip_accept_hdr* pjsua_im_create_accept(pj_pool_t *pool)
89{
90 /* Create Accept header. */
91 pjsip_accept_hdr *accept;
92
93 accept = pjsip_accept_hdr_create(pool);
94 accept->values[0] = pj_str("text/plain");
95 accept->values[1] = pj_str("application/im-iscomposing+xml");
96 accept->count = 2;
97
98 return accept;
99}
100
101/**
102 * Private: check if we can accept the message.
103 */
104pj_bool_t pjsua_im_accept_pager(pjsip_rx_data *rdata,
Benny Prijonof9c668f2006-03-06 15:14:59 +0000105 pjsip_accept_hdr **p_accept_hdr)
Benny Prijonob0808372006-03-02 21:18:58 +0000106{
107 pjsip_ctype_hdr *ctype;
108 pjsip_msg *msg;
109
110 msg = rdata->msg_info.msg;
111
112 /* Request MUST have message body, with Content-Type equal to
113 * "text/plain".
114 */
115 ctype = pjsip_msg_find_hdr(msg, PJSIP_H_CONTENT_TYPE, NULL);
116 if (msg->body == NULL || ctype == NULL ||
117 !acceptable_message(&ctype->media))
118 {
119 /* Create Accept header. */
120 if (p_accept_hdr)
121 *p_accept_hdr = pjsua_im_create_accept(rdata->tp_info.pool);
122
123 return PJ_FALSE;
124 }
125
126 return PJ_TRUE;
127}
128
129/**
130 * Private: process pager message.
131 * This may trigger pjsua_ui_on_pager() or pjsua_ui_on_typing().
132 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000133void pjsua_im_process_pager(int call_id, const pj_str_t *from,
Benny Prijonob0808372006-03-02 21:18:58 +0000134 const pj_str_t *to, pjsip_rx_data *rdata)
135{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000136 pjsip_contact_hdr *contact_hdr;
137 pj_str_t contact;
Benny Prijonob0808372006-03-02 21:18:58 +0000138 pjsip_msg_body *body = rdata->msg_info.msg->body;
139
140 /* Body MUST have been checked before */
141 pj_assert(body != NULL);
142
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000143
144 /* Build remote contact */
145 contact_hdr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT,
146 NULL);
147 if (contact_hdr) {
148 contact.ptr = pj_pool_alloc(rdata->tp_info.pool,
149 PJSIP_MAX_URL_SIZE);
150 contact.slen = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
151 contact_hdr->uri, contact.ptr,
152 PJSIP_MAX_URL_SIZE);
153 } else {
154 contact.slen = 0;
155 }
156
157
Benny Prijonob0808372006-03-02 21:18:58 +0000158 if (pj_stricmp(&body->content_type.type, &STR_MIME_TEXT)==0 &&
159 pj_stricmp(&body->content_type.subtype, &STR_MIME_PLAIN)==0)
160 {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000161 const pj_str_t mime_text_plain = pj_str("text/plain");
162 pj_str_t text_body;
163
164 /* Save text body */
165 text_body.ptr = rdata->msg_info.msg->body->data;
166 text_body.slen = rdata->msg_info.msg->body->len;
Benny Prijonob0808372006-03-02 21:18:58 +0000167
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000168 if (pjsua_var.ua_cfg.cb.on_pager) {
169 (*pjsua_var.ua_cfg.cb.on_pager)(call_id, from, to, &contact,
170 &mime_text_plain, &text_body);
171 }
Benny Prijonob0808372006-03-02 21:18:58 +0000172
173 } else {
Benny Prijonob0808372006-03-02 21:18:58 +0000174 /* Expecting typing indication */
Benny Prijonob0808372006-03-02 21:18:58 +0000175 pj_status_t status;
176 pj_bool_t is_typing;
177
178 status = pjsip_iscomposing_parse( rdata->tp_info.pool, body->data,
179 body->len, &is_typing, NULL, NULL,
180 NULL );
181 if (status != PJ_SUCCESS) {
182 pjsua_perror(THIS_FILE, "Invalid MESSAGE body", status);
183 return;
184 }
185
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000186 if (pjsua_var.ua_cfg.cb.on_typing) {
187 (*pjsua_var.ua_cfg.cb.on_typing)(call_id, from, to, &contact,
188 is_typing);
189 }
Benny Prijonob0808372006-03-02 21:18:58 +0000190 }
191
192}
193
194
195/*
196 * Handler to receive incoming MESSAGE
197 */
198static pj_bool_t im_on_rx_request(pjsip_rx_data *rdata)
199{
200 pj_str_t from, to;
201 pjsip_accept_hdr *accept_hdr;
Benny Prijono8b1889b2006-06-06 18:40:40 +0000202 pjsip_contact_hdr *contact_hdr;
Benny Prijonob0808372006-03-02 21:18:58 +0000203 pjsip_msg *msg;
204 pj_status_t status;
205
206 msg = rdata->msg_info.msg;
207
208 /* Only want to handle MESSAGE requests. */
209 if (pjsip_method_cmp(&msg->line.req.method, &pjsip_message_method) != 0) {
210 return PJ_FALSE;
211 }
212
213
214 /* Should not have any transaction attached to rdata. */
215 PJ_ASSERT_RETURN(pjsip_rdata_get_tsx(rdata)==NULL, PJ_FALSE);
216
217 /* Should not have any dialog attached to rdata. */
218 PJ_ASSERT_RETURN(pjsip_rdata_get_dlg(rdata)==NULL, PJ_FALSE);
219
220 /* Check if we can accept the message. */
221 if (!pjsua_im_accept_pager(rdata, &accept_hdr)) {
222 pjsip_hdr hdr_list;
223
224 pj_list_init(&hdr_list);
225 pj_list_push_back(&hdr_list, accept_hdr);
226
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000227 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata,
Benny Prijonob0808372006-03-02 21:18:58 +0000228 PJSIP_SC_NOT_ACCEPTABLE_HERE, NULL,
229 &hdr_list, NULL);
230 return PJ_TRUE;
231 }
232
233 /* Respond with 200 first, so that remote doesn't retransmit in case
234 * the UI takes too long to process the message.
235 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000236 status = pjsip_endpt_respond( pjsua_var.endpt, NULL, rdata, 200, NULL,
Benny Prijonob0808372006-03-02 21:18:58 +0000237 NULL, NULL, NULL);
238
Benny Prijono8b1889b2006-06-06 18:40:40 +0000239 /* For the source URI, we use Contact header if present, since
240 * Contact header contains the port number information. If this is
241 * not available, then use From header.
242 */
Benny Prijonob0808372006-03-02 21:18:58 +0000243 from.ptr = pj_pool_alloc(rdata->tp_info.pool, PJSIP_MAX_URL_SIZE);
Benny Prijono8b1889b2006-06-06 18:40:40 +0000244 contact_hdr = pjsip_msg_find_hdr(rdata->msg_info.msg,
245 PJSIP_H_CONTACT, NULL);
246 if (contact_hdr) {
247 from.slen = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
248 contact_hdr->uri,
249 from.ptr, PJSIP_MAX_URL_SIZE);
250 } else {
251 from.slen = pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR,
252 rdata->msg_info.from->uri,
253 from.ptr, PJSIP_MAX_URL_SIZE);
254 }
255
Benny Prijonob0808372006-03-02 21:18:58 +0000256 if (from.slen < 1)
257 from = pj_str("<--URI is too long-->");
258
259 /* Build the To text. */
260 to.ptr = pj_pool_alloc(rdata->tp_info.pool, PJSIP_MAX_URL_SIZE);
261 to.slen = pjsip_uri_print( PJSIP_URI_IN_FROMTO_HDR,
262 rdata->msg_info.to->uri,
263 to.ptr, PJSIP_MAX_URL_SIZE);
264 if (to.slen < 1)
265 to = pj_str("<--URI is too long-->");
266
267 /* Process pager. */
268 pjsua_im_process_pager(-1, &from, &to, rdata);
269
270 /* Done. */
271 return PJ_TRUE;
272}
273
274
275/* Outgoing IM callback. */
276static void im_callback(void *token, pjsip_event *e)
277{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000278 pjsua_im_data *im_data = token;
Benny Prijonob0808372006-03-02 21:18:58 +0000279
280 if (e->type == PJSIP_EVENT_TSX_STATE) {
281
282 pjsip_transaction *tsx = e->body.tsx_state.tsx;
283
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000284 /* Ignore provisional response, if any */
285 if (tsx->status_code < 200)
286 return;
287
288
289 /* Handle authentication challenges */
290 if (e->body.tsx_state.type == PJSIP_EVENT_RX_MSG &&
291 (tsx->status_code == 401 || tsx->status_code == 407))
292 {
293 pjsip_rx_data *rdata = e->body.tsx_state.src.rdata;
294 pjsip_tx_data *tdata;
295 pjsip_auth_clt_sess auth;
296 pj_status_t status;
297
298 PJ_LOG(4,(THIS_FILE, "Resending IM with authentication"));
299
300 /* Create temporary authentication session */
301 pjsip_auth_clt_init(&auth,pjsua_var.endpt,rdata->tp_info.pool, 0);
302
303 pjsip_auth_clt_set_credentials(&auth,
304 pjsua_var.acc[im_data->acc_id].cred_cnt,
305 pjsua_var.acc[im_data->acc_id].cred);
306
307 status = pjsip_auth_clt_reinit_req(&auth, rdata, tsx->last_tx,
308 &tdata);
309 if (status == PJ_SUCCESS) {
310 pjsua_im_data *im_data2;
311
312 /* Must duplicate im_data */
313 im_data2 = pjsua_im_data_dup(tdata->pool, im_data);
314
315 /* Re-send request */
316 status = pjsip_endpt_send_request( pjsua_var.endpt, tdata, -1,
317 im_data2, &im_callback);
318 if (status == PJ_SUCCESS) {
319 /* Done */
320 return;
321 }
322 }
323 }
324
Benny Prijonob0808372006-03-02 21:18:58 +0000325 if (tsx->status_code/100 == 2) {
326 PJ_LOG(4,(THIS_FILE,
327 "Message \'%s\' delivered successfully",
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000328 im_data->body.ptr));
Benny Prijonob0808372006-03-02 21:18:58 +0000329 } else {
330 PJ_LOG(3,(THIS_FILE,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000331 "Failed to deliver message \'%s\': %d/%.*s",
332 im_data->body.ptr,
333 tsx->status_code,
334 (int)tsx->status_text.slen,
335 tsx->status_text.ptr));
Benny Prijonob0808372006-03-02 21:18:58 +0000336 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000337
338 if (pjsua_var.ua_cfg.cb.on_pager_status)
339 pjsua_var.ua_cfg.cb.on_pager_status(im_data->call_id,
340 &im_data->to,
341 &im_data->body,
342 im_data->user_data,
343 tsx->status_code,
344 &tsx->status_text);
Benny Prijonob0808372006-03-02 21:18:58 +0000345 }
346}
347
348
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000349/* Outgoing typing indication callback.
350 * (used to reauthenticate request)
Benny Prijonob0808372006-03-02 21:18:58 +0000351 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000352static void typing_callback(void *token, pjsip_event *e)
353{
354 pjsua_im_data *im_data = token;
355
356 if (e->type == PJSIP_EVENT_TSX_STATE) {
357
358 pjsip_transaction *tsx = e->body.tsx_state.tsx;
359
360 /* Ignore provisional response, if any */
361 if (tsx->status_code < 200)
362 return;
363
364 /* Handle authentication challenges */
365 if (e->body.tsx_state.type == PJSIP_EVENT_RX_MSG &&
366 (tsx->status_code == 401 || tsx->status_code == 407))
367 {
368 pjsip_rx_data *rdata = e->body.tsx_state.src.rdata;
369 pjsip_tx_data *tdata;
370 pjsip_auth_clt_sess auth;
371 pj_status_t status;
372
373 PJ_LOG(4,(THIS_FILE, "Resending IM with authentication"));
374
375 /* Create temporary authentication session */
376 pjsip_auth_clt_init(&auth,pjsua_var.endpt,rdata->tp_info.pool, 0);
377
378 pjsip_auth_clt_set_credentials(&auth,
379 pjsua_var.acc[im_data->acc_id].cred_cnt,
380 pjsua_var.acc[im_data->acc_id].cred);
381
382 status = pjsip_auth_clt_reinit_req(&auth, rdata, tsx->last_tx,
383 &tdata);
384 if (status == PJ_SUCCESS) {
385 pjsua_im_data *im_data2;
386
387 /* Must duplicate im_data */
388 im_data2 = pjsua_im_data_dup(tdata->pool, im_data);
389
390 /* Re-send request */
391 status = pjsip_endpt_send_request( pjsua_var.endpt, tdata, -1,
392 im_data2, &typing_callback);
393 if (status == PJ_SUCCESS) {
394 /* Done */
395 return;
396 }
397 }
398 }
399
400 }
401}
402
403
404/*
405 * Send instant messaging outside dialog, using the specified account for
406 * route set and authentication.
407 */
408PJ_DEF(pj_status_t) pjsua_im_send( pjsua_acc_id acc_id,
409 const pj_str_t *to,
410 const pj_str_t *mime_type,
411 const pj_str_t *content,
412 const pjsua_msg_data *msg_data,
413 void *user_data)
Benny Prijonob0808372006-03-02 21:18:58 +0000414{
415 pjsip_tx_data *tdata;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000416 const pj_str_t mime_text_plain = pj_str("text/plain");
Benny Prijonob0808372006-03-02 21:18:58 +0000417 const pj_str_t STR_CONTACT = { "Contact", 7 };
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000418 pjsip_media_type media_type;
419 pjsua_im_data *im_data;
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000420 pj_str_t contact;
Benny Prijonob0808372006-03-02 21:18:58 +0000421 pj_status_t status;
422
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000423 /* To and message body must be specified. */
424 PJ_ASSERT_RETURN(to && content, PJ_EINVAL);
425
Benny Prijonob0808372006-03-02 21:18:58 +0000426 /* Create request. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000427 status = pjsip_endpt_create_request(pjsua_var.endpt,
428 &pjsip_message_method, to,
429 &pjsua_var.acc[acc_id].cfg.id,
430 to, NULL, NULL, -1, NULL, &tdata);
Benny Prijonob0808372006-03-02 21:18:58 +0000431 if (status != PJ_SUCCESS) {
432 pjsua_perror(THIS_FILE, "Unable to create request", status);
433 return status;
434 }
435
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000436 /* If account is locked to specific transport, then set transport to
437 * the request.
438 */
439 if (pjsua_var.acc[acc_id].cfg.transport_id != PJSUA_INVALID_ID) {
440 pjsip_tpselector tp_sel;
441
442 pjsua_init_tpselector(pjsua_var.acc[acc_id].cfg.transport_id, &tp_sel);
443 pjsip_tx_data_set_transport(tdata, &tp_sel);
444 }
445
Benny Prijonob0808372006-03-02 21:18:58 +0000446 /* Add accept header. */
447 pjsip_msg_add_hdr( tdata->msg,
448 (pjsip_hdr*)pjsua_im_create_accept(tdata->pool));
449
450 /* Add contact. */
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000451 status = pjsua_acc_create_uac_contact(tdata->pool, &contact, acc_id, to);
452 if (status != PJ_SUCCESS) {
453 pjsua_perror(THIS_FILE, "Unable to generate Contact header", status);
454 pjsip_tx_data_dec_ref(tdata);
455 return status;
456 }
457
Benny Prijonob0808372006-03-02 21:18:58 +0000458 pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)
Benny Prijonodc39fe82006-05-26 12:17:46 +0000459 pjsip_generic_string_hdr_create(tdata->pool,
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000460 &STR_CONTACT, &contact));
Benny Prijonob0808372006-03-02 21:18:58 +0000461
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000462 /* Create IM data to keep message details and give it back to
463 * application on the callback
Benny Prijonob0808372006-03-02 21:18:58 +0000464 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000465 im_data = pj_pool_zalloc(tdata->pool, sizeof(*im_data));
466 im_data->acc_id = acc_id;
467 im_data->call_id = PJSUA_INVALID_ID;
468 pj_strdup_with_null(tdata->pool, &im_data->to, to);
469 pj_strdup_with_null(tdata->pool, &im_data->body, content);
470 im_data->user_data = user_data;
471
472
473 /* Set default media type if none is specified */
474 if (mime_type == NULL) {
475 mime_type = &mime_text_plain;
476 }
477
478 /* Parse MIME type */
479 pjsua_parse_media_type(tdata->pool, mime_type, &media_type);
Benny Prijonob0808372006-03-02 21:18:58 +0000480
481 /* Add message body */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000482 tdata->msg->body = pjsip_msg_body_create( tdata->pool, &media_type.type,
483 &media_type.subtype,
484 &im_data->body);
Benny Prijonob0808372006-03-02 21:18:58 +0000485 if (tdata->msg->body == NULL) {
486 pjsua_perror(THIS_FILE, "Unable to create msg body", PJ_ENOMEM);
487 pjsip_tx_data_dec_ref(tdata);
488 return PJ_ENOMEM;
489 }
490
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000491 /* Add additional headers etc. */
492 pjsua_process_msg_data(tdata, msg_data);
493
494 /* Add route set */
495 pjsua_set_msg_route_set(tdata, &pjsua_var.acc[acc_id].route_set);
496
Benny Prijonob0808372006-03-02 21:18:58 +0000497 /* Send request (statefully) */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000498 status = pjsip_endpt_send_request( pjsua_var.endpt, tdata, -1,
499 im_data, &im_callback);
Benny Prijonob0808372006-03-02 21:18:58 +0000500 if (status != PJ_SUCCESS) {
501 pjsua_perror(THIS_FILE, "Unable to send request", status);
502 return status;
503 }
504
505 return PJ_SUCCESS;
506}
507
508
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000509/*
Benny Prijonob0808372006-03-02 21:18:58 +0000510 * Send typing indication outside dialog.
511 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000512PJ_DEF(pj_status_t) pjsua_im_typing( pjsua_acc_id acc_id,
513 const pj_str_t *to,
514 pj_bool_t is_typing,
515 const pjsua_msg_data *msg_data)
Benny Prijonob0808372006-03-02 21:18:58 +0000516{
Benny Prijono8b1889b2006-06-06 18:40:40 +0000517 const pj_str_t STR_CONTACT = { "Contact", 7 };
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000518 pjsua_im_data *im_data;
Benny Prijonob0808372006-03-02 21:18:58 +0000519 pjsip_tx_data *tdata;
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000520 pj_str_t contact;
Benny Prijonob0808372006-03-02 21:18:58 +0000521 pj_status_t status;
522
523 /* Create request. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000524 status = pjsip_endpt_create_request( pjsua_var.endpt, &pjsip_message_method,
525 to, &pjsua_var.acc[acc_id].cfg.id,
526 to, NULL, NULL, -1, NULL, &tdata);
Benny Prijonob0808372006-03-02 21:18:58 +0000527 if (status != PJ_SUCCESS) {
528 pjsua_perror(THIS_FILE, "Unable to create request", status);
529 return status;
530 }
531
532
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000533 /* If account is locked to specific transport, then set transport to
534 * the request.
535 */
536 if (pjsua_var.acc[acc_id].cfg.transport_id != PJSUA_INVALID_ID) {
537 pjsip_tpselector tp_sel;
538
539 pjsua_init_tpselector(pjsua_var.acc[acc_id].cfg.transport_id, &tp_sel);
540 pjsip_tx_data_set_transport(tdata, &tp_sel);
541 }
542
Benny Prijono8b1889b2006-06-06 18:40:40 +0000543 /* Add accept header. */
544 pjsip_msg_add_hdr( tdata->msg,
545 (pjsip_hdr*)pjsua_im_create_accept(tdata->pool));
546
547
548 /* Add contact. */
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000549 status = pjsua_acc_create_uac_contact(tdata->pool, &contact, acc_id, to);
550 if (status != PJ_SUCCESS) {
551 pjsua_perror(THIS_FILE, "Unable to generate Contact header", status);
552 pjsip_tx_data_dec_ref(tdata);
553 return status;
554 }
555
Benny Prijono8b1889b2006-06-06 18:40:40 +0000556 pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)
557 pjsip_generic_string_hdr_create(tdata->pool,
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000558 &STR_CONTACT, &contact));
Benny Prijono8b1889b2006-06-06 18:40:40 +0000559
560
Benny Prijonob0808372006-03-02 21:18:58 +0000561 /* Create "application/im-iscomposing+xml" msg body. */
562 tdata->msg->body = pjsip_iscomposing_create_body( tdata->pool, is_typing,
563 NULL, NULL, -1);
564
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000565 /* Add additional headers etc. */
566 pjsua_process_msg_data(tdata, msg_data);
567
Benny Prijonoe1a10cb2007-04-04 10:45:44 +0000568 /* Add route set */
569 pjsua_set_msg_route_set(tdata, &pjsua_var.acc[acc_id].route_set);
570
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000571 /* Create data to reauthenticate */
572 im_data = pj_pool_zalloc(tdata->pool, sizeof(*im_data));
573 im_data->acc_id = acc_id;
574
Benny Prijonob0808372006-03-02 21:18:58 +0000575 /* Send request (statefully) */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000576 status = pjsip_endpt_send_request( pjsua_var.endpt, tdata, -1,
577 im_data, &typing_callback);
Benny Prijonob0808372006-03-02 21:18:58 +0000578 if (status != PJ_SUCCESS) {
579 pjsua_perror(THIS_FILE, "Unable to send request", status);
580 return status;
581 }
582
583 return PJ_SUCCESS;
584}
585
586
587/*
588 * Init pjsua IM module.
589 */
590pj_status_t pjsua_im_init(void)
591{
592 const pj_str_t msg_tag = { "MESSAGE", 7 };
Benny Prijonoc8141a82006-08-20 09:12:19 +0000593 const pj_str_t STR_MIME_TEXT_PLAIN = { "text/plain", 10 };
594 const pj_str_t STR_MIME_APP_ISCOMPOSING =
595 { "application/im-iscomposing+xml", 30 };
Benny Prijonob0808372006-03-02 21:18:58 +0000596 pj_status_t status;
597
598 /* Register module */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000599 status = pjsip_endpt_register_module(pjsua_var.endpt, &mod_pjsua_im);
Benny Prijonob0808372006-03-02 21:18:58 +0000600 if (status != PJ_SUCCESS)
601 return status;
602
603 /* Register support for MESSAGE method. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000604 pjsip_endpt_add_capability( pjsua_var.endpt, &mod_pjsua_im, PJSIP_H_ALLOW,
Benny Prijonob0808372006-03-02 21:18:58 +0000605 NULL, 1, &msg_tag);
606
Benny Prijonoc8141a82006-08-20 09:12:19 +0000607 /* Register support for "application/im-iscomposing+xml" content */
608 pjsip_endpt_add_capability( pjsua_var.endpt, &mod_pjsua_im, PJSIP_H_ACCEPT,
609 NULL, 1, &STR_MIME_APP_ISCOMPOSING);
610
611 /* Register support for "text/plain" content */
612 pjsip_endpt_add_capability( pjsua_var.endpt, &mod_pjsua_im, PJSIP_H_ACCEPT,
613 NULL, 1, &STR_MIME_TEXT_PLAIN);
614
Benny Prijonob0808372006-03-02 21:18:58 +0000615 return PJ_SUCCESS;
616}
617