blob: eba2925a94f80198a0557db984ed928cad0802da [file] [log] [blame]
Benny Prijonob0808372006-03-02 21:18:58 +00001/* $Id$ */
2/*
Benny Prijono844653c2008-12-23 17:27:53 +00003 * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
Benny Prijono32177c02008-06-20 22:44:47 +00004 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
Benny Prijonob0808372006-03-02 21:18:58 +00005 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20#include <pjsua-lib/pjsua.h>
Benny Prijonoeebe9af2006-06-13 22:57:13 +000021#include <pjsua-lib/pjsua_internal.h>
Benny Prijonob0808372006-03-02 21:18:58 +000022
Benny Prijonob0808372006-03-02 21:18:58 +000023
Benny Prijonoeebe9af2006-06-13 22:57:13 +000024#define THIS_FILE "pjsua_im.h"
Benny Prijonob0808372006-03-02 21:18:58 +000025
26
27/* Declare MESSAGE method */
28/* We put PJSIP_MESSAGE_METHOD as the enum here, so that when
29 * somebody add that method into pjsip_method_e in sip_msg.h we
30 * will get an error here.
31 */
32enum
33{
34 PJSIP_MESSAGE_METHOD = PJSIP_OTHER_METHOD
35};
36
37const pjsip_method pjsip_message_method =
38{
Benny Prijonoba5926a2007-05-02 11:29:37 +000039 (pjsip_method_e) PJSIP_MESSAGE_METHOD,
Benny Prijonob0808372006-03-02 21:18:58 +000040 { "MESSAGE", 7 }
41};
42
43
44/* Proto */
45static pj_bool_t im_on_rx_request(pjsip_rx_data *rdata);
46
Benny Prijonoeebe9af2006-06-13 22:57:13 +000047
Benny Prijonob0808372006-03-02 21:18:58 +000048/* The module instance. */
49static pjsip_module mod_pjsua_im =
50{
51 NULL, NULL, /* prev, next. */
52 { "mod-pjsua-im", 12 }, /* Name. */
53 -1, /* Id */
54 PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */
55 NULL, /* load() */
56 NULL, /* start() */
57 NULL, /* stop() */
58 NULL, /* unload() */
59 &im_on_rx_request, /* on_rx_request() */
60 NULL, /* on_rx_response() */
61 NULL, /* on_tx_request. */
62 NULL, /* on_tx_response() */
63 NULL, /* on_tsx_state() */
64
65};
66
67
68/* MIME constants. */
69static const pj_str_t STR_MIME_APP = { "application", 11 };
70static const pj_str_t STR_MIME_ISCOMPOSING = { "im-iscomposing+xml", 18 };
71static const pj_str_t STR_MIME_TEXT = { "text", 4 };
72static const pj_str_t STR_MIME_PLAIN = { "plain", 5 };
73
74
75/* Check if content type is acceptable */
Benny Prijonof4b538d2007-05-14 16:45:20 +000076#if 0
Benny Prijonob0808372006-03-02 21:18:58 +000077static pj_bool_t acceptable_message(const pjsip_media_type *mime)
78{
79 return (pj_stricmp(&mime->type, &STR_MIME_TEXT)==0 &&
80 pj_stricmp(&mime->subtype, &STR_MIME_PLAIN)==0)
81 ||
82 (pj_stricmp(&mime->type, &STR_MIME_APP)==0 &&
83 pj_stricmp(&mime->subtype, &STR_MIME_ISCOMPOSING)==0);
84}
Benny Prijonof4b538d2007-05-14 16:45:20 +000085#endif
Benny Prijonob0808372006-03-02 21:18:58 +000086
87/**
88 * Create Accept header for MESSAGE.
89 */
90pjsip_accept_hdr* pjsua_im_create_accept(pj_pool_t *pool)
91{
92 /* Create Accept header. */
93 pjsip_accept_hdr *accept;
94
95 accept = pjsip_accept_hdr_create(pool);
96 accept->values[0] = pj_str("text/plain");
97 accept->values[1] = pj_str("application/im-iscomposing+xml");
98 accept->count = 2;
99
100 return accept;
101}
102
103/**
104 * Private: check if we can accept the message.
105 */
106pj_bool_t pjsua_im_accept_pager(pjsip_rx_data *rdata,
Benny Prijonof9c668f2006-03-06 15:14:59 +0000107 pjsip_accept_hdr **p_accept_hdr)
Benny Prijonob0808372006-03-02 21:18:58 +0000108{
Benny Prijonof4b538d2007-05-14 16:45:20 +0000109 /* Some UA sends text/html, so this check will break */
110#if 0
Benny Prijonob0808372006-03-02 21:18:58 +0000111 pjsip_ctype_hdr *ctype;
112 pjsip_msg *msg;
113
114 msg = rdata->msg_info.msg;
115
116 /* Request MUST have message body, with Content-Type equal to
117 * "text/plain".
118 */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000119 ctype = (pjsip_ctype_hdr*)
120 pjsip_msg_find_hdr(msg, PJSIP_H_CONTENT_TYPE, NULL);
Benny Prijonob0808372006-03-02 21:18:58 +0000121 if (msg->body == NULL || ctype == NULL ||
122 !acceptable_message(&ctype->media))
123 {
124 /* Create Accept header. */
125 if (p_accept_hdr)
126 *p_accept_hdr = pjsua_im_create_accept(rdata->tp_info.pool);
127
128 return PJ_FALSE;
129 }
Benny Prijonof4b538d2007-05-14 16:45:20 +0000130#else
Benny Prijono0fe5acff2008-06-28 00:39:58 +0000131 pjsip_msg *msg;
132
133 msg = rdata->msg_info.msg;
134 if (msg->body == NULL) {
135 /* Create Accept header. */
136 if (p_accept_hdr)
137 *p_accept_hdr = pjsua_im_create_accept(rdata->tp_info.pool);
138
139 return PJ_FALSE;
140 }
Benny Prijonof4b538d2007-05-14 16:45:20 +0000141#endif
Benny Prijonob0808372006-03-02 21:18:58 +0000142
143 return PJ_TRUE;
144}
145
146/**
147 * Private: process pager message.
148 * This may trigger pjsua_ui_on_pager() or pjsua_ui_on_typing().
149 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000150void pjsua_im_process_pager(int call_id, const pj_str_t *from,
Benny Prijonob0808372006-03-02 21:18:58 +0000151 const pj_str_t *to, pjsip_rx_data *rdata)
152{
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000153 pjsip_contact_hdr *contact_hdr;
154 pj_str_t contact;
Benny Prijonob0808372006-03-02 21:18:58 +0000155 pjsip_msg_body *body = rdata->msg_info.msg->body;
156
157 /* Body MUST have been checked before */
158 pj_assert(body != NULL);
159
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000160
161 /* Build remote contact */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000162 contact_hdr = (pjsip_contact_hdr*)
163 pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000164 NULL);
165 if (contact_hdr) {
Benny Prijonoa1e69682007-05-11 15:14:34 +0000166 contact.ptr = (char*) pj_pool_alloc(rdata->tp_info.pool,
167 PJSIP_MAX_URL_SIZE);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000168 contact.slen = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR,
169 contact_hdr->uri, contact.ptr,
170 PJSIP_MAX_URL_SIZE);
171 } else {
172 contact.slen = 0;
173 }
174
Benny Prijonof4b538d2007-05-14 16:45:20 +0000175 if (pj_stricmp(&body->content_type.type, &STR_MIME_APP)==0 &&
176 pj_stricmp(&body->content_type.subtype, &STR_MIME_ISCOMPOSING)==0)
Benny Prijonob0808372006-03-02 21:18:58 +0000177 {
Benny Prijonob0808372006-03-02 21:18:58 +0000178 /* Expecting typing indication */
Benny Prijonob0808372006-03-02 21:18:58 +0000179 pj_status_t status;
180 pj_bool_t is_typing;
181
Benny Prijonoa1e69682007-05-11 15:14:34 +0000182 status = pjsip_iscomposing_parse(rdata->tp_info.pool, (char*)body->data,
183 body->len, &is_typing, NULL, NULL,
184 NULL );
Benny Prijonob0808372006-03-02 21:18:58 +0000185 if (status != PJ_SUCCESS) {
186 pjsua_perror(THIS_FILE, "Invalid MESSAGE body", status);
187 return;
188 }
189
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000190 if (pjsua_var.ua_cfg.cb.on_typing) {
191 (*pjsua_var.ua_cfg.cb.on_typing)(call_id, from, to, &contact,
192 is_typing);
193 }
Benny Prijonof4b538d2007-05-14 16:45:20 +0000194
Benny Prijonoba736c42008-07-10 20:45:03 +0000195 if (pjsua_var.ua_cfg.cb.on_typing2) {
196 pjsua_acc_id acc_id;
197
198 if (call_id == PJSUA_INVALID_ID) {
199 acc_id = pjsua_acc_find_for_incoming(rdata);
200 } else {
201 pjsua_call *call = &pjsua_var.calls[call_id];
202 acc_id = call->acc_id;
203 }
204
205
206 (*pjsua_var.ua_cfg.cb.on_typing2)(call_id, from, to, &contact,
207 is_typing, rdata, acc_id);
208 }
209
Benny Prijonof4b538d2007-05-14 16:45:20 +0000210 } else {
211 pj_str_t mime_type;
212 char buf[256];
213 pjsip_media_type *m;
214 pj_str_t text_body;
215
216 /* Save text body */
Benny Prijono38507402007-05-15 11:15:58 +0000217 text_body.ptr = (char*)rdata->msg_info.msg->body->data;
Benny Prijonof4b538d2007-05-14 16:45:20 +0000218 text_body.slen = rdata->msg_info.msg->body->len;
219
220 /* Get mime type */
221 m = &rdata->msg_info.msg->body->content_type;
222 mime_type.ptr = buf;
223 mime_type.slen = pj_ansi_snprintf(buf, sizeof(buf),
224 "%.*s/%.*s",
225 (int)m->type.slen,
226 m->type.ptr,
227 (int)m->subtype.slen,
228 m->subtype.ptr);
229 if (mime_type.slen < 1)
230 mime_type.slen = 0;
231
232 if (pjsua_var.ua_cfg.cb.on_pager) {
233 (*pjsua_var.ua_cfg.cb.on_pager)(call_id, from, to, &contact,
234 &mime_type, &text_body);
235 }
Benny Prijonob0808372006-03-02 21:18:58 +0000236
Benny Prijonobbeb3992007-05-21 13:48:35 +0000237 if (pjsua_var.ua_cfg.cb.on_pager2) {
Benny Prijonoba736c42008-07-10 20:45:03 +0000238 pjsua_acc_id acc_id;
239
240 if (call_id == PJSUA_INVALID_ID) {
241 acc_id = pjsua_acc_find_for_incoming(rdata);
242 } else {
243 pjsua_call *call = &pjsua_var.calls[call_id];
244 acc_id = call->acc_id;
245 }
246
Benny Prijonobbeb3992007-05-21 13:48:35 +0000247 (*pjsua_var.ua_cfg.cb.on_pager2)(call_id, from, to, &contact,
Benny Prijonoba736c42008-07-10 20:45:03 +0000248 &mime_type, &text_body, rdata,
249 acc_id);
Benny Prijonobbeb3992007-05-21 13:48:35 +0000250 }
251 }
Benny Prijonob0808372006-03-02 21:18:58 +0000252}
253
254
255/*
256 * Handler to receive incoming MESSAGE
257 */
258static pj_bool_t im_on_rx_request(pjsip_rx_data *rdata)
259{
260 pj_str_t from, to;
261 pjsip_accept_hdr *accept_hdr;
262 pjsip_msg *msg;
263 pj_status_t status;
264
265 msg = rdata->msg_info.msg;
266
267 /* Only want to handle MESSAGE requests. */
268 if (pjsip_method_cmp(&msg->line.req.method, &pjsip_message_method) != 0) {
269 return PJ_FALSE;
270 }
271
272
273 /* Should not have any transaction attached to rdata. */
274 PJ_ASSERT_RETURN(pjsip_rdata_get_tsx(rdata)==NULL, PJ_FALSE);
275
276 /* Should not have any dialog attached to rdata. */
277 PJ_ASSERT_RETURN(pjsip_rdata_get_dlg(rdata)==NULL, PJ_FALSE);
278
279 /* Check if we can accept the message. */
280 if (!pjsua_im_accept_pager(rdata, &accept_hdr)) {
281 pjsip_hdr hdr_list;
282
283 pj_list_init(&hdr_list);
284 pj_list_push_back(&hdr_list, accept_hdr);
285
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000286 pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata,
Benny Prijonob0808372006-03-02 21:18:58 +0000287 PJSIP_SC_NOT_ACCEPTABLE_HERE, NULL,
288 &hdr_list, NULL);
289 return PJ_TRUE;
290 }
291
292 /* Respond with 200 first, so that remote doesn't retransmit in case
293 * the UI takes too long to process the message.
294 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000295 status = pjsip_endpt_respond( pjsua_var.endpt, NULL, rdata, 200, NULL,
Benny Prijonob0808372006-03-02 21:18:58 +0000296 NULL, NULL, NULL);
297
Benny Prijono8b1889b2006-06-06 18:40:40 +0000298 /* For the source URI, we use Contact header if present, since
299 * Contact header contains the port number information. If this is
300 * not available, then use From header.
301 */
Benny Prijono38507402007-05-15 11:15:58 +0000302 from.ptr = (char*)pj_pool_alloc(rdata->tp_info.pool, PJSIP_MAX_URL_SIZE);
Benny Prijonof4b538d2007-05-14 16:45:20 +0000303 from.slen = pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR,
304 rdata->msg_info.from->uri,
305 from.ptr, PJSIP_MAX_URL_SIZE);
Benny Prijono8b1889b2006-06-06 18:40:40 +0000306
Benny Prijonob0808372006-03-02 21:18:58 +0000307 if (from.slen < 1)
308 from = pj_str("<--URI is too long-->");
309
310 /* Build the To text. */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000311 to.ptr = (char*) pj_pool_alloc(rdata->tp_info.pool, PJSIP_MAX_URL_SIZE);
Benny Prijonob0808372006-03-02 21:18:58 +0000312 to.slen = pjsip_uri_print( PJSIP_URI_IN_FROMTO_HDR,
313 rdata->msg_info.to->uri,
314 to.ptr, PJSIP_MAX_URL_SIZE);
315 if (to.slen < 1)
316 to = pj_str("<--URI is too long-->");
317
318 /* Process pager. */
319 pjsua_im_process_pager(-1, &from, &to, rdata);
320
321 /* Done. */
322 return PJ_TRUE;
323}
324
325
326/* Outgoing IM callback. */
327static void im_callback(void *token, pjsip_event *e)
328{
Benny Prijonoa1e69682007-05-11 15:14:34 +0000329 pjsua_im_data *im_data = (pjsua_im_data*) token;
Benny Prijonob0808372006-03-02 21:18:58 +0000330
331 if (e->type == PJSIP_EVENT_TSX_STATE) {
332
333 pjsip_transaction *tsx = e->body.tsx_state.tsx;
334
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000335 /* Ignore provisional response, if any */
336 if (tsx->status_code < 200)
337 return;
338
339
340 /* Handle authentication challenges */
341 if (e->body.tsx_state.type == PJSIP_EVENT_RX_MSG &&
342 (tsx->status_code == 401 || tsx->status_code == 407))
343 {
344 pjsip_rx_data *rdata = e->body.tsx_state.src.rdata;
345 pjsip_tx_data *tdata;
346 pjsip_auth_clt_sess auth;
347 pj_status_t status;
348
349 PJ_LOG(4,(THIS_FILE, "Resending IM with authentication"));
350
351 /* Create temporary authentication session */
352 pjsip_auth_clt_init(&auth,pjsua_var.endpt,rdata->tp_info.pool, 0);
353
354 pjsip_auth_clt_set_credentials(&auth,
355 pjsua_var.acc[im_data->acc_id].cred_cnt,
356 pjsua_var.acc[im_data->acc_id].cred);
357
Benny Prijono48ab2b72007-11-08 09:24:30 +0000358 pjsip_auth_clt_set_prefs(&auth,
359 &pjsua_var.acc[im_data->acc_id].cfg.auth_pref);
360
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000361 status = pjsip_auth_clt_reinit_req(&auth, rdata, tsx->last_tx,
362 &tdata);
363 if (status == PJ_SUCCESS) {
364 pjsua_im_data *im_data2;
365
366 /* Must duplicate im_data */
367 im_data2 = pjsua_im_data_dup(tdata->pool, im_data);
368
369 /* Re-send request */
370 status = pjsip_endpt_send_request( pjsua_var.endpt, tdata, -1,
371 im_data2, &im_callback);
372 if (status == PJ_SUCCESS) {
373 /* Done */
374 return;
375 }
376 }
377 }
378
Benny Prijonob0808372006-03-02 21:18:58 +0000379 if (tsx->status_code/100 == 2) {
380 PJ_LOG(4,(THIS_FILE,
381 "Message \'%s\' delivered successfully",
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000382 im_data->body.ptr));
Benny Prijonob0808372006-03-02 21:18:58 +0000383 } else {
384 PJ_LOG(3,(THIS_FILE,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000385 "Failed to deliver message \'%s\': %d/%.*s",
386 im_data->body.ptr,
387 tsx->status_code,
388 (int)tsx->status_text.slen,
389 tsx->status_text.ptr));
Benny Prijonob0808372006-03-02 21:18:58 +0000390 }
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000391
Benny Prijono4da0b1d2007-06-11 18:22:54 +0000392 if (pjsua_var.ua_cfg.cb.on_pager_status) {
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000393 pjsua_var.ua_cfg.cb.on_pager_status(im_data->call_id,
394 &im_data->to,
395 &im_data->body,
396 im_data->user_data,
Benny Prijonoba5926a2007-05-02 11:29:37 +0000397 (pjsip_status_code)
398 tsx->status_code,
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000399 &tsx->status_text);
Benny Prijono4da0b1d2007-06-11 18:22:54 +0000400 }
401
402 if (pjsua_var.ua_cfg.cb.on_pager_status2) {
403 pjsip_rx_data *rdata;
404
405 if (e->body.tsx_state.type == PJSIP_EVENT_RX_MSG)
406 rdata = e->body.tsx_state.src.rdata;
407 else
408 rdata = NULL;
409
410 pjsua_var.ua_cfg.cb.on_pager_status2(im_data->call_id,
411 &im_data->to,
412 &im_data->body,
413 im_data->user_data,
414 (pjsip_status_code)
415 tsx->status_code,
416 &tsx->status_text,
Benny Prijono0a982002007-06-12 16:22:09 +0000417 tsx->last_tx,
Benny Prijonoba736c42008-07-10 20:45:03 +0000418 rdata, im_data->acc_id);
Benny Prijono4da0b1d2007-06-11 18:22:54 +0000419 }
Benny Prijonob0808372006-03-02 21:18:58 +0000420 }
421}
422
423
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000424/* Outgoing typing indication callback.
425 * (used to reauthenticate request)
Benny Prijonob0808372006-03-02 21:18:58 +0000426 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000427static void typing_callback(void *token, pjsip_event *e)
428{
Benny Prijonoa1e69682007-05-11 15:14:34 +0000429 pjsua_im_data *im_data = (pjsua_im_data*) token;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000430
431 if (e->type == PJSIP_EVENT_TSX_STATE) {
432
433 pjsip_transaction *tsx = e->body.tsx_state.tsx;
434
435 /* Ignore provisional response, if any */
436 if (tsx->status_code < 200)
437 return;
438
439 /* Handle authentication challenges */
440 if (e->body.tsx_state.type == PJSIP_EVENT_RX_MSG &&
441 (tsx->status_code == 401 || tsx->status_code == 407))
442 {
443 pjsip_rx_data *rdata = e->body.tsx_state.src.rdata;
444 pjsip_tx_data *tdata;
445 pjsip_auth_clt_sess auth;
446 pj_status_t status;
447
448 PJ_LOG(4,(THIS_FILE, "Resending IM with authentication"));
449
450 /* Create temporary authentication session */
451 pjsip_auth_clt_init(&auth,pjsua_var.endpt,rdata->tp_info.pool, 0);
452
453 pjsip_auth_clt_set_credentials(&auth,
454 pjsua_var.acc[im_data->acc_id].cred_cnt,
455 pjsua_var.acc[im_data->acc_id].cred);
456
Benny Prijono48ab2b72007-11-08 09:24:30 +0000457 pjsip_auth_clt_set_prefs(&auth,
458 &pjsua_var.acc[im_data->acc_id].cfg.auth_pref);
459
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000460 status = pjsip_auth_clt_reinit_req(&auth, rdata, tsx->last_tx,
461 &tdata);
462 if (status == PJ_SUCCESS) {
463 pjsua_im_data *im_data2;
464
465 /* Must duplicate im_data */
466 im_data2 = pjsua_im_data_dup(tdata->pool, im_data);
467
468 /* Re-send request */
469 status = pjsip_endpt_send_request( pjsua_var.endpt, tdata, -1,
470 im_data2, &typing_callback);
471 if (status == PJ_SUCCESS) {
472 /* Done */
473 return;
474 }
475 }
476 }
477
478 }
479}
480
481
482/*
483 * Send instant messaging outside dialog, using the specified account for
484 * route set and authentication.
485 */
486PJ_DEF(pj_status_t) pjsua_im_send( pjsua_acc_id acc_id,
487 const pj_str_t *to,
488 const pj_str_t *mime_type,
489 const pj_str_t *content,
490 const pjsua_msg_data *msg_data,
491 void *user_data)
Benny Prijonob0808372006-03-02 21:18:58 +0000492{
493 pjsip_tx_data *tdata;
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000494 const pj_str_t mime_text_plain = pj_str("text/plain");
Benny Prijonob0808372006-03-02 21:18:58 +0000495 const pj_str_t STR_CONTACT = { "Contact", 7 };
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000496 pjsip_media_type media_type;
497 pjsua_im_data *im_data;
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000498 pj_str_t contact;
Benny Prijonob0808372006-03-02 21:18:58 +0000499 pj_status_t status;
500
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000501 /* To and message body must be specified. */
502 PJ_ASSERT_RETURN(to && content, PJ_EINVAL);
503
Benny Prijonob0808372006-03-02 21:18:58 +0000504 /* Create request. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000505 status = pjsip_endpt_create_request(pjsua_var.endpt,
506 &pjsip_message_method, to,
507 &pjsua_var.acc[acc_id].cfg.id,
508 to, NULL, NULL, -1, NULL, &tdata);
Benny Prijonob0808372006-03-02 21:18:58 +0000509 if (status != PJ_SUCCESS) {
510 pjsua_perror(THIS_FILE, "Unable to create request", status);
511 return status;
512 }
513
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000514 /* If account is locked to specific transport, then set transport to
515 * the request.
516 */
517 if (pjsua_var.acc[acc_id].cfg.transport_id != PJSUA_INVALID_ID) {
518 pjsip_tpselector tp_sel;
519
520 pjsua_init_tpselector(pjsua_var.acc[acc_id].cfg.transport_id, &tp_sel);
521 pjsip_tx_data_set_transport(tdata, &tp_sel);
522 }
523
Benny Prijonob0808372006-03-02 21:18:58 +0000524 /* Add accept header. */
525 pjsip_msg_add_hdr( tdata->msg,
526 (pjsip_hdr*)pjsua_im_create_accept(tdata->pool));
527
528 /* Add contact. */
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000529 status = pjsua_acc_create_uac_contact(tdata->pool, &contact, acc_id, to);
530 if (status != PJ_SUCCESS) {
531 pjsua_perror(THIS_FILE, "Unable to generate Contact header", status);
532 pjsip_tx_data_dec_ref(tdata);
533 return status;
534 }
535
Benny Prijonob0808372006-03-02 21:18:58 +0000536 pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)
Benny Prijonodc39fe82006-05-26 12:17:46 +0000537 pjsip_generic_string_hdr_create(tdata->pool,
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000538 &STR_CONTACT, &contact));
Benny Prijonob0808372006-03-02 21:18:58 +0000539
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000540 /* Create IM data to keep message details and give it back to
541 * application on the callback
Benny Prijonob0808372006-03-02 21:18:58 +0000542 */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000543 im_data = PJ_POOL_ZALLOC_T(tdata->pool, pjsua_im_data);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000544 im_data->acc_id = acc_id;
545 im_data->call_id = PJSUA_INVALID_ID;
546 pj_strdup_with_null(tdata->pool, &im_data->to, to);
547 pj_strdup_with_null(tdata->pool, &im_data->body, content);
548 im_data->user_data = user_data;
549
550
551 /* Set default media type if none is specified */
552 if (mime_type == NULL) {
553 mime_type = &mime_text_plain;
554 }
555
556 /* Parse MIME type */
557 pjsua_parse_media_type(tdata->pool, mime_type, &media_type);
Benny Prijonob0808372006-03-02 21:18:58 +0000558
559 /* Add message body */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000560 tdata->msg->body = pjsip_msg_body_create( tdata->pool, &media_type.type,
561 &media_type.subtype,
562 &im_data->body);
Benny Prijonob0808372006-03-02 21:18:58 +0000563 if (tdata->msg->body == NULL) {
564 pjsua_perror(THIS_FILE, "Unable to create msg body", PJ_ENOMEM);
565 pjsip_tx_data_dec_ref(tdata);
566 return PJ_ENOMEM;
567 }
568
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000569 /* Add additional headers etc. */
570 pjsua_process_msg_data(tdata, msg_data);
571
572 /* Add route set */
573 pjsua_set_msg_route_set(tdata, &pjsua_var.acc[acc_id].route_set);
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, &im_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
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000587/*
Benny Prijonob0808372006-03-02 21:18:58 +0000588 * Send typing indication outside dialog.
589 */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000590PJ_DEF(pj_status_t) pjsua_im_typing( pjsua_acc_id acc_id,
591 const pj_str_t *to,
592 pj_bool_t is_typing,
593 const pjsua_msg_data *msg_data)
Benny Prijonob0808372006-03-02 21:18:58 +0000594{
Benny Prijono8b1889b2006-06-06 18:40:40 +0000595 const pj_str_t STR_CONTACT = { "Contact", 7 };
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000596 pjsua_im_data *im_data;
Benny Prijonob0808372006-03-02 21:18:58 +0000597 pjsip_tx_data *tdata;
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000598 pj_str_t contact;
Benny Prijonob0808372006-03-02 21:18:58 +0000599 pj_status_t status;
600
601 /* Create request. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000602 status = pjsip_endpt_create_request( pjsua_var.endpt, &pjsip_message_method,
603 to, &pjsua_var.acc[acc_id].cfg.id,
604 to, NULL, NULL, -1, NULL, &tdata);
Benny Prijonob0808372006-03-02 21:18:58 +0000605 if (status != PJ_SUCCESS) {
606 pjsua_perror(THIS_FILE, "Unable to create request", status);
607 return status;
608 }
609
610
Benny Prijono62c5c5b2007-01-13 23:22:40 +0000611 /* If account is locked to specific transport, then set transport to
612 * the request.
613 */
614 if (pjsua_var.acc[acc_id].cfg.transport_id != PJSUA_INVALID_ID) {
615 pjsip_tpselector tp_sel;
616
617 pjsua_init_tpselector(pjsua_var.acc[acc_id].cfg.transport_id, &tp_sel);
618 pjsip_tx_data_set_transport(tdata, &tp_sel);
619 }
620
Benny Prijono8b1889b2006-06-06 18:40:40 +0000621 /* Add accept header. */
622 pjsip_msg_add_hdr( tdata->msg,
623 (pjsip_hdr*)pjsua_im_create_accept(tdata->pool));
624
625
626 /* Add contact. */
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000627 status = pjsua_acc_create_uac_contact(tdata->pool, &contact, acc_id, to);
628 if (status != PJ_SUCCESS) {
629 pjsua_perror(THIS_FILE, "Unable to generate Contact header", status);
630 pjsip_tx_data_dec_ref(tdata);
631 return status;
632 }
633
Benny Prijono8b1889b2006-06-06 18:40:40 +0000634 pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)
635 pjsip_generic_string_hdr_create(tdata->pool,
Benny Prijonoc570f2d2006-07-18 00:33:02 +0000636 &STR_CONTACT, &contact));
Benny Prijono8b1889b2006-06-06 18:40:40 +0000637
638
Benny Prijonob0808372006-03-02 21:18:58 +0000639 /* Create "application/im-iscomposing+xml" msg body. */
640 tdata->msg->body = pjsip_iscomposing_create_body( tdata->pool, is_typing,
641 NULL, NULL, -1);
642
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000643 /* Add additional headers etc. */
644 pjsua_process_msg_data(tdata, msg_data);
645
Benny Prijonoe1a10cb2007-04-04 10:45:44 +0000646 /* Add route set */
647 pjsua_set_msg_route_set(tdata, &pjsua_var.acc[acc_id].route_set);
648
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000649 /* Create data to reauthenticate */
Benny Prijonoa1e69682007-05-11 15:14:34 +0000650 im_data = PJ_POOL_ZALLOC_T(tdata->pool, pjsua_im_data);
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000651 im_data->acc_id = acc_id;
652
Benny Prijonob0808372006-03-02 21:18:58 +0000653 /* Send request (statefully) */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000654 status = pjsip_endpt_send_request( pjsua_var.endpt, tdata, -1,
655 im_data, &typing_callback);
Benny Prijonob0808372006-03-02 21:18:58 +0000656 if (status != PJ_SUCCESS) {
657 pjsua_perror(THIS_FILE, "Unable to send request", status);
658 return status;
659 }
660
661 return PJ_SUCCESS;
662}
663
664
665/*
666 * Init pjsua IM module.
667 */
668pj_status_t pjsua_im_init(void)
669{
670 const pj_str_t msg_tag = { "MESSAGE", 7 };
Benny Prijonoc8141a82006-08-20 09:12:19 +0000671 const pj_str_t STR_MIME_TEXT_PLAIN = { "text/plain", 10 };
672 const pj_str_t STR_MIME_APP_ISCOMPOSING =
673 { "application/im-iscomposing+xml", 30 };
Benny Prijonob0808372006-03-02 21:18:58 +0000674 pj_status_t status;
675
676 /* Register module */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000677 status = pjsip_endpt_register_module(pjsua_var.endpt, &mod_pjsua_im);
Benny Prijonob0808372006-03-02 21:18:58 +0000678 if (status != PJ_SUCCESS)
679 return status;
680
681 /* Register support for MESSAGE method. */
Benny Prijonoeebe9af2006-06-13 22:57:13 +0000682 pjsip_endpt_add_capability( pjsua_var.endpt, &mod_pjsua_im, PJSIP_H_ALLOW,
Benny Prijonob0808372006-03-02 21:18:58 +0000683 NULL, 1, &msg_tag);
684
Benny Prijonoc8141a82006-08-20 09:12:19 +0000685 /* Register support for "application/im-iscomposing+xml" content */
686 pjsip_endpt_add_capability( pjsua_var.endpt, &mod_pjsua_im, PJSIP_H_ACCEPT,
687 NULL, 1, &STR_MIME_APP_ISCOMPOSING);
688
689 /* Register support for "text/plain" content */
690 pjsip_endpt_add_capability( pjsua_var.endpt, &mod_pjsua_im, PJSIP_H_ACCEPT,
691 NULL, 1, &STR_MIME_TEXT_PLAIN);
692
Benny Prijonob0808372006-03-02 21:18:58 +0000693 return PJ_SUCCESS;
694}
695