blob: 6eaf2b4ceef409674a0362de9910cd27edc9ba1b [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{
Benny Prijonoba5926a2007-05-02 11:29:37 +000038 (pjsip_method_e) PJSIP_MESSAGE_METHOD,
Benny Prijonob0808372006-03-02 21:18:58 +000039 { "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 */
Benny Prijonof4b538d2007-05-14 16:45:20 +000075#if 0
Benny Prijonob0808372006-03-02 21:18:58 +000076static pj_bool_t acceptable_message(const pjsip_media_type *mime)
77{
78 return (pj_stricmp(&mime->type, &STR_MIME_TEXT)==0 &&
79 pj_stricmp(&mime->subtype, &STR_MIME_PLAIN)==0)
80 ||
81 (pj_stricmp(&mime->type, &STR_MIME_APP)==0 &&
82 pj_stricmp(&mime->subtype, &STR_MIME_ISCOMPOSING)==0);
83}
Benny Prijonof4b538d2007-05-14 16:45:20 +000084#endif
Benny Prijonob0808372006-03-02 21:18:58 +000085
86/**
87 * Create Accept header for MESSAGE.
88 */
89pjsip_accept_hdr* pjsua_im_create_accept(pj_pool_t *pool)
90{
91 /* Create Accept header. */
92 pjsip_accept_hdr *accept;
93
94 accept = pjsip_accept_hdr_create(pool);
95 accept->values[0] = pj_str("text/plain");
96 accept->values[1] = pj_str("application/im-iscomposing+xml");
97 accept->count = 2;
98
99 return accept;
100}
101
102/**
103 * Private: check if we can accept the message.
104 */
105pj_bool_t pjsua_im_accept_pager(pjsip_rx_data *rdata,
Benny Prijonof9c668f2006-03-06 15:14:59 +0000106 pjsip_accept_hdr **p_accept_hdr)
Benny Prijonob0808372006-03-02 21:18:58 +0000107{
Benny Prijonof4b538d2007-05-14 16:45:20 +0000108 /* Some UA sends text/html, so this check will break */
109#if 0
Benny Prijonob0808372006-03-02 21:18:58 +0000110 pjsip_ctype_hdr *ctype;
111 pjsip_msg *msg;
112
113 msg = rdata->msg_info.msg;
114
115 /* Request MUST have message body, with Content-Type equal to
116 * "text/plain".
117 */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000118 ctype = (pjsip_ctype_hdr*)
119 pjsip_msg_find_hdr(msg, PJSIP_H_CONTENT_TYPE, NULL);
Benny Prijonob0808372006-03-02 21:18:58 +0000120 if (msg->body == NULL || ctype == NULL ||
121 !acceptable_message(&ctype->media))
122 {
123 /* Create Accept header. */
124 if (p_accept_hdr)
125 *p_accept_hdr = pjsua_im_create_accept(rdata->tp_info.pool);
126
127 return PJ_FALSE;
128 }
Benny Prijonof4b538d2007-05-14 16:45:20 +0000129#else
130 PJ_UNUSED_ARG(rdata);
131 PJ_UNUSED_ARG(p_accept_hdr);
132#endif
Benny Prijonob0808372006-03-02 21:18:58 +0000133
134 return PJ_TRUE;
135}
136
137/**
138 * Private: process pager message.
139 * This may trigger pjsua_ui_on_pager() or pjsua_ui_on_typing().
140 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000141void pjsua_im_process_pager(int call_id, const pj_str_t *from,
Benny Prijonob0808372006-03-02 21:18:58 +0000142 const pj_str_t *to, pjsip_rx_data *rdata)
143{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000144 pjsip_contact_hdr *contact_hdr;
145 pj_str_t contact;
Benny Prijonob0808372006-03-02 21:18:58 +0000146 pjsip_msg_body *body = rdata->msg_info.msg->body;
147
148 /* Body MUST have been checked before */
149 pj_assert(body != NULL);
150
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000151
152 /* Build remote contact */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000153 contact_hdr = (pjsip_contact_hdr*)
154 pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000155 NULL);
156 if (contact_hdr) {
Benny Prijonoa1e69682007-05-11 15:14:34 +0000157 contact.ptr = (char*) pj_pool_alloc(rdata->tp_info.pool,
158 PJSIP_MAX_URL_SIZE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000159 contact.slen = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
160 contact_hdr->uri, contact.ptr,
161 PJSIP_MAX_URL_SIZE);
162 } else {
163 contact.slen = 0;
164 }
165
Benny Prijonof4b538d2007-05-14 16:45:20 +0000166 if (pj_stricmp(&body->content_type.type, &STR_MIME_APP)==0 &&
167 pj_stricmp(&body->content_type.subtype, &STR_MIME_ISCOMPOSING)==0)
Benny Prijonob0808372006-03-02 21:18:58 +0000168 {
Benny Prijonob0808372006-03-02 21:18:58 +0000169 /* Expecting typing indication */
Benny Prijonob0808372006-03-02 21:18:58 +0000170 pj_status_t status;
171 pj_bool_t is_typing;
172
Benny Prijonoa1e69682007-05-11 15:14:34 +0000173 status = pjsip_iscomposing_parse(rdata->tp_info.pool, (char*)body->data,
174 body->len, &is_typing, NULL, NULL,
175 NULL );
Benny Prijonob0808372006-03-02 21:18:58 +0000176 if (status != PJ_SUCCESS) {
177 pjsua_perror(THIS_FILE, "Invalid MESSAGE body", status);
178 return;
179 }
180
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000181 if (pjsua_var.ua_cfg.cb.on_typing) {
182 (*pjsua_var.ua_cfg.cb.on_typing)(call_id, from, to, &contact,
183 is_typing);
184 }
Benny Prijonof4b538d2007-05-14 16:45:20 +0000185
186 } else {
187 pj_str_t mime_type;
188 char buf[256];
189 pjsip_media_type *m;
190 pj_str_t text_body;
191
192 /* Save text body */
Benny Prijono38507402007-05-15 11:15:58 +0000193 text_body.ptr = (char*)rdata->msg_info.msg->body->data;
Benny Prijonof4b538d2007-05-14 16:45:20 +0000194 text_body.slen = rdata->msg_info.msg->body->len;
195
196 /* Get mime type */
197 m = &rdata->msg_info.msg->body->content_type;
198 mime_type.ptr = buf;
199 mime_type.slen = pj_ansi_snprintf(buf, sizeof(buf),
200 "%.*s/%.*s",
201 (int)m->type.slen,
202 m->type.ptr,
203 (int)m->subtype.slen,
204 m->subtype.ptr);
205 if (mime_type.slen < 1)
206 mime_type.slen = 0;
207
208 if (pjsua_var.ua_cfg.cb.on_pager) {
209 (*pjsua_var.ua_cfg.cb.on_pager)(call_id, from, to, &contact,
210 &mime_type, &text_body);
211 }
Benny Prijonob0808372006-03-02 21:18:58 +0000212
Benny Prijonobbeb3992007-05-21 13:48:35 +0000213 if (pjsua_var.ua_cfg.cb.on_pager2) {
214 (*pjsua_var.ua_cfg.cb.on_pager2)(call_id, from, to, &contact,
215 &mime_type, &text_body, rdata);
216 }
217 }
Benny Prijonob0808372006-03-02 21:18:58 +0000218}
219
220
221/*
222 * Handler to receive incoming MESSAGE
223 */
224static pj_bool_t im_on_rx_request(pjsip_rx_data *rdata)
225{
226 pj_str_t from, to;
227 pjsip_accept_hdr *accept_hdr;
228 pjsip_msg *msg;
229 pj_status_t status;
230
231 msg = rdata->msg_info.msg;
232
233 /* Only want to handle MESSAGE requests. */
234 if (pjsip_method_cmp(&msg->line.req.method, &pjsip_message_method) != 0) {
235 return PJ_FALSE;
236 }
237
238
239 /* Should not have any transaction attached to rdata. */
240 PJ_ASSERT_RETURN(pjsip_rdata_get_tsx(rdata)==NULL, PJ_FALSE);
241
242 /* Should not have any dialog attached to rdata. */
243 PJ_ASSERT_RETURN(pjsip_rdata_get_dlg(rdata)==NULL, PJ_FALSE);
244
245 /* Check if we can accept the message. */
246 if (!pjsua_im_accept_pager(rdata, &accept_hdr)) {
247 pjsip_hdr hdr_list;
248
249 pj_list_init(&hdr_list);
250 pj_list_push_back(&hdr_list, accept_hdr);
251
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000252 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata,
Benny Prijonob0808372006-03-02 21:18:58 +0000253 PJSIP_SC_NOT_ACCEPTABLE_HERE, NULL,
254 &hdr_list, NULL);
255 return PJ_TRUE;
256 }
257
258 /* Respond with 200 first, so that remote doesn't retransmit in case
259 * the UI takes too long to process the message.
260 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000261 status = pjsip_endpt_respond( pjsua_var.endpt, NULL, rdata, 200, NULL,
Benny Prijonob0808372006-03-02 21:18:58 +0000262 NULL, NULL, NULL);
263
Benny Prijono8b1889b2006-06-06 18:40:40 +0000264 /* For the source URI, we use Contact header if present, since
265 * Contact header contains the port number information. If this is
266 * not available, then use From header.
267 */
Benny Prijono38507402007-05-15 11:15:58 +0000268 from.ptr = (char*)pj_pool_alloc(rdata->tp_info.pool, PJSIP_MAX_URL_SIZE);
Benny Prijonof4b538d2007-05-14 16:45:20 +0000269 from.slen = pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR,
270 rdata->msg_info.from->uri,
271 from.ptr, PJSIP_MAX_URL_SIZE);
Benny Prijono8b1889b2006-06-06 18:40:40 +0000272
Benny Prijonob0808372006-03-02 21:18:58 +0000273 if (from.slen < 1)
274 from = pj_str("<--URI is too long-->");
275
276 /* Build the To text. */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000277 to.ptr = (char*) pj_pool_alloc(rdata->tp_info.pool, PJSIP_MAX_URL_SIZE);
Benny Prijonob0808372006-03-02 21:18:58 +0000278 to.slen = pjsip_uri_print( PJSIP_URI_IN_FROMTO_HDR,
279 rdata->msg_info.to->uri,
280 to.ptr, PJSIP_MAX_URL_SIZE);
281 if (to.slen < 1)
282 to = pj_str("<--URI is too long-->");
283
284 /* Process pager. */
285 pjsua_im_process_pager(-1, &from, &to, rdata);
286
287 /* Done. */
288 return PJ_TRUE;
289}
290
291
292/* Outgoing IM callback. */
293static void im_callback(void *token, pjsip_event *e)
294{
Benny Prijonoa1e69682007-05-11 15:14:34 +0000295 pjsua_im_data *im_data = (pjsua_im_data*) token;
Benny Prijonob0808372006-03-02 21:18:58 +0000296
297 if (e->type == PJSIP_EVENT_TSX_STATE) {
298
299 pjsip_transaction *tsx = e->body.tsx_state.tsx;
300
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000301 /* Ignore provisional response, if any */
302 if (tsx->status_code < 200)
303 return;
304
305
306 /* Handle authentication challenges */
307 if (e->body.tsx_state.type == PJSIP_EVENT_RX_MSG &&
308 (tsx->status_code == 401 || tsx->status_code == 407))
309 {
310 pjsip_rx_data *rdata = e->body.tsx_state.src.rdata;
311 pjsip_tx_data *tdata;
312 pjsip_auth_clt_sess auth;
313 pj_status_t status;
314
315 PJ_LOG(4,(THIS_FILE, "Resending IM with authentication"));
316
317 /* Create temporary authentication session */
318 pjsip_auth_clt_init(&auth,pjsua_var.endpt,rdata->tp_info.pool, 0);
319
320 pjsip_auth_clt_set_credentials(&auth,
321 pjsua_var.acc[im_data->acc_id].cred_cnt,
322 pjsua_var.acc[im_data->acc_id].cred);
323
Benny Prijono48ab2b72007-11-08 09:24:30 +0000324 pjsip_auth_clt_set_prefs(&auth,
325 &pjsua_var.acc[im_data->acc_id].cfg.auth_pref);
326
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000327 status = pjsip_auth_clt_reinit_req(&auth, rdata, tsx->last_tx,
328 &tdata);
329 if (status == PJ_SUCCESS) {
330 pjsua_im_data *im_data2;
331
332 /* Must duplicate im_data */
333 im_data2 = pjsua_im_data_dup(tdata->pool, im_data);
334
335 /* Re-send request */
336 status = pjsip_endpt_send_request( pjsua_var.endpt, tdata, -1,
337 im_data2, &im_callback);
338 if (status == PJ_SUCCESS) {
339 /* Done */
340 return;
341 }
342 }
343 }
344
Benny Prijonob0808372006-03-02 21:18:58 +0000345 if (tsx->status_code/100 == 2) {
346 PJ_LOG(4,(THIS_FILE,
347 "Message \'%s\' delivered successfully",
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000348 im_data->body.ptr));
Benny Prijonob0808372006-03-02 21:18:58 +0000349 } else {
350 PJ_LOG(3,(THIS_FILE,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000351 "Failed to deliver message \'%s\': %d/%.*s",
352 im_data->body.ptr,
353 tsx->status_code,
354 (int)tsx->status_text.slen,
355 tsx->status_text.ptr));
Benny Prijonob0808372006-03-02 21:18:58 +0000356 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000357
Benny Prijono4da0b1d2007-06-11 18:22:54 +0000358 if (pjsua_var.ua_cfg.cb.on_pager_status) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000359 pjsua_var.ua_cfg.cb.on_pager_status(im_data->call_id,
360 &im_data->to,
361 &im_data->body,
362 im_data->user_data,
Benny Prijonoba5926a2007-05-02 11:29:37 +0000363 (pjsip_status_code)
364 tsx->status_code,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000365 &tsx->status_text);
Benny Prijono4da0b1d2007-06-11 18:22:54 +0000366 }
367
368 if (pjsua_var.ua_cfg.cb.on_pager_status2) {
369 pjsip_rx_data *rdata;
370
371 if (e->body.tsx_state.type == PJSIP_EVENT_RX_MSG)
372 rdata = e->body.tsx_state.src.rdata;
373 else
374 rdata = NULL;
375
376 pjsua_var.ua_cfg.cb.on_pager_status2(im_data->call_id,
377 &im_data->to,
378 &im_data->body,
379 im_data->user_data,
380 (pjsip_status_code)
381 tsx->status_code,
382 &tsx->status_text,
Benny Prijono0a982002007-06-12 16:22:09 +0000383 tsx->last_tx,
Benny Prijono4da0b1d2007-06-11 18:22:54 +0000384 rdata);
385 }
Benny Prijonob0808372006-03-02 21:18:58 +0000386 }
387}
388
389
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000390/* Outgoing typing indication callback.
391 * (used to reauthenticate request)
Benny Prijonob0808372006-03-02 21:18:58 +0000392 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000393static void typing_callback(void *token, pjsip_event *e)
394{
Benny Prijonoa1e69682007-05-11 15:14:34 +0000395 pjsua_im_data *im_data = (pjsua_im_data*) token;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000396
397 if (e->type == PJSIP_EVENT_TSX_STATE) {
398
399 pjsip_transaction *tsx = e->body.tsx_state.tsx;
400
401 /* Ignore provisional response, if any */
402 if (tsx->status_code < 200)
403 return;
404
405 /* Handle authentication challenges */
406 if (e->body.tsx_state.type == PJSIP_EVENT_RX_MSG &&
407 (tsx->status_code == 401 || tsx->status_code == 407))
408 {
409 pjsip_rx_data *rdata = e->body.tsx_state.src.rdata;
410 pjsip_tx_data *tdata;
411 pjsip_auth_clt_sess auth;
412 pj_status_t status;
413
414 PJ_LOG(4,(THIS_FILE, "Resending IM with authentication"));
415
416 /* Create temporary authentication session */
417 pjsip_auth_clt_init(&auth,pjsua_var.endpt,rdata->tp_info.pool, 0);
418
419 pjsip_auth_clt_set_credentials(&auth,
420 pjsua_var.acc[im_data->acc_id].cred_cnt,
421 pjsua_var.acc[im_data->acc_id].cred);
422
Benny Prijono48ab2b72007-11-08 09:24:30 +0000423 pjsip_auth_clt_set_prefs(&auth,
424 &pjsua_var.acc[im_data->acc_id].cfg.auth_pref);
425
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000426 status = pjsip_auth_clt_reinit_req(&auth, rdata, tsx->last_tx,
427 &tdata);
428 if (status == PJ_SUCCESS) {
429 pjsua_im_data *im_data2;
430
431 /* Must duplicate im_data */
432 im_data2 = pjsua_im_data_dup(tdata->pool, im_data);
433
434 /* Re-send request */
435 status = pjsip_endpt_send_request( pjsua_var.endpt, tdata, -1,
436 im_data2, &typing_callback);
437 if (status == PJ_SUCCESS) {
438 /* Done */
439 return;
440 }
441 }
442 }
443
444 }
445}
446
447
448/*
449 * Send instant messaging outside dialog, using the specified account for
450 * route set and authentication.
451 */
452PJ_DEF(pj_status_t) pjsua_im_send( pjsua_acc_id acc_id,
453 const pj_str_t *to,
454 const pj_str_t *mime_type,
455 const pj_str_t *content,
456 const pjsua_msg_data *msg_data,
457 void *user_data)
Benny Prijonob0808372006-03-02 21:18:58 +0000458{
459 pjsip_tx_data *tdata;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000460 const pj_str_t mime_text_plain = pj_str("text/plain");
Benny Prijonob0808372006-03-02 21:18:58 +0000461 const pj_str_t STR_CONTACT = { "Contact", 7 };
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000462 pjsip_media_type media_type;
463 pjsua_im_data *im_data;
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000464 pj_str_t contact;
Benny Prijonob0808372006-03-02 21:18:58 +0000465 pj_status_t status;
466
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000467 /* To and message body must be specified. */
468 PJ_ASSERT_RETURN(to && content, PJ_EINVAL);
469
Benny Prijonob0808372006-03-02 21:18:58 +0000470 /* Create request. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000471 status = pjsip_endpt_create_request(pjsua_var.endpt,
472 &pjsip_message_method, to,
473 &pjsua_var.acc[acc_id].cfg.id,
474 to, NULL, NULL, -1, NULL, &tdata);
Benny Prijonob0808372006-03-02 21:18:58 +0000475 if (status != PJ_SUCCESS) {
476 pjsua_perror(THIS_FILE, "Unable to create request", status);
477 return status;
478 }
479
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000480 /* If account is locked to specific transport, then set transport to
481 * the request.
482 */
483 if (pjsua_var.acc[acc_id].cfg.transport_id != PJSUA_INVALID_ID) {
484 pjsip_tpselector tp_sel;
485
486 pjsua_init_tpselector(pjsua_var.acc[acc_id].cfg.transport_id, &tp_sel);
487 pjsip_tx_data_set_transport(tdata, &tp_sel);
488 }
489
Benny Prijonob0808372006-03-02 21:18:58 +0000490 /* Add accept header. */
491 pjsip_msg_add_hdr( tdata->msg,
492 (pjsip_hdr*)pjsua_im_create_accept(tdata->pool));
493
494 /* Add contact. */
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000495 status = pjsua_acc_create_uac_contact(tdata->pool, &contact, acc_id, to);
496 if (status != PJ_SUCCESS) {
497 pjsua_perror(THIS_FILE, "Unable to generate Contact header", status);
498 pjsip_tx_data_dec_ref(tdata);
499 return status;
500 }
501
Benny Prijonob0808372006-03-02 21:18:58 +0000502 pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)
Benny Prijonodc39fe82006-05-26 12:17:46 +0000503 pjsip_generic_string_hdr_create(tdata->pool,
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000504 &STR_CONTACT, &contact));
Benny Prijonob0808372006-03-02 21:18:58 +0000505
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000506 /* Create IM data to keep message details and give it back to
507 * application on the callback
Benny Prijonob0808372006-03-02 21:18:58 +0000508 */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000509 im_data = PJ_POOL_ZALLOC_T(tdata->pool, pjsua_im_data);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000510 im_data->acc_id = acc_id;
511 im_data->call_id = PJSUA_INVALID_ID;
512 pj_strdup_with_null(tdata->pool, &im_data->to, to);
513 pj_strdup_with_null(tdata->pool, &im_data->body, content);
514 im_data->user_data = user_data;
515
516
517 /* Set default media type if none is specified */
518 if (mime_type == NULL) {
519 mime_type = &mime_text_plain;
520 }
521
522 /* Parse MIME type */
523 pjsua_parse_media_type(tdata->pool, mime_type, &media_type);
Benny Prijonob0808372006-03-02 21:18:58 +0000524
525 /* Add message body */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000526 tdata->msg->body = pjsip_msg_body_create( tdata->pool, &media_type.type,
527 &media_type.subtype,
528 &im_data->body);
Benny Prijonob0808372006-03-02 21:18:58 +0000529 if (tdata->msg->body == NULL) {
530 pjsua_perror(THIS_FILE, "Unable to create msg body", PJ_ENOMEM);
531 pjsip_tx_data_dec_ref(tdata);
532 return PJ_ENOMEM;
533 }
534
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000535 /* Add additional headers etc. */
536 pjsua_process_msg_data(tdata, msg_data);
537
538 /* Add route set */
539 pjsua_set_msg_route_set(tdata, &pjsua_var.acc[acc_id].route_set);
540
Benny Prijonob0808372006-03-02 21:18:58 +0000541 /* Send request (statefully) */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000542 status = pjsip_endpt_send_request( pjsua_var.endpt, tdata, -1,
543 im_data, &im_callback);
Benny Prijonob0808372006-03-02 21:18:58 +0000544 if (status != PJ_SUCCESS) {
545 pjsua_perror(THIS_FILE, "Unable to send request", status);
546 return status;
547 }
548
549 return PJ_SUCCESS;
550}
551
552
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000553/*
Benny Prijonob0808372006-03-02 21:18:58 +0000554 * Send typing indication outside dialog.
555 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000556PJ_DEF(pj_status_t) pjsua_im_typing( pjsua_acc_id acc_id,
557 const pj_str_t *to,
558 pj_bool_t is_typing,
559 const pjsua_msg_data *msg_data)
Benny Prijonob0808372006-03-02 21:18:58 +0000560{
Benny Prijono8b1889b2006-06-06 18:40:40 +0000561 const pj_str_t STR_CONTACT = { "Contact", 7 };
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000562 pjsua_im_data *im_data;
Benny Prijonob0808372006-03-02 21:18:58 +0000563 pjsip_tx_data *tdata;
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000564 pj_str_t contact;
Benny Prijonob0808372006-03-02 21:18:58 +0000565 pj_status_t status;
566
567 /* Create request. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000568 status = pjsip_endpt_create_request( pjsua_var.endpt, &pjsip_message_method,
569 to, &pjsua_var.acc[acc_id].cfg.id,
570 to, NULL, NULL, -1, NULL, &tdata);
Benny Prijonob0808372006-03-02 21:18:58 +0000571 if (status != PJ_SUCCESS) {
572 pjsua_perror(THIS_FILE, "Unable to create request", status);
573 return status;
574 }
575
576
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000577 /* If account is locked to specific transport, then set transport to
578 * the request.
579 */
580 if (pjsua_var.acc[acc_id].cfg.transport_id != PJSUA_INVALID_ID) {
581 pjsip_tpselector tp_sel;
582
583 pjsua_init_tpselector(pjsua_var.acc[acc_id].cfg.transport_id, &tp_sel);
584 pjsip_tx_data_set_transport(tdata, &tp_sel);
585 }
586
Benny Prijono8b1889b2006-06-06 18:40:40 +0000587 /* Add accept header. */
588 pjsip_msg_add_hdr( tdata->msg,
589 (pjsip_hdr*)pjsua_im_create_accept(tdata->pool));
590
591
592 /* Add contact. */
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000593 status = pjsua_acc_create_uac_contact(tdata->pool, &contact, acc_id, to);
594 if (status != PJ_SUCCESS) {
595 pjsua_perror(THIS_FILE, "Unable to generate Contact header", status);
596 pjsip_tx_data_dec_ref(tdata);
597 return status;
598 }
599
Benny Prijono8b1889b2006-06-06 18:40:40 +0000600 pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)
601 pjsip_generic_string_hdr_create(tdata->pool,
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000602 &STR_CONTACT, &contact));
Benny Prijono8b1889b2006-06-06 18:40:40 +0000603
604
Benny Prijonob0808372006-03-02 21:18:58 +0000605 /* Create "application/im-iscomposing+xml" msg body. */
606 tdata->msg->body = pjsip_iscomposing_create_body( tdata->pool, is_typing,
607 NULL, NULL, -1);
608
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000609 /* Add additional headers etc. */
610 pjsua_process_msg_data(tdata, msg_data);
611
Benny Prijonoe1a10cb2007-04-04 10:45:44 +0000612 /* Add route set */
613 pjsua_set_msg_route_set(tdata, &pjsua_var.acc[acc_id].route_set);
614
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000615 /* Create data to reauthenticate */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000616 im_data = PJ_POOL_ZALLOC_T(tdata->pool, pjsua_im_data);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000617 im_data->acc_id = acc_id;
618
Benny Prijonob0808372006-03-02 21:18:58 +0000619 /* Send request (statefully) */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000620 status = pjsip_endpt_send_request( pjsua_var.endpt, tdata, -1,
621 im_data, &typing_callback);
Benny Prijonob0808372006-03-02 21:18:58 +0000622 if (status != PJ_SUCCESS) {
623 pjsua_perror(THIS_FILE, "Unable to send request", status);
624 return status;
625 }
626
627 return PJ_SUCCESS;
628}
629
630
631/*
632 * Init pjsua IM module.
633 */
634pj_status_t pjsua_im_init(void)
635{
636 const pj_str_t msg_tag = { "MESSAGE", 7 };
Benny Prijonoc8141a82006-08-20 09:12:19 +0000637 const pj_str_t STR_MIME_TEXT_PLAIN = { "text/plain", 10 };
638 const pj_str_t STR_MIME_APP_ISCOMPOSING =
639 { "application/im-iscomposing+xml", 30 };
Benny Prijonob0808372006-03-02 21:18:58 +0000640 pj_status_t status;
641
642 /* Register module */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000643 status = pjsip_endpt_register_module(pjsua_var.endpt, &mod_pjsua_im);
Benny Prijonob0808372006-03-02 21:18:58 +0000644 if (status != PJ_SUCCESS)
645 return status;
646
647 /* Register support for MESSAGE method. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000648 pjsip_endpt_add_capability( pjsua_var.endpt, &mod_pjsua_im, PJSIP_H_ALLOW,
Benny Prijonob0808372006-03-02 21:18:58 +0000649 NULL, 1, &msg_tag);
650
Benny Prijonoc8141a82006-08-20 09:12:19 +0000651 /* Register support for "application/im-iscomposing+xml" content */
652 pjsip_endpt_add_capability( pjsua_var.endpt, &mod_pjsua_im, PJSIP_H_ACCEPT,
653 NULL, 1, &STR_MIME_APP_ISCOMPOSING);
654
655 /* Register support for "text/plain" content */
656 pjsip_endpt_add_capability( pjsua_var.endpt, &mod_pjsua_im, PJSIP_H_ACCEPT,
657 NULL, 1, &STR_MIME_TEXT_PLAIN);
658
Benny Prijonob0808372006-03-02 21:18:58 +0000659 return PJ_SUCCESS;
660}
661