im: fix response/timeout for multiparty/conference messages
[siplcs.git] / src / core / sipe-im.c
blob9e9ee7754530732c0c9f591ef138d30094f57a95
1 /**
2 * @file sipe-im.c
4 * pidgin-sipe
6 * Copyright (C) 2011 SIPE Project <http://sipe.sourceforge.net/>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
27 #include <stdlib.h>
28 #include <string.h>
30 #include <glib.h>
32 #include "sipe-common.h"
33 #include "sipmsg.h"
34 #include "sip-transport.h"
35 #include "sipe-backend.h"
36 #include "sipe-buddy.h"
37 #include "sipe-chat.h"
38 #include "sipe-core.h"
39 #include "sipe-core-private.h"
40 #include "sipe-dialog.h"
41 #include "sipe-ft.h"
42 #include "sipe-groupchat.h"
43 #include "sipe-im.h"
44 #include "sipe-nls.h"
45 #include "sipe-session.h"
46 #include "sipe-user.h"
47 #include "sipe-utils.h"
48 #include "sipe-xml.h"
51 * Hash key template for unconfirmed messages
53 * Call-ID Recipient URI (or empty)
54 * | |
55 * | SIP method | CSeq
56 * | | | | */
57 #define UNCONFIRMED_KEY_TEMPLATE(method, cseq) "<%s><" method "><%s><" cseq
59 /* key must be g_free()'d */
60 static gchar *get_unconfirmed_message_key(const gchar *callid,
61 unsigned int cseq,
62 const gchar *with)
64 return(g_strdup_printf(UNCONFIRMED_KEY_TEMPLATE("%s", "%d>"),
65 callid,
66 with ? "MESSAGE" : "INVITE",
67 with ? with : "",
68 cseq));
71 static void insert_unconfirmed_message(struct sip_session *session,
72 struct sip_dialog *dialog,
73 const gchar *with,
74 const gchar *body,
75 const gchar *content_type)
77 gchar *key = get_unconfirmed_message_key(dialog->callid, dialog->cseq + 1, with);
78 struct queued_message *message = g_new0(struct queued_message, 1);
80 message->body = g_strdup(body);
81 if (content_type != NULL)
82 message->content_type = g_strdup(content_type);
83 message->cseq = dialog->cseq + 1;
85 g_hash_table_insert(session->unconfirmed_messages, key, message);
86 SIPE_DEBUG_INFO("insert_unconfirmed_message: added %s to list (count=%d)",
87 key, g_hash_table_size(session->unconfirmed_messages));
90 static gboolean remove_unconfirmed_message(struct sip_session *session,
91 const gchar *key)
93 gboolean found = g_hash_table_remove(session->unconfirmed_messages, key);
94 if (found) {
95 SIPE_DEBUG_INFO("remove_unconfirmed_message: removed %s from list (count=%d)",
96 key, g_hash_table_size(session->unconfirmed_messages));
97 } else {
98 SIPE_DEBUG_INFO("remove_unconfirmed_message: key %s not found",
99 key);
101 return(found);
104 static void sipe_refer_notify(struct sipe_core_private *sipe_private,
105 struct sip_session *session,
106 const gchar *who,
107 int status,
108 const gchar *desc)
110 gchar *hdr;
111 gchar *body;
112 struct sip_dialog *dialog = sipe_dialog_find(session, who);
114 hdr = g_strdup_printf(
115 "Event: refer\r\n"
116 "Subscription-State: %s\r\n"
117 "Content-Type: message/sipfrag\r\n",
118 status >= 200 ? "terminated" : "active");
120 body = g_strdup_printf(
121 "SIP/2.0 %d %s\r\n",
122 status, desc);
124 sip_transport_request(sipe_private,
125 "NOTIFY",
126 who,
127 who,
128 hdr,
129 body,
130 dialog,
131 NULL);
133 g_free(hdr);
134 g_free(body);
137 static gboolean process_invite_response(struct sipe_core_private *sipe_private,
138 struct sipmsg *msg,
139 struct transaction *trans)
141 gchar *with = parse_from(sipmsg_find_header(msg, "To"));
142 struct sip_session *session;
143 struct sip_dialog *dialog;
144 gchar *key;
145 struct queued_message *message;
146 struct sipmsg *request_msg = trans->msg;
148 const gchar *callid = sipmsg_find_header(msg, "Call-ID");
149 gchar *referred_by;
151 session = sipe_session_find_chat_or_im(sipe_private, callid, with);
152 if (!session) {
153 SIPE_DEBUG_INFO_NOFORMAT("process_invite_response: unable to find IM session");
154 g_free(with);
155 return FALSE;
158 dialog = sipe_dialog_find(session, with);
159 if (!dialog) {
160 SIPE_DEBUG_INFO_NOFORMAT("process_invite_response: session outgoing dialog is NULL");
161 g_free(with);
162 return FALSE;
165 sipe_dialog_parse(dialog, msg, TRUE);
167 key = get_unconfirmed_message_key(dialog->callid, sipmsg_parse_cseq(msg), NULL);
168 message = g_hash_table_lookup(session->unconfirmed_messages, key);
170 if (msg->response != 200) {
171 gchar *alias = sipe_buddy_get_alias(sipe_private, with);
172 int warning = sipmsg_parse_warning(msg, NULL);
174 SIPE_DEBUG_INFO_NOFORMAT("process_invite_response: INVITE response not 200");
176 /* cancel file transfer as rejected by server */
177 if (msg->response == 606 && /* Not acceptable all. */
178 warning == 309 && /* Message contents not allowed by policy */
179 message && g_str_has_prefix(message->content_type, "text/x-msmsgsinvite"))
181 GSList *parsed_body = sipe_ft_parse_msg_body(message->body);
182 sipe_ft_incoming_cancel(dialog, parsed_body);
183 sipe_utils_nameval_free(parsed_body);
186 if (message) {
187 /* generate error for each unprocessed message */
188 GSList *entry = session->outgoing_message_queue;
189 while (entry) {
190 struct queued_message *queued = entry->data;
191 sipe_user_present_message_undelivered(sipe_private, session, msg->response, warning, alias ? alias : with, queued->body);
192 entry = sipe_session_dequeue_message(session);
194 } else {
195 /* generate one error and remove all unprocessed messages */
196 gchar *tmp_msg = g_strdup_printf(_("Failed to invite %s"), alias ? alias : with);
197 sipe_user_present_error(sipe_private, session, tmp_msg);
198 g_free(tmp_msg);
199 while (sipe_session_dequeue_message(session));
201 g_free(alias);
203 remove_unconfirmed_message(session, key);
204 /* message is no longer valid */
205 g_free(key);
207 sipe_dialog_remove(session, with);
208 g_free(with);
210 if (session->is_groupchat) {
211 sipe_groupchat_invite_failed(sipe_private, session);
212 /* session is no longer valid */
215 return FALSE;
218 dialog->cseq = 0;
219 sip_transport_ack(sipe_private, dialog);
220 dialog->outgoing_invite = NULL;
221 dialog->is_established = TRUE;
223 referred_by = parse_from(sipmsg_find_header(request_msg, "Referred-By"));
224 if (referred_by) {
225 sipe_refer_notify(sipe_private, session, referred_by, 200, "OK");
226 g_free(referred_by);
229 /* add user to chat if it is a multiparty session */
230 if (session->chat_session &&
231 (session->chat_session->type == SIPE_CHAT_TYPE_MULTIPARTY)) {
232 sipe_backend_chat_add(session->chat_session->backend,
233 with,
234 TRUE);
237 if (session->is_groupchat) {
238 sipe_groupchat_invite_response(sipe_private, dialog);
241 if(g_slist_find_custom(dialog->supported, "ms-text-format", (GCompareFunc)g_ascii_strcasecmp)) {
242 SIPE_DEBUG_INFO_NOFORMAT("process_invite_response: remote system accepted message in INVITE");
243 sipe_session_dequeue_message(session);
246 sipe_im_process_queue(sipe_private, session);
248 remove_unconfirmed_message(session, key);
250 g_free(key);
251 g_free(with);
252 return TRUE;
255 /* EndPoints: "alice alisson" <sip:alice@atlanta.local>, <sip:bob@atlanta.local>;epid=ebca82d94d, <sip:carol@atlanta.local> */
256 static gchar *get_end_points(struct sipe_core_private *sipe_private,
257 struct sip_session *session)
259 gchar *res;
261 if (session == NULL) {
262 return NULL;
265 res = g_strdup_printf("<sip:%s>", sipe_private->username);
267 SIPE_DIALOG_FOREACH {
268 gchar *tmp = res;
269 res = g_strdup_printf("%s, <%s>", res, dialog->with);
270 g_free(tmp);
272 if (dialog->theirepid) {
273 tmp = res;
274 res = g_strdup_printf("%s;epid=%s", res, dialog->theirepid);
275 g_free(tmp);
277 } SIPE_DIALOG_FOREACH_END;
279 return res;
282 void
283 sipe_im_invite(struct sipe_core_private *sipe_private,
284 struct sip_session *session,
285 const gchar *who,
286 const gchar *msg_body,
287 const gchar *content_type,
288 const gchar *referred_by,
289 const gboolean is_triggered)
291 gchar *hdr;
292 gchar *to;
293 gchar *contact;
294 gchar *body;
295 gchar *self;
296 char *ms_text_format = NULL;
297 char *ms_conversation_id = NULL;
298 gchar *roster_manager;
299 gchar *end_points;
300 gchar *referred_by_str;
301 gboolean is_multiparty =
302 session->chat_session &&
303 (session->chat_session->type == SIPE_CHAT_TYPE_MULTIPARTY);
304 struct sip_dialog *dialog = sipe_dialog_find(session, who);
306 if (dialog && dialog->is_established) {
307 SIPE_DEBUG_INFO("session with %s already has a dialog open", who);
308 return;
311 if (!dialog) {
312 dialog = sipe_dialog_add(session);
313 dialog->callid = session->callid ? g_strdup(session->callid) : gencallid();
314 dialog->with = g_strdup(who);
317 if (!(dialog->ourtag)) {
318 dialog->ourtag = gentag();
321 to = sip_uri(who);
323 if (msg_body) {
324 char *msgtext = NULL;
325 char *base64_msg;
326 const gchar *msgr = "";
327 gchar *tmp = NULL;
329 if (!g_str_has_prefix(content_type, "text/x-msmsgsinvite")) {
330 char *msgformat;
331 gchar *msgr_value;
333 sipe_parse_html(msg_body, &msgformat, &msgtext);
334 SIPE_DEBUG_INFO("sipe_invite: msgformat=%s", msgformat);
336 msgr_value = sipmsg_get_msgr_string(msgformat);
337 g_free(msgformat);
338 if (msgr_value) {
339 msgr = tmp = g_strdup_printf(";msgr=%s", msgr_value);
340 g_free(msgr_value);
343 /* When Sipe reconnects after a crash, we are not able
344 * to send messages to contacts with which we had open
345 * conversations when the crash occured. Server sends
346 * error response with reason="This client has an IM
347 * session with the same conversation ID"
349 * Setting random Ms-Conversation-ID prevents this problem
350 * so we can continue the conversation. */
351 ms_conversation_id = g_strdup_printf("Ms-Conversation-ID: %u\r\n",
352 rand() % 1000000000);
353 } else {
354 msgtext = g_strdup(msg_body);
357 base64_msg = g_base64_encode((guchar*) msgtext, strlen(msgtext));
358 ms_text_format = g_strdup_printf("ms-text-format: %s; charset=UTF-8%s;ms-body=%s\r\n",
359 content_type ? content_type : "text/plain",
360 msgr,
361 base64_msg);
362 g_free(msgtext);
363 g_free(tmp);
364 g_free(base64_msg);
366 insert_unconfirmed_message(session, dialog, NULL,
367 msg_body, content_type);
370 contact = get_contact(sipe_private);
371 end_points = get_end_points(sipe_private, session);
372 self = sip_uri_self(sipe_private);
373 roster_manager = g_strdup_printf(
374 "Roster-Manager: %s\r\n"
375 "EndPoints: %s\r\n",
376 self,
377 end_points);
378 referred_by_str = referred_by ?
379 g_strdup_printf(
380 "Referred-By: %s\r\n",
381 referred_by)
382 : g_strdup("");
383 hdr = g_strdup_printf(
384 "Supported: ms-sender\r\n"
385 "%s"
386 "%s"
387 "%s"
388 "%s"
389 "Contact: %s\r\n%s"
390 "%s"
391 "Content-Type: application/sdp\r\n",
392 is_multiparty && sipe_strcase_equal(session->chat_session->id, self) ? roster_manager : "",
393 referred_by_str,
394 is_triggered ? "TriggeredInvite: TRUE\r\n" : "",
395 is_triggered || is_multiparty ? "Require: com.microsoft.rtc-multiparty\r\n" : "",
396 contact,
397 ms_text_format ? ms_text_format : "",
398 ms_conversation_id ? ms_conversation_id : "");
399 g_free(ms_text_format);
400 g_free(ms_conversation_id);
401 g_free(self);
403 body = g_strdup_printf(
404 "v=0\r\n"
405 "o=- 0 0 IN IP4 %s\r\n"
406 "s=session\r\n"
407 "c=IN IP4 %s\r\n"
408 "t=0 0\r\n"
409 "m=%s %d sip null\r\n"
410 "a=accept-types:" SDP_ACCEPT_TYPES "\r\n",
411 sipe_backend_network_ip_address(),
412 sipe_backend_network_ip_address(),
413 SIPE_CORE_PRIVATE_FLAG_IS(OCS2007) ? "message" : "x-ms-message",
414 sip_transport_port(sipe_private));
416 dialog->outgoing_invite = sip_transport_request(sipe_private,
417 "INVITE",
420 hdr,
421 body,
422 dialog,
423 process_invite_response);
425 g_free(to);
426 g_free(roster_manager);
427 g_free(end_points);
428 g_free(referred_by_str);
429 g_free(body);
430 g_free(hdr);
431 g_free(contact);
434 static gboolean
435 process_message_response(struct sipe_core_private *sipe_private,
436 struct sipmsg *msg,
437 SIPE_UNUSED_PARAMETER struct transaction *trans)
439 gboolean ret = TRUE;
440 gchar *with = parse_from(sipmsg_find_header(msg, "To"));
441 const gchar *callid = sipmsg_find_header(msg, "Call-ID");
442 struct sip_session *session = sipe_session_find_chat_or_im(sipe_private, callid, with);
443 struct sip_dialog *dialog;
444 gchar *key;
445 struct queued_message *message;
447 if (!session) {
448 SIPE_DEBUG_INFO_NOFORMAT("process_message_response: unable to find IM session");
449 g_free(with);
450 return FALSE;
453 dialog = sipe_dialog_find(session, with);
454 if (!dialog) {
455 SIPE_DEBUG_INFO_NOFORMAT("process_message_response: session outgoing dialog is NULL");
456 g_free(with);
457 return FALSE;
460 key = get_unconfirmed_message_key(sipmsg_find_header(msg, "Call-ID"), sipmsg_parse_cseq(msg), with);
461 message = g_hash_table_lookup(session->unconfirmed_messages, key);
463 if (msg->response >= 400) {
464 int warning = sipmsg_parse_warning(msg, NULL);
466 SIPE_DEBUG_INFO_NOFORMAT("process_message_response: MESSAGE response >= 400");
468 /* cancel file transfer as rejected by server */
469 if (msg->response == 606 && /* Not acceptable all. */
470 warning == 309 && /* Message contents not allowed by policy */
471 message && g_str_has_prefix(message->content_type, "text/x-msmsgsinvite"))
473 GSList *parsed_body = sipe_ft_parse_msg_body(msg->body);
474 sipe_ft_incoming_cancel(dialog, parsed_body);
475 sipe_utils_nameval_free(parsed_body);
478 /* drop dangling IM sessions: assume that BYE from remote never reached us */
479 if (msg->response == 408 || /* Request timeout */
480 msg->response == 480 || /* Temporarily Unavailable */
481 msg->response == 481) { /* Call/Transaction Does Not Exist */
482 sipe_im_cancel_dangling(sipe_private, session, dialog, with,
483 sipe_im_cancel_unconfirmed);
484 /* dialog is no longer valid */
485 } else {
486 gchar *alias = sipe_buddy_get_alias(sipe_private, with);
487 sipe_user_present_message_undelivered(sipe_private, session,
488 msg->response, warning,
489 alias ? alias : with,
490 message ? message->body : NULL);
491 remove_unconfirmed_message(session, key);
492 /* message is no longer valid */
493 g_free(alias);
496 ret = FALSE;
497 } else {
498 const gchar *message_id = sipmsg_find_header(msg, "Message-Id");
499 if (message_id) {
500 g_hash_table_insert(session->conf_unconfirmed_messages, g_strdup(message_id), g_strdup(message->body));
501 SIPE_DEBUG_INFO("process_message_response: added message with id %s to conf_unconfirmed_messages(count=%d)",
502 message_id, g_hash_table_size(session->conf_unconfirmed_messages));
504 remove_unconfirmed_message(session, key);
507 g_free(key);
508 g_free(with);
510 if (ret) sipe_im_process_queue(sipe_private, session);
511 return ret;
514 static gboolean
515 process_message_timeout(struct sipe_core_private *sipe_private,
516 struct sipmsg *msg,
517 SIPE_UNUSED_PARAMETER struct transaction *trans)
519 gchar *with = parse_from(sipmsg_find_header(msg, "To"));
520 const gchar *callid = sipmsg_find_header(msg, "Call-ID");
521 struct sip_session *session = sipe_session_find_chat_or_im(sipe_private, callid, with);
522 gchar *key;
523 gboolean found;
525 if (!session) {
526 SIPE_DEBUG_INFO_NOFORMAT("process_message_timeout: unable to find IM session");
527 g_free(with);
528 return TRUE;
531 /* Remove timed-out message from unconfirmed list */
532 key = get_unconfirmed_message_key(sipmsg_find_header(msg, "Call-ID"), sipmsg_parse_cseq(msg), with);
533 found = remove_unconfirmed_message(session, key);
534 g_free(key);
536 if (found) {
537 gchar *alias = sipe_buddy_get_alias(sipe_private, with);
538 sipe_user_present_message_undelivered(sipe_private, session, -1, -1,
539 alias ? alias : with,
540 msg->body);
541 g_free(alias);
544 g_free(with);
545 return TRUE;
548 static void sipe_im_send_message(struct sipe_core_private *sipe_private,
549 struct sip_dialog *dialog,
550 const gchar *msg_body,
551 const gchar *content_type)
553 gchar *hdr;
554 gchar *tmp;
555 char *msgtext = NULL;
556 const gchar *msgr = "";
557 gchar *tmp2 = NULL;
559 if (content_type == NULL)
560 content_type = "text/plain";
562 if (!g_str_has_prefix(content_type, "text/x-msmsgsinvite")) {
563 char *msgformat;
564 gchar *msgr_value;
566 sipe_parse_html(msg_body, &msgformat, &msgtext);
567 SIPE_DEBUG_INFO("sipe_send_message: msgformat=%s", msgformat);
569 msgr_value = sipmsg_get_msgr_string(msgformat);
570 g_free(msgformat);
571 if (msgr_value) {
572 msgr = tmp2 = g_strdup_printf(";msgr=%s", msgr_value);
573 g_free(msgr_value);
575 } else {
576 msgtext = g_strdup(msg_body);
579 tmp = get_contact(sipe_private);
580 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8\r\n");
581 //hdr = g_strdup("Content-Type: text/rtf\r\n");
582 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8;msgr=WAAtAE0ATQBTAC....AoADQA\r\nSupported: timer\r\n");
584 hdr = g_strdup_printf("Contact: %s\r\nContent-Type: %s; charset=UTF-8%s\r\n", tmp, content_type, msgr);
585 g_free(tmp);
586 g_free(tmp2);
588 sip_transport_request_timeout(sipe_private,
589 "MESSAGE",
590 dialog->with,
591 dialog->with,
592 hdr,
593 msgtext,
594 dialog,
595 process_message_response,
597 process_message_timeout);
598 g_free(msgtext);
599 g_free(hdr);
602 void sipe_im_process_queue(struct sipe_core_private *sipe_private,
603 struct sip_session *session)
605 GSList *entry2 = session->outgoing_message_queue;
606 while (entry2) {
607 struct queued_message *msg = entry2->data;
609 /* for multiparty chat or conference */
610 if (session->chat_session) {
611 gchar *who = sip_uri_self(sipe_private);
612 sipe_backend_chat_message(SIPE_CORE_PUBLIC,
613 session->chat_session->backend,
614 who,
615 msg->body);
616 g_free(who);
619 SIPE_DIALOG_FOREACH {
620 if (dialog->outgoing_invite) continue; /* do not send messages as INVITE is not responded. */
622 insert_unconfirmed_message(session, dialog, dialog->with,
623 msg->body, msg->content_type);
625 sipe_im_send_message(sipe_private, dialog, msg->body, msg->content_type);
626 } SIPE_DIALOG_FOREACH_END;
628 entry2 = sipe_session_dequeue_message(session);
632 struct unconfirmed_callback_data {
633 const gchar *prefix;
634 GSList *list;
637 struct unconfirmed_message {
638 const gchar *key;
639 const struct queued_message *msg;
642 static gint compare_cseq(gconstpointer a,
643 gconstpointer b)
645 return(((struct unconfirmed_message *) a)->msg->cseq -
646 ((struct unconfirmed_message *) b)->msg->cseq);
649 static void unconfirmed_message_callback(gpointer key,
650 gpointer value,
651 gpointer user_data)
653 const gchar *message_key = key;
654 struct unconfirmed_callback_data *data = user_data;
656 SIPE_DEBUG_INFO("unconfirmed_message_callback: key %s", message_key);
658 /* Put messages with the same prefix on a list sorted by CSeq */
659 if (g_str_has_prefix(message_key, data->prefix)) {
660 struct unconfirmed_message *msg = g_malloc(sizeof(struct unconfirmed_message));
661 msg->key = message_key;
662 msg->msg = value;
663 data->list = g_slist_insert_sorted(data->list, msg,
664 compare_cseq);
668 static void foreach_unconfirmed_message(struct sipe_core_private *sipe_private,
669 struct sip_session *session,
670 const gchar *callid,
671 const gchar *with,
672 unconfirmed_callback callback,
673 const gchar *callback_data)
675 gchar *prefix = g_strdup_printf(UNCONFIRMED_KEY_TEMPLATE("MESSAGE", ""),
676 callid, with);
677 struct unconfirmed_callback_data data = { prefix, NULL };
679 SIPE_DEBUG_INFO("foreach_unconfirmed_message: prefix %s", prefix);
681 /* Generate list of matching unconfirmed messages */
682 g_hash_table_foreach(session->unconfirmed_messages,
683 unconfirmed_message_callback,
684 &data);
685 g_free(prefix);
687 /* Process list unconfirmed messages */
688 if (data.list) {
689 GSList *entry;
691 while ((entry = data.list) != NULL) {
692 struct unconfirmed_message *unconfirmed = entry->data;
693 data.list = g_slist_remove(data.list, unconfirmed);
695 SIPE_DEBUG_INFO("foreach_unconfirmed_message: %s", unconfirmed->key);
696 (*callback)(sipe_private, session, unconfirmed->msg->body, callback_data);
698 g_hash_table_remove(session->unconfirmed_messages, unconfirmed->key);
699 g_free(unconfirmed);
704 static void cancel_callback(struct sipe_core_private *sipe_private,
705 struct sip_session *session,
706 const gchar *body,
707 const gchar *with)
709 sipe_user_present_message_undelivered(sipe_private, session,
710 -1, -1, with, body);
713 void sipe_im_cancel_unconfirmed(struct sipe_core_private *sipe_private,
714 struct sip_session *session,
715 const gchar *callid,
716 const gchar *with)
718 gchar *alias = sipe_buddy_get_alias(sipe_private, with);
720 SIPE_DEBUG_INFO("sipe_im_cancel_unconfirmed: with %s callid '%s'",
721 with, callid);
723 foreach_unconfirmed_message(sipe_private, session, callid, with,
724 cancel_callback, alias ? alias : with);
725 g_free(alias);
728 static void reenqueue_callback(SIPE_UNUSED_PARAMETER struct sipe_core_private *sipe_private,
729 struct sip_session *session,
730 const gchar *body,
731 SIPE_UNUSED_PARAMETER const gchar *with)
733 sipe_session_enqueue_message(session, body, NULL);
736 void sipe_im_reenqueue_unconfirmed(struct sipe_core_private *sipe_private,
737 struct sip_session *session,
738 const gchar *callid,
739 const gchar *with)
741 /* Remember original list, start with an empty list */
742 GSList *first = session->outgoing_message_queue;
743 session->outgoing_message_queue = NULL;
745 SIPE_DEBUG_INFO("sipe_im_reenqueue_unconfirmed: with %s callid '%s'",
746 with, callid);
748 /* Enqueue unconfirmed messages */
749 foreach_unconfirmed_message(sipe_private, session, callid, with,
750 reenqueue_callback, NULL);
752 /* Append or restore original list */
753 if (session->outgoing_message_queue) {
754 GSList *last = g_slist_last(session->outgoing_message_queue);
755 last->next = first;
756 } else {
757 session->outgoing_message_queue = first;
761 void sipe_core_im_send(struct sipe_core_public *sipe_public,
762 const gchar *who,
763 const gchar *what)
765 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
766 struct sip_session *session;
767 struct sip_dialog *dialog;
768 gchar *uri = sip_uri(who);
770 SIPE_DEBUG_INFO("sipe_core_im_send: '%s'", what);
772 session = sipe_session_find_or_add_im(sipe_private, uri);
773 dialog = sipe_dialog_find(session, uri);
775 /* Queue the message */
776 sipe_session_enqueue_message(session, what, NULL);
778 if (dialog && !dialog->outgoing_invite) {
779 sipe_im_process_queue(sipe_private, session);
780 } else if (!dialog || !dialog->outgoing_invite) {
781 /* Need to send the INVITE to get the outgoing dialog setup */
782 sipe_im_invite(sipe_private, session, uri, what, NULL, NULL, FALSE);
785 g_free(uri);
788 void sipe_im_cancel_dangling(struct sipe_core_private *sipe_private,
789 struct sip_session *session,
790 struct sip_dialog *dialog,
791 const gchar *with,
792 unconfirmed_callback callback)
794 SIPE_DEBUG_INFO_NOFORMAT("sipe_im_cancel_dangling: assuming dangling IM session, dropping it.");
795 sip_transport_bye(sipe_private, dialog);
797 (*callback)(sipe_private, session, dialog->callid, with);
799 /* We might not get a valid reply to our BYE,
800 so make sure the dialog is removed for sure. */
801 sipe_dialog_remove(session, with);
802 /* dialog is no longer valid */
805 void sipe_im_topic(struct sipe_core_private *sipe_private,
806 struct sip_session *session,
807 const gchar *topic)
809 g_free(session->subject);
810 session->subject = g_strdup(topic);
811 sipe_backend_im_topic(SIPE_CORE_PUBLIC, session->with, topic);
814 void process_incoming_info_conversation(struct sipe_core_private *sipe_private,
815 struct sipmsg *msg)
817 sipe_xml *xml = sipe_xml_parse(msg->body, msg->bodylen);
818 const gchar *from = NULL;
819 const gchar *subject = NULL;
822 if (!xml)
823 return;
825 if (sipe_strequal(sipe_xml_name(xml), "ConversationInfo")) {
826 const sipe_xml *node = sipe_xml_child(xml, "From");
827 if (node)
828 from = sipe_xml_attribute(node, "uri");
830 node = sipe_xml_child(xml, "Subject");
831 if (node)
832 subject = sipe_xml_data(node);
835 if (from && subject) {
836 struct sip_session *session;
837 session = sipe_session_find_im(sipe_private, from);
839 if (session)
840 sipe_im_topic(sipe_private, session, subject);
843 sipe_xml_free(xml);
845 sip_transport_response(sipe_private, msg, 200, "OK", NULL);
849 Local Variables:
850 mode: c
851 c-file-style: "bsd"
852 indent-tabs-mode: t
853 tab-width: 8
854 End: