6 * Copyright (C) 2011-2019 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
32 #include "sipe-common.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"
42 #include "sipe-groupchat.h"
44 #include "sipe-incoming.h"
46 #include "sipe-session.h"
47 #include "sipe-user.h"
48 #include "sipe-utils.h"
52 * Hash key template for unconfirmed messages
54 * Call-ID Recipient URI (or empty)
58 #define UNCONFIRMED_KEY_TEMPLATE(method, cseq) "<%s><" method "><%s><" cseq
60 /* key must be g_free()'d */
61 static gchar
*get_unconfirmed_message_key(const gchar
*callid
,
65 return(g_strdup_printf(UNCONFIRMED_KEY_TEMPLATE("%s", "%d>"),
67 with
? "MESSAGE" : "INVITE",
72 static void insert_unconfirmed_message(struct sip_session
*session
,
73 struct sip_dialog
*dialog
,
76 const gchar
*content_type
)
78 gchar
*key
= get_unconfirmed_message_key(dialog
->callid
, dialog
->cseq
+ 1, with
);
79 struct queued_message
*message
= g_new0(struct queued_message
, 1);
81 message
->body
= g_strdup(body
);
82 if (content_type
!= NULL
)
83 message
->content_type
= g_strdup(content_type
);
84 message
->cseq
= dialog
->cseq
+ 1;
86 g_hash_table_insert(session
->unconfirmed_messages
, key
, message
);
87 SIPE_DEBUG_INFO("insert_unconfirmed_message: added %s to list (count=%d)",
88 key
, g_hash_table_size(session
->unconfirmed_messages
));
91 static gboolean
remove_unconfirmed_message(struct sip_session
*session
,
94 gboolean found
= g_hash_table_remove(session
->unconfirmed_messages
, key
);
96 SIPE_DEBUG_INFO("remove_unconfirmed_message: removed %s from list (count=%d)",
97 key
, g_hash_table_size(session
->unconfirmed_messages
));
99 SIPE_DEBUG_INFO("remove_unconfirmed_message: key %s not found",
105 static void sipe_refer_notify(struct sipe_core_private
*sipe_private
,
106 struct sip_session
*session
,
113 struct sip_dialog
*dialog
= sipe_dialog_find(session
, who
);
115 hdr
= g_strdup_printf(
117 "Subscription-State: %s\r\n"
118 "Content-Type: message/sipfrag\r\n",
119 status
>= 200 ? "terminated" : "active");
121 body
= g_strdup_printf(
125 sip_transport_request(sipe_private
,
138 static gboolean
process_invite_response(struct sipe_core_private
*sipe_private
,
140 struct transaction
*trans
)
142 gchar
*with
= sipmsg_parse_to_address(msg
);
143 struct sip_session
*session
;
144 struct sip_dialog
*dialog
;
146 struct queued_message
*message
;
147 struct sipmsg
*request_msg
= trans
->msg
;
149 const gchar
*callid
= sipmsg_find_call_id_header(msg
);
152 session
= sipe_session_find_chat_or_im(sipe_private
, callid
, with
);
154 SIPE_DEBUG_INFO_NOFORMAT("process_invite_response: unable to find IM session");
159 dialog
= sipe_dialog_find(session
, with
);
161 SIPE_DEBUG_INFO_NOFORMAT("process_invite_response: session outgoing dialog is NULL");
166 sipe_dialog_parse(dialog
, msg
, TRUE
);
168 key
= get_unconfirmed_message_key(dialog
->callid
, sipmsg_parse_cseq(msg
), NULL
);
169 message
= g_hash_table_lookup(session
->unconfirmed_messages
, key
);
171 if (msg
->response
!= 200) {
172 gchar
*alias
= sipe_buddy_get_alias(sipe_private
, with
);
173 int warning
= sipmsg_parse_warning(msg
, NULL
);
175 SIPE_DEBUG_INFO_NOFORMAT("process_invite_response: INVITE response not 200");
177 /* cancel file transfer as rejected by server */
178 if (msg
->response
== 606 && /* Not acceptable all. */
179 warning
== 309 && /* Message contents not allowed by policy */
180 message
&& g_str_has_prefix(message
->content_type
, "text/x-msmsgsinvite"))
182 GSList
*parsed_body
= sipe_ft_parse_msg_body(message
->body
);
183 sipe_ft_incoming_cancel(dialog
, parsed_body
);
184 sipe_utils_nameval_free(parsed_body
);
188 /* generate error for each unprocessed message */
189 GSList
*entry
= session
->outgoing_message_queue
;
191 struct queued_message
*queued
= entry
->data
;
192 sipe_user_present_message_undelivered(sipe_private
, session
, msg
->response
, warning
, alias
? alias
: with
, queued
->body
);
193 entry
= sipe_session_dequeue_message(session
);
196 /* generate one error and remove all unprocessed messages */
197 gchar
*tmp_msg
= g_strdup_printf(_("Failed to invite %s"), alias
? alias
: with
);
198 sipe_user_present_error(sipe_private
, session
, tmp_msg
);
200 while (sipe_session_dequeue_message(session
));
204 remove_unconfirmed_message(session
, key
);
205 /* message is no longer valid */
208 sipe_dialog_remove(session
, with
);
211 if (session
->is_groupchat
) {
212 sipe_groupchat_invite_failed(sipe_private
, session
);
213 /* session is no longer valid */
220 sip_transport_ack(sipe_private
, dialog
);
221 dialog
->outgoing_invite
= NULL
;
222 dialog
->is_established
= TRUE
;
224 referred_by
= sipmsg_parse_address_from_header(request_msg
, "Referred-By");
226 sipe_refer_notify(sipe_private
, session
, referred_by
, 200, "OK");
230 /* add user to chat if it is a multiparty session */
231 if (session
->chat_session
&&
232 (session
->chat_session
->type
== SIPE_CHAT_TYPE_MULTIPARTY
)) {
233 sipe_backend_chat_add(session
->chat_session
->backend
,
238 if (session
->is_groupchat
) {
239 sipe_groupchat_invite_response(sipe_private
, dialog
, msg
);
242 if(g_slist_find_custom(dialog
->supported
, "ms-text-format", (GCompareFunc
)g_ascii_strcasecmp
)) {
243 SIPE_DEBUG_INFO_NOFORMAT("process_invite_response: remote system accepted message in INVITE");
244 sipe_session_dequeue_message(session
);
247 sipe_im_process_queue(sipe_private
, session
);
249 remove_unconfirmed_message(session
, key
);
256 /* EndPoints: "alice alisson" <sip:alice@atlanta.local>, <sip:bob@atlanta.local>;epid=ebca82d94d, <sip:carol@atlanta.local> */
257 static gchar
*get_end_points(struct sipe_core_private
*sipe_private
,
258 struct sip_session
*session
)
262 if (session
== NULL
) {
266 res
= g_strdup_printf("<sip:%s>", sipe_private
->username
);
268 SIPE_DIALOG_FOREACH
{
270 res
= g_strdup_printf("%s, <%s>", res
, dialog
->with
);
273 if (dialog
->theirepid
) {
275 res
= g_strdup_printf("%s;epid=%s", res
, dialog
->theirepid
);
278 } SIPE_DIALOG_FOREACH_END
;
284 sipe_im_invite(struct sipe_core_private
*sipe_private
,
285 struct sip_session
*session
,
287 const gchar
*msg_body
,
288 const gchar
*content_type
,
289 const gchar
*referred_by
,
290 const gboolean is_triggered
)
297 char *ms_text_format
= NULL
;
298 char *ms_conversation_id
= NULL
;
299 gchar
*roster_manager
;
301 gchar
*referred_by_str
;
302 gboolean is_multiparty
=
303 session
->chat_session
&&
304 (session
->chat_session
->type
== SIPE_CHAT_TYPE_MULTIPARTY
);
305 struct sip_dialog
*dialog
= sipe_dialog_find(session
, who
);
307 if (dialog
&& dialog
->is_established
) {
308 SIPE_DEBUG_INFO("session with %s already has a dialog open", who
);
313 dialog
= sipe_dialog_add(session
);
314 dialog
->callid
= session
->callid
? g_strdup(session
->callid
) : gencallid();
315 dialog
->with
= g_strdup(who
);
318 if (!(dialog
->ourtag
)) {
319 dialog
->ourtag
= gentag();
325 char *msgtext
= NULL
;
327 const gchar
*msgr
= "";
330 if (!g_str_has_prefix(content_type
, "text/x-msmsgsinvite")) {
331 char *msgformat
= NULL
;
334 sipe_parse_html(msg_body
, &msgformat
, &msgtext
);
335 SIPE_DEBUG_INFO("sipe_invite: msgformat=%s", msgformat
);
337 msgr_value
= sipmsg_get_msgr_string(msgformat
);
340 msgr
= tmp
= g_strdup_printf(";msgr=%s", msgr_value
);
344 /* When Sipe reconnects after a crash, we are not able
345 * to send messages to contacts with which we had open
346 * conversations when the crash occured. Server sends
347 * error response with reason="This client has an IM
348 * session with the same conversation ID"
350 * Setting random Ms-Conversation-ID prevents this problem
351 * so we can continue the conversation. */
352 ms_conversation_id
= g_strdup_printf("Ms-Conversation-ID: %u\r\n",
353 rand() % 1000000000);
355 msgtext
= g_strdup(msg_body
);
358 base64_msg
= g_base64_encode((guchar
*) msgtext
, strlen(msgtext
));
359 ms_text_format
= g_strdup_printf("ms-text-format: %s; charset=UTF-8%s;ms-body=%s\r\n",
360 content_type
? content_type
: "text/plain",
367 insert_unconfirmed_message(session
, dialog
, NULL
,
368 msg_body
, content_type
);
371 contact
= get_contact(sipe_private
);
372 end_points
= get_end_points(sipe_private
, session
);
373 self
= sip_uri_self(sipe_private
);
374 roster_manager
= g_strdup_printf(
375 "Roster-Manager: %s\r\n"
379 referred_by_str
= referred_by
?
381 "Referred-By: %s\r\n",
384 hdr
= g_strdup_printf(
385 "Supported: ms-sender\r\n"
392 "Content-Type: application/sdp\r\n",
393 is_multiparty
&& sipe_strcase_equal(session
->chat_session
->id
, self
) ? roster_manager
: "",
395 is_triggered
? "TriggeredInvite: TRUE\r\n" : "",
396 is_triggered
|| is_multiparty
? "Require: com.microsoft.rtc-multiparty\r\n" : "",
398 ms_text_format
? ms_text_format
: "",
399 ms_conversation_id
? ms_conversation_id
: "");
400 g_free(ms_text_format
);
401 g_free(ms_conversation_id
);
404 body
= g_strdup_printf(
406 "o=- 0 0 IN %s %s\r\n"
410 "m=%s %d sip null\r\n"
411 "a=accept-types:" SDP_ACCEPT_TYPES
"\r\n",
412 sip_transport_sdp_address_marker(sipe_private
),
413 sip_transport_ip_address(sipe_private
),
414 sip_transport_sdp_address_marker(sipe_private
),
415 sip_transport_ip_address(sipe_private
),
416 SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
) ? "message" : "x-ms-message",
417 sip_transport_port(sipe_private
));
419 dialog
->outgoing_invite
= sip_transport_request(sipe_private
,
426 process_invite_response
);
429 g_free(roster_manager
);
431 g_free(referred_by_str
);
438 process_message_response(struct sipe_core_private
*sipe_private
,
440 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
443 gchar
*with
= sipmsg_parse_to_address(msg
);
444 const gchar
*callid
= sipmsg_find_call_id_header(msg
);
445 struct sip_session
*session
= sipe_session_find_chat_or_im(sipe_private
, callid
, with
);
446 struct sip_dialog
*dialog
;
448 struct queued_message
*message
;
451 SIPE_DEBUG_INFO_NOFORMAT("process_message_response: unable to find IM session");
456 dialog
= sipe_dialog_find(session
, with
);
458 SIPE_DEBUG_INFO_NOFORMAT("process_message_response: session outgoing dialog is NULL");
463 key
= get_unconfirmed_message_key(sipmsg_find_call_id_header(msg
), sipmsg_parse_cseq(msg
), with
);
464 message
= g_hash_table_lookup(session
->unconfirmed_messages
, key
);
466 if (msg
->response
>= 400) {
467 int warning
= sipmsg_parse_warning(msg
, NULL
);
469 SIPE_DEBUG_INFO_NOFORMAT("process_message_response: MESSAGE response >= 400");
471 /* cancel file transfer as rejected by server */
472 if (msg
->response
== 606 && /* Not acceptable all. */
473 warning
== 309 && /* Message contents not allowed by policy */
474 message
&& g_str_has_prefix(message
->content_type
, "text/x-msmsgsinvite"))
476 GSList
*parsed_body
= sipe_ft_parse_msg_body(msg
->body
);
477 sipe_ft_incoming_cancel(dialog
, parsed_body
);
478 sipe_utils_nameval_free(parsed_body
);
481 /* drop dangling IM sessions: assume that BYE from remote never reached us */
482 if (msg
->response
== 408 || /* Request timeout */
483 msg
->response
== 480 || /* Temporarily Unavailable */
484 msg
->response
== 481) { /* Call/Transaction Does Not Exist */
485 sipe_im_cancel_dangling(sipe_private
, session
, dialog
, with
,
486 sipe_im_cancel_unconfirmed
);
487 /* dialog is no longer valid */
489 gchar
*alias
= sipe_buddy_get_alias(sipe_private
, with
);
490 sipe_user_present_message_undelivered(sipe_private
, session
,
491 msg
->response
, warning
,
492 alias
? alias
: with
,
493 message
? message
->body
: NULL
);
494 remove_unconfirmed_message(session
, key
);
495 /* message is no longer valid */
501 const gchar
*message_id
= sipmsg_find_header(msg
, "Message-Id");
503 g_hash_table_insert(session
->conf_unconfirmed_messages
, g_strdup(message_id
), g_strdup(message
->body
));
504 SIPE_DEBUG_INFO("process_message_response: added message with id %s to conf_unconfirmed_messages(count=%d)",
505 message_id
, g_hash_table_size(session
->conf_unconfirmed_messages
));
507 remove_unconfirmed_message(session
, key
);
513 if (ret
) sipe_im_process_queue(sipe_private
, session
);
517 #ifndef ENABLE_OCS2005_MESSAGE_HACK
519 * Hack to circumvent problems reported in the bug report
521 * #3267073 - False "could not be delivered" errors
523 * The logs provided by the reporters indicate that OCS2005 clients DO NOT
524 * acknowledge our SIP MESSAGEs. Therefore the message timeout is triggered
525 * and messages are reported to the user as not delivered.
527 * Either this is a bug in the OCS2005 client or we do something wrong in our
528 * SIP MESSAGEs. This hack removes the message timeout and is provided for
529 * users who need to communicate with a still existing OCS2005 user base.
531 * Do not enable it by default!
534 process_message_timeout(struct sipe_core_private
*sipe_private
,
536 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
538 gchar
*with
= sipmsg_parse_to_address(msg
);
539 const gchar
*callid
= sipmsg_find_call_id_header(msg
);
540 struct sip_session
*session
= sipe_session_find_chat_or_im(sipe_private
, callid
, with
);
545 SIPE_DEBUG_INFO_NOFORMAT("process_message_timeout: unable to find IM session");
550 /* Remove timed-out message from unconfirmed list */
551 key
= get_unconfirmed_message_key(sipmsg_find_call_id_header(msg
), sipmsg_parse_cseq(msg
), with
);
552 found
= remove_unconfirmed_message(session
, key
);
556 gchar
*alias
= sipe_buddy_get_alias(sipe_private
, with
);
557 sipe_user_present_message_undelivered(sipe_private
, session
, -1, -1,
558 alias
? alias
: with
,
568 static void sipe_im_send_message(struct sipe_core_private
*sipe_private
,
569 struct sip_dialog
*dialog
,
570 const gchar
*msg_body
,
571 const gchar
*content_type
)
575 char *msgtext
= NULL
;
576 const gchar
*msgr
= "";
579 if (content_type
== NULL
)
580 content_type
= "text/plain";
582 if (!g_str_has_prefix(content_type
, "text/x-msmsgsinvite")) {
583 char *msgformat
= NULL
;
586 sipe_parse_html(msg_body
, &msgformat
, &msgtext
);
587 SIPE_DEBUG_INFO("sipe_send_message: msgformat=%s", msgformat
);
589 msgr_value
= sipmsg_get_msgr_string(msgformat
);
592 msgr
= tmp2
= g_strdup_printf(";msgr=%s", msgr_value
);
596 msgtext
= g_strdup(msg_body
);
599 tmp
= get_contact(sipe_private
);
600 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8\r\n");
601 //hdr = g_strdup("Content-Type: text/rtf\r\n");
602 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8;msgr=WAAtAE0ATQBTAC....AoADQA\r\nSupported: timer\r\n");
604 hdr
= g_strdup_printf("Contact: %s\r\nContent-Type: %s; charset=UTF-8%s\r\n", tmp
, content_type
, msgr
);
608 #ifdef ENABLE_OCS2005_MESSAGE_HACK
609 sip_transport_request(
611 sip_transport_request_timeout(
620 process_message_response
621 #ifndef ENABLE_OCS2005_MESSAGE_HACK
624 process_message_timeout
631 void sipe_im_process_queue(struct sipe_core_private
*sipe_private
,
632 struct sip_session
*session
)
634 GSList
*entry2
= session
->outgoing_message_queue
;
636 struct queued_message
*msg
= entry2
->data
;
638 /* for multiparty chat or conference */
639 if (session
->chat_session
) {
640 gchar
*who
= sip_uri_self(sipe_private
);
641 sipe_backend_chat_message(SIPE_CORE_PUBLIC
,
642 session
->chat_session
->backend
,
649 SIPE_DIALOG_FOREACH
{
650 if (dialog
->outgoing_invite
) continue; /* do not send messages as INVITE is not responded. */
652 insert_unconfirmed_message(session
, dialog
, dialog
->with
,
653 msg
->body
, msg
->content_type
);
655 sipe_im_send_message(sipe_private
, dialog
, msg
->body
, msg
->content_type
);
656 } SIPE_DIALOG_FOREACH_END
;
658 entry2
= sipe_session_dequeue_message(session
);
662 struct unconfirmed_callback_data
{
667 struct unconfirmed_message
{
669 const struct queued_message
*msg
;
672 static gint
compare_cseq(gconstpointer a
,
675 return(((struct unconfirmed_message
*) a
)->msg
->cseq
-
676 ((struct unconfirmed_message
*) b
)->msg
->cseq
);
679 static void unconfirmed_message_callback(gpointer key
,
683 const gchar
*message_key
= key
;
684 struct unconfirmed_callback_data
*data
= user_data
;
686 SIPE_DEBUG_INFO("unconfirmed_message_callback: key %s", message_key
);
688 /* Put messages with the same prefix on a list sorted by CSeq */
689 if (g_str_has_prefix(message_key
, data
->prefix
)) {
690 struct unconfirmed_message
*msg
= g_malloc(sizeof(struct unconfirmed_message
));
691 msg
->key
= message_key
;
693 data
->list
= g_slist_insert_sorted(data
->list
, msg
,
698 static void foreach_unconfirmed_message(struct sipe_core_private
*sipe_private
,
699 struct sip_session
*session
,
702 unconfirmed_callback callback
,
703 const gchar
*callback_data
)
705 gchar
*prefix
= g_strdup_printf(UNCONFIRMED_KEY_TEMPLATE("MESSAGE", ""),
707 struct unconfirmed_callback_data data
= { prefix
, NULL
};
709 SIPE_DEBUG_INFO("foreach_unconfirmed_message: prefix %s", prefix
);
711 /* Generate list of matching unconfirmed messages */
712 g_hash_table_foreach(session
->unconfirmed_messages
,
713 unconfirmed_message_callback
,
717 /* Process list unconfirmed messages */
721 while ((entry
= data
.list
) != NULL
) {
722 struct unconfirmed_message
*unconfirmed
= entry
->data
;
723 data
.list
= g_slist_remove(data
.list
, unconfirmed
);
725 SIPE_DEBUG_INFO("foreach_unconfirmed_message: %s", unconfirmed
->key
);
726 (*callback
)(sipe_private
, session
, unconfirmed
->msg
->body
, callback_data
);
728 g_hash_table_remove(session
->unconfirmed_messages
, unconfirmed
->key
);
734 static void cancel_callback(struct sipe_core_private
*sipe_private
,
735 struct sip_session
*session
,
739 sipe_user_present_message_undelivered(sipe_private
, session
,
743 void sipe_im_cancel_unconfirmed(struct sipe_core_private
*sipe_private
,
744 struct sip_session
*session
,
748 gchar
*alias
= sipe_buddy_get_alias(sipe_private
, with
);
750 SIPE_DEBUG_INFO("sipe_im_cancel_unconfirmed: with %s callid '%s'",
753 foreach_unconfirmed_message(sipe_private
, session
, callid
, with
,
754 cancel_callback
, alias
? alias
: with
);
758 static void reenqueue_callback(SIPE_UNUSED_PARAMETER
struct sipe_core_private
*sipe_private
,
759 struct sip_session
*session
,
761 SIPE_UNUSED_PARAMETER
const gchar
*with
)
763 sipe_session_enqueue_message(session
, body
, NULL
);
766 void sipe_im_reenqueue_unconfirmed(struct sipe_core_private
*sipe_private
,
767 struct sip_session
*session
,
771 /* Remember original list, start with an empty list */
772 GSList
*first
= session
->outgoing_message_queue
;
773 session
->outgoing_message_queue
= NULL
;
775 SIPE_DEBUG_INFO("sipe_im_reenqueue_unconfirmed: with %s callid '%s'",
778 /* Enqueue unconfirmed messages */
779 foreach_unconfirmed_message(sipe_private
, session
, callid
, with
,
780 reenqueue_callback
, NULL
);
782 /* Append or restore original list */
783 if (session
->outgoing_message_queue
) {
784 GSList
*last
= g_slist_last(session
->outgoing_message_queue
);
787 session
->outgoing_message_queue
= first
;
791 void sipe_core_im_send(struct sipe_core_public
*sipe_public
,
795 struct sipe_core_private
*sipe_private
= SIPE_CORE_PRIVATE
;
796 struct sip_session
*session
;
797 struct sip_dialog
*dialog
;
798 gchar
*uri
= sip_uri(who
);
800 SIPE_DEBUG_INFO("sipe_core_im_send: '%s'", what
);
802 session
= sipe_session_find_or_add_im(sipe_private
, uri
);
803 dialog
= sipe_dialog_find(session
, uri
);
805 /* Queue the message */
806 sipe_session_enqueue_message(session
, what
, NULL
);
808 if (dialog
&& !dialog
->outgoing_invite
) {
809 if (dialog
->delayed_invite
)
810 sipe_incoming_cancel_delayed_invite(sipe_private
,
812 sipe_im_process_queue(sipe_private
, session
);
813 } else if (!dialog
|| !dialog
->outgoing_invite
) {
814 /* Need to send the INVITE to get the outgoing dialog setup */
815 sipe_im_invite(sipe_private
, session
, uri
, what
, NULL
, NULL
, FALSE
);
821 void sipe_core_im_close(struct sipe_core_public
*sipe_public
,
824 struct sipe_core_private
*sipe_private
= SIPE_CORE_PRIVATE
;
826 SIPE_DEBUG_INFO("sipe_core_im_close: conversation with %s closed", who
);
827 sipe_session_close(sipe_private
,
828 sipe_session_find_im(sipe_private
, who
));
831 void sipe_im_cancel_dangling(struct sipe_core_private
*sipe_private
,
832 struct sip_session
*session
,
833 struct sip_dialog
*dialog
,
835 unconfirmed_callback callback
)
837 SIPE_DEBUG_INFO_NOFORMAT("sipe_im_cancel_dangling: assuming dangling IM session, dropping it.");
838 sip_transport_bye(sipe_private
, dialog
);
840 (*callback
)(sipe_private
, session
, dialog
->callid
, with
);
842 /* We might not get a valid reply to our BYE,
843 so make sure the dialog is removed for sure. */
844 sipe_dialog_remove(session
, with
);
845 /* dialog is no longer valid */
848 void sipe_im_topic(struct sipe_core_private
*sipe_private
,
849 struct sip_session
*session
,
852 g_free(session
->subject
);
853 session
->subject
= g_strdup(topic
);
854 sipe_backend_im_topic(SIPE_CORE_PUBLIC
, session
->with
, topic
);
857 void process_incoming_info_conversation(struct sipe_core_private
*sipe_private
,
860 sipe_xml
*xml
= sipe_xml_parse(msg
->body
, msg
->bodylen
);
861 const gchar
*from
= NULL
;
862 gchar
*subject
= NULL
;
868 if (sipe_strequal(sipe_xml_name(xml
), "ConversationInfo")) {
869 const sipe_xml
*node
= sipe_xml_child(xml
, "From");
871 from
= sipe_xml_attribute(node
, "uri");
873 node
= sipe_xml_child(xml
, "Subject");
875 subject
= sipe_xml_data(node
);
878 if (from
&& subject
) {
879 struct sip_session
*session
;
880 session
= sipe_session_find_im(sipe_private
, from
);
883 sipe_im_topic(sipe_private
, session
, subject
);
889 sip_transport_response(sipe_private
, msg
, 200, "OK", NULL
);