core: moved repeating code to sipmsg_parse_warning()
[siplcs.git] / src / core / sipe-im.c
blobed9a6dc69a37c1a66c60c8d22711eea8c74cb1d6
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-chat.h"
37 #include "sipe-core.h"
38 #include "sipe-core-private.h"
39 #include "sipe-dialog.h"
40 #include "sipe-ft.h"
41 #include "sipe-groupchat.h"
42 #include "sipe-im.h"
43 #include "sipe-nls.h"
44 #include "sipe-session.h"
45 #include "sipe-user.h"
46 #include "sipe-utils.h"
47 #include "sipe-xml.h"
50 * Hash key template for unconfirmed messages
52 * Call-ID Recipient URI (or empty)
53 * | |
54 * | SIP method | CSeq
55 * | | | | */
56 #define UNCONFIRMED_KEY_TEMPLATE(method, cseq) "<%s><" method "><%s><" cseq
58 /* key must be g_free()'d */
59 static gchar *get_unconfirmed_message_key(const gchar *callid,
60 unsigned int cseq,
61 const gchar *with)
63 return(g_strdup_printf(UNCONFIRMED_KEY_TEMPLATE("%s", "%d>"),
64 callid,
65 with ? "MESSAGE" : "INVITE",
66 with ? with : "",
67 cseq));
70 static void insert_unconfirmed_message(struct sip_session *session,
71 struct sip_dialog *dialog,
72 const gchar *with,
73 const gchar *body,
74 const gchar *content_type)
76 gchar *key = get_unconfirmed_message_key(dialog->callid, dialog->cseq + 1, with);
77 struct queued_message *message = g_new0(struct queued_message, 1);
79 message->body = g_strdup(body);
80 if (content_type != NULL)
81 message->content_type = g_strdup(content_type);
82 message->cseq = dialog->cseq + 1;
84 g_hash_table_insert(session->unconfirmed_messages, key, message);
85 SIPE_DEBUG_INFO("insert_unconfirmed_message: added %s to list (count=%d)",
86 key, g_hash_table_size(session->unconfirmed_messages));
89 static gboolean remove_unconfirmed_message(struct sip_session *session,
90 const gchar *key)
92 gboolean found = g_hash_table_remove(session->unconfirmed_messages, key);
93 if (found) {
94 SIPE_DEBUG_INFO("remove_unconfirmed_message: removed %s from list (count=%d)",
95 key, g_hash_table_size(session->unconfirmed_messages));
96 } else {
97 SIPE_DEBUG_INFO("remove_unconfirmed_message: key %s not found",
98 key);
100 return(found);
103 static void sipe_refer_notify(struct sipe_core_private *sipe_private,
104 struct sip_session *session,
105 const gchar *who,
106 int status,
107 const gchar *desc)
109 gchar *hdr;
110 gchar *body;
111 struct sip_dialog *dialog = sipe_dialog_find(session, who);
113 hdr = g_strdup_printf(
114 "Event: refer\r\n"
115 "Subscription-State: %s\r\n"
116 "Content-Type: message/sipfrag\r\n",
117 status >= 200 ? "terminated" : "active");
119 body = g_strdup_printf(
120 "SIP/2.0 %d %s\r\n",
121 status, desc);
123 sip_transport_request(sipe_private,
124 "NOTIFY",
125 who,
126 who,
127 hdr,
128 body,
129 dialog,
130 NULL);
132 g_free(hdr);
133 g_free(body);
136 static gchar *get_buddy_alias(struct sipe_core_private *sipe_private,
137 const gchar *with)
139 sipe_backend_buddy pbuddy;
140 gchar *alias = NULL;
141 if ((pbuddy = sipe_backend_buddy_find(SIPE_CORE_PUBLIC, with, NULL))) {
142 alias = sipe_backend_buddy_get_alias(SIPE_CORE_PUBLIC, pbuddy);
144 return(alias);
147 static gboolean process_invite_response(struct sipe_core_private *sipe_private,
148 struct sipmsg *msg,
149 struct transaction *trans)
151 gchar *with = parse_from(sipmsg_find_header(msg, "To"));
152 struct sip_session *session;
153 struct sip_dialog *dialog;
154 gchar *key;
155 struct queued_message *message;
156 struct sipmsg *request_msg = trans->msg;
158 const gchar *callid = sipmsg_find_header(msg, "Call-ID");
159 gchar *referred_by;
161 session = sipe_session_find_chat_or_im(sipe_private, callid, with);
162 if (!session) {
163 SIPE_DEBUG_INFO_NOFORMAT("process_invite_response: unable to find IM session");
164 g_free(with);
165 return FALSE;
168 dialog = sipe_dialog_find(session, with);
169 if (!dialog) {
170 SIPE_DEBUG_INFO_NOFORMAT("process_invite_response: session outgoing dialog is NULL");
171 g_free(with);
172 return FALSE;
175 sipe_dialog_parse(dialog, msg, TRUE);
177 key = get_unconfirmed_message_key(dialog->callid, sipmsg_parse_cseq(msg), NULL);
178 message = g_hash_table_lookup(session->unconfirmed_messages, key);
180 if (msg->response != 200) {
181 gchar *alias = get_buddy_alias(sipe_private, with);
182 int warning = sipmsg_parse_warning(msg, NULL);
184 SIPE_DEBUG_INFO_NOFORMAT("process_invite_response: INVITE response not 200");
186 /* cancel file transfer as rejected by server */
187 if (msg->response == 606 && /* Not acceptable all. */
188 warning == 309 && /* Message contents not allowed by policy */
189 message && g_str_has_prefix(message->content_type, "text/x-msmsgsinvite"))
191 GSList *parsed_body = sipe_ft_parse_msg_body(message->body);
192 sipe_ft_incoming_cancel(dialog, parsed_body);
193 sipe_utils_nameval_free(parsed_body);
196 if (message) {
197 /* generate error for each unprocessed message */
198 GSList *entry = session->outgoing_message_queue;
199 while (entry) {
200 struct queued_message *queued = entry->data;
201 sipe_user_present_message_undelivered(sipe_private, session, msg->response, warning, alias ? alias : with, queued->body);
202 entry = sipe_session_dequeue_message(session);
204 } else {
205 /* generate one error and remove all unprocessed messages */
206 gchar *tmp_msg = g_strdup_printf(_("Failed to invite %s"), alias ? alias : with);
207 sipe_user_present_error(sipe_private, session, tmp_msg);
208 g_free(tmp_msg);
209 while (sipe_session_dequeue_message(session));
211 g_free(alias);
213 remove_unconfirmed_message(session, key);
214 /* message is no longer valid */
215 g_free(key);
217 sipe_dialog_remove(session, with);
218 g_free(with);
220 if (session->is_groupchat) {
221 sipe_groupchat_invite_failed(sipe_private, session);
222 /* session is no longer valid */
225 return FALSE;
228 dialog->cseq = 0;
229 sip_transport_ack(sipe_private, dialog);
230 dialog->outgoing_invite = NULL;
231 dialog->is_established = TRUE;
233 referred_by = parse_from(sipmsg_find_header(request_msg, "Referred-By"));
234 if (referred_by) {
235 sipe_refer_notify(sipe_private, session, referred_by, 200, "OK");
236 g_free(referred_by);
239 /* add user to chat if it is a multiparty session */
240 if (session->chat_session &&
241 (session->chat_session->type == SIPE_CHAT_TYPE_MULTIPARTY)) {
242 sipe_backend_chat_add(session->chat_session->backend,
243 with,
244 TRUE);
247 if (session->is_groupchat) {
248 sipe_groupchat_invite_response(sipe_private, dialog);
251 if(g_slist_find_custom(dialog->supported, "ms-text-format", (GCompareFunc)g_ascii_strcasecmp)) {
252 SIPE_DEBUG_INFO_NOFORMAT("process_invite_response: remote system accepted message in INVITE");
253 sipe_session_dequeue_message(session);
256 sipe_im_process_queue(sipe_private, session);
258 remove_unconfirmed_message(session, key);
260 g_free(key);
261 g_free(with);
262 return TRUE;
265 /* EndPoints: "alice alisson" <sip:alice@atlanta.local>, <sip:bob@atlanta.local>;epid=ebca82d94d, <sip:carol@atlanta.local> */
266 static gchar *get_end_points(struct sipe_core_private *sipe_private,
267 struct sip_session *session)
269 gchar *res;
271 if (session == NULL) {
272 return NULL;
275 res = g_strdup_printf("<sip:%s>", sipe_private->username);
277 SIPE_DIALOG_FOREACH {
278 gchar *tmp = res;
279 res = g_strdup_printf("%s, <%s>", res, dialog->with);
280 g_free(tmp);
282 if (dialog->theirepid) {
283 tmp = res;
284 res = g_strdup_printf("%s;epid=%s", res, dialog->theirepid);
285 g_free(tmp);
287 } SIPE_DIALOG_FOREACH_END;
289 return res;
292 void
293 sipe_im_invite(struct sipe_core_private *sipe_private,
294 struct sip_session *session,
295 const gchar *who,
296 const gchar *msg_body,
297 const gchar *content_type,
298 const gchar *referred_by,
299 const gboolean is_triggered)
301 gchar *hdr;
302 gchar *to;
303 gchar *contact;
304 gchar *body;
305 gchar *self;
306 char *ms_text_format = NULL;
307 char *ms_conversation_id = NULL;
308 gchar *roster_manager;
309 gchar *end_points;
310 gchar *referred_by_str;
311 gboolean is_multiparty =
312 session->chat_session &&
313 (session->chat_session->type == SIPE_CHAT_TYPE_MULTIPARTY);
314 struct sip_dialog *dialog = sipe_dialog_find(session, who);
316 if (dialog && dialog->is_established) {
317 SIPE_DEBUG_INFO("session with %s already has a dialog open", who);
318 return;
321 if (!dialog) {
322 dialog = sipe_dialog_add(session);
323 dialog->callid = session->callid ? g_strdup(session->callid) : gencallid();
324 dialog->with = g_strdup(who);
327 if (!(dialog->ourtag)) {
328 dialog->ourtag = gentag();
331 to = sip_uri(who);
333 if (msg_body) {
334 char *msgtext = NULL;
335 char *base64_msg;
336 const gchar *msgr = "";
337 gchar *tmp = NULL;
339 if (!g_str_has_prefix(content_type, "text/x-msmsgsinvite")) {
340 char *msgformat;
341 gchar *msgr_value;
343 sipe_parse_html(msg_body, &msgformat, &msgtext);
344 SIPE_DEBUG_INFO("sipe_invite: msgformat=%s", msgformat);
346 msgr_value = sipmsg_get_msgr_string(msgformat);
347 g_free(msgformat);
348 if (msgr_value) {
349 msgr = tmp = g_strdup_printf(";msgr=%s", msgr_value);
350 g_free(msgr_value);
353 /* When Sipe reconnects after a crash, we are not able
354 * to send messages to contacts with which we had open
355 * conversations when the crash occured. Server sends
356 * error response with reason="This client has an IM
357 * session with the same conversation ID"
359 * Setting random Ms-Conversation-ID prevents this problem
360 * so we can continue the conversation. */
361 ms_conversation_id = g_strdup_printf("Ms-Conversation-ID: %u\r\n",
362 rand() % 1000000000);
363 } else {
364 msgtext = g_strdup(msg_body);
367 base64_msg = g_base64_encode((guchar*) msgtext, strlen(msgtext));
368 ms_text_format = g_strdup_printf("ms-text-format: %s; charset=UTF-8%s;ms-body=%s\r\n",
369 content_type ? content_type : "text/plain",
370 msgr,
371 base64_msg);
372 g_free(msgtext);
373 g_free(tmp);
374 g_free(base64_msg);
376 insert_unconfirmed_message(session, dialog, NULL,
377 msg_body, content_type);
380 contact = get_contact(sipe_private);
381 end_points = get_end_points(sipe_private, session);
382 self = sip_uri_self(sipe_private);
383 roster_manager = g_strdup_printf(
384 "Roster-Manager: %s\r\n"
385 "EndPoints: %s\r\n",
386 self,
387 end_points);
388 referred_by_str = referred_by ?
389 g_strdup_printf(
390 "Referred-By: %s\r\n",
391 referred_by)
392 : g_strdup("");
393 hdr = g_strdup_printf(
394 "Supported: ms-sender\r\n"
395 "%s"
396 "%s"
397 "%s"
398 "%s"
399 "Contact: %s\r\n%s"
400 "%s"
401 "Content-Type: application/sdp\r\n",
402 is_multiparty && sipe_strcase_equal(session->chat_session->id, self) ? roster_manager : "",
403 referred_by_str,
404 is_triggered ? "TriggeredInvite: TRUE\r\n" : "",
405 is_triggered || is_multiparty ? "Require: com.microsoft.rtc-multiparty\r\n" : "",
406 contact,
407 ms_text_format ? ms_text_format : "",
408 ms_conversation_id ? ms_conversation_id : "");
409 g_free(ms_text_format);
410 g_free(ms_conversation_id);
411 g_free(self);
413 body = g_strdup_printf(
414 "v=0\r\n"
415 "o=- 0 0 IN IP4 %s\r\n"
416 "s=session\r\n"
417 "c=IN IP4 %s\r\n"
418 "t=0 0\r\n"
419 "m=%s %d sip null\r\n"
420 "a=accept-types:" SDP_ACCEPT_TYPES "\r\n",
421 sipe_backend_network_ip_address(),
422 sipe_backend_network_ip_address(),
423 SIPE_CORE_PRIVATE_FLAG_IS(OCS2007) ? "message" : "x-ms-message",
424 sip_transport_port(sipe_private));
426 dialog->outgoing_invite = sip_transport_request(sipe_private,
427 "INVITE",
430 hdr,
431 body,
432 dialog,
433 process_invite_response);
435 g_free(to);
436 g_free(roster_manager);
437 g_free(end_points);
438 g_free(referred_by_str);
439 g_free(body);
440 g_free(hdr);
441 g_free(contact);
444 static gboolean
445 process_message_response(struct sipe_core_private *sipe_private,
446 struct sipmsg *msg,
447 SIPE_UNUSED_PARAMETER struct transaction *trans)
449 gboolean ret = TRUE;
450 gchar *with = parse_from(sipmsg_find_header(msg, "To"));
451 struct sip_session *session = sipe_session_find_im(sipe_private, with);
452 struct sip_dialog *dialog;
453 gchar *key;
454 struct queued_message *message;
456 if (!session) {
457 SIPE_DEBUG_INFO_NOFORMAT("process_message_response: unable to find IM session");
458 g_free(with);
459 return FALSE;
462 dialog = sipe_dialog_find(session, with);
463 if (!dialog) {
464 SIPE_DEBUG_INFO_NOFORMAT("process_message_response: session outgoing dialog is NULL");
465 g_free(with);
466 return FALSE;
469 key = get_unconfirmed_message_key(sipmsg_find_header(msg, "Call-ID"), sipmsg_parse_cseq(msg), with);
470 message = g_hash_table_lookup(session->unconfirmed_messages, key);
472 if (msg->response >= 400) {
473 int warning = sipmsg_parse_warning(msg, NULL);
475 SIPE_DEBUG_INFO_NOFORMAT("process_message_response: MESSAGE response >= 400");
477 /* cancel file transfer as rejected by server */
478 if (msg->response == 606 && /* Not acceptable all. */
479 warning == 309 && /* Message contents not allowed by policy */
480 message && g_str_has_prefix(message->content_type, "text/x-msmsgsinvite"))
482 GSList *parsed_body = sipe_ft_parse_msg_body(msg->body);
483 sipe_ft_incoming_cancel(dialog, parsed_body);
484 sipe_utils_nameval_free(parsed_body);
487 /* drop dangling IM sessions: assume that BYE from remote never reached us */
488 if (msg->response == 408 || /* Request timeout */
489 msg->response == 480 || /* Temporarily Unavailable */
490 msg->response == 481) { /* Call/Transaction Does Not Exist */
491 sipe_im_cancel_dangling(sipe_private, session, dialog, with,
492 sipe_im_cancel_unconfirmed);
493 /* dialog is no longer valid */
494 } else {
495 gchar *alias = get_buddy_alias(sipe_private, with);
496 sipe_user_present_message_undelivered(sipe_private, session,
497 msg->response, warning,
498 alias ? alias : with,
499 message ? message->body : NULL);
500 remove_unconfirmed_message(session, key);
501 /* message is no longer valid */
502 g_free(alias);
505 ret = FALSE;
506 } else {
507 const gchar *message_id = sipmsg_find_header(msg, "Message-Id");
508 if (message_id) {
509 g_hash_table_insert(session->conf_unconfirmed_messages, g_strdup(message_id), g_strdup(message->body));
510 SIPE_DEBUG_INFO("process_message_response: added message with id %s to conf_unconfirmed_messages(count=%d)",
511 message_id, g_hash_table_size(session->conf_unconfirmed_messages));
513 remove_unconfirmed_message(session, key);
516 g_free(key);
517 g_free(with);
519 if (ret) sipe_im_process_queue(sipe_private, session);
520 return ret;
523 static gboolean
524 process_message_timeout(struct sipe_core_private *sipe_private,
525 struct sipmsg *msg,
526 SIPE_UNUSED_PARAMETER struct transaction *trans)
528 gchar *with = parse_from(sipmsg_find_header(msg, "To"));
529 struct sip_session *session = sipe_session_find_im(sipe_private, with);
530 gchar *key;
531 gboolean found;
533 if (!session) {
534 SIPE_DEBUG_INFO_NOFORMAT("process_message_timeout: unable to find IM session");
535 g_free(with);
536 return TRUE;
539 /* Remove timed-out message from unconfirmed list */
540 key = get_unconfirmed_message_key(sipmsg_find_header(msg, "Call-ID"), sipmsg_parse_cseq(msg), with);
541 found = remove_unconfirmed_message(session, key);
542 g_free(key);
544 if (found) {
545 gchar *alias = get_buddy_alias(sipe_private, with);
546 sipe_user_present_message_undelivered(sipe_private, session, -1, -1,
547 alias ? alias : with,
548 msg->body);
549 g_free(alias);
552 g_free(with);
553 return TRUE;
556 static void sipe_im_send_message(struct sipe_core_private *sipe_private,
557 struct sip_dialog *dialog,
558 const gchar *msg_body,
559 const gchar *content_type)
561 gchar *hdr;
562 gchar *tmp;
563 char *msgtext = NULL;
564 const gchar *msgr = "";
565 gchar *tmp2 = NULL;
567 if (content_type == NULL)
568 content_type = "text/plain";
570 if (!g_str_has_prefix(content_type, "text/x-msmsgsinvite")) {
571 char *msgformat;
572 gchar *msgr_value;
574 sipe_parse_html(msg_body, &msgformat, &msgtext);
575 SIPE_DEBUG_INFO("sipe_send_message: msgformat=%s", msgformat);
577 msgr_value = sipmsg_get_msgr_string(msgformat);
578 g_free(msgformat);
579 if (msgr_value) {
580 msgr = tmp2 = g_strdup_printf(";msgr=%s", msgr_value);
581 g_free(msgr_value);
583 } else {
584 msgtext = g_strdup(msg_body);
587 tmp = get_contact(sipe_private);
588 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8\r\n");
589 //hdr = g_strdup("Content-Type: text/rtf\r\n");
590 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8;msgr=WAAtAE0ATQBTAC....AoADQA\r\nSupported: timer\r\n");
592 hdr = g_strdup_printf("Contact: %s\r\nContent-Type: %s; charset=UTF-8%s\r\n", tmp, content_type, msgr);
593 g_free(tmp);
594 g_free(tmp2);
596 sip_transport_request_timeout(sipe_private,
597 "MESSAGE",
598 dialog->with,
599 dialog->with,
600 hdr,
601 msgtext,
602 dialog,
603 process_message_response,
605 process_message_timeout);
606 g_free(msgtext);
607 g_free(hdr);
610 void sipe_im_process_queue(struct sipe_core_private *sipe_private,
611 struct sip_session *session)
613 GSList *entry2 = session->outgoing_message_queue;
614 while (entry2) {
615 struct queued_message *msg = entry2->data;
617 /* for multiparty chat or conference */
618 if (session->chat_session) {
619 gchar *who = sip_uri_self(sipe_private);
620 sipe_backend_chat_message(SIPE_CORE_PUBLIC,
621 session->chat_session->backend,
622 who,
623 msg->body);
624 g_free(who);
627 SIPE_DIALOG_FOREACH {
628 if (dialog->outgoing_invite) continue; /* do not send messages as INVITE is not responded. */
630 insert_unconfirmed_message(session, dialog, dialog->with,
631 msg->body, msg->content_type);
633 sipe_im_send_message(sipe_private, dialog, msg->body, msg->content_type);
634 } SIPE_DIALOG_FOREACH_END;
636 entry2 = sipe_session_dequeue_message(session);
640 struct unconfirmed_callback_data {
641 const gchar *prefix;
642 GSList *list;
645 struct unconfirmed_message {
646 const gchar *key;
647 const struct queued_message *msg;
650 static gint compare_cseq(gconstpointer a,
651 gconstpointer b)
653 return(((struct unconfirmed_message *) a)->msg->cseq -
654 ((struct unconfirmed_message *) b)->msg->cseq);
657 static void unconfirmed_message_callback(gpointer key,
658 gpointer value,
659 gpointer user_data)
661 const gchar *message_key = key;
662 struct unconfirmed_callback_data *data = user_data;
664 SIPE_DEBUG_INFO("unconfirmed_message_callback: key %s", message_key);
666 /* Put messages with the same prefix on a list sorted by CSeq */
667 if (g_str_has_prefix(message_key, data->prefix)) {
668 struct unconfirmed_message *msg = g_malloc(sizeof(struct unconfirmed_message));
669 msg->key = message_key;
670 msg->msg = value;
671 data->list = g_slist_insert_sorted(data->list, msg,
672 compare_cseq);
676 static void foreach_unconfirmed_message(struct sipe_core_private *sipe_private,
677 struct sip_session *session,
678 const gchar *callid,
679 const gchar *with,
680 unconfirmed_callback callback,
681 const gchar *callback_data)
683 gchar *prefix = g_strdup_printf(UNCONFIRMED_KEY_TEMPLATE("MESSAGE", ""),
684 callid, with);
685 struct unconfirmed_callback_data data = { prefix, NULL };
687 SIPE_DEBUG_INFO("foreach_unconfirmed_message: prefix %s", prefix);
689 /* Generate list of matching unconfirmed messages */
690 g_hash_table_foreach(session->unconfirmed_messages,
691 unconfirmed_message_callback,
692 &data);
693 g_free(prefix);
695 /* Process list unconfirmed messages */
696 if (data.list) {
697 GSList *entry;
699 while ((entry = data.list) != NULL) {
700 struct unconfirmed_message *unconfirmed = entry->data;
701 data.list = g_slist_remove(data.list, unconfirmed);
703 SIPE_DEBUG_INFO("foreach_unconfirmed_message: %s", unconfirmed->key);
704 (*callback)(sipe_private, session, unconfirmed->msg->body, callback_data);
706 g_hash_table_remove(session->unconfirmed_messages, unconfirmed->key);
707 g_free(unconfirmed);
712 static void cancel_callback(struct sipe_core_private *sipe_private,
713 struct sip_session *session,
714 const gchar *body,
715 const gchar *with)
717 sipe_user_present_message_undelivered(sipe_private, session,
718 -1, -1, with, body);
721 void sipe_im_cancel_unconfirmed(struct sipe_core_private *sipe_private,
722 struct sip_session *session,
723 const gchar *callid,
724 const gchar *with)
726 gchar *alias = get_buddy_alias(sipe_private, with);
728 SIPE_DEBUG_INFO("sipe_im_cancel_unconfirmed: with %s callid '%s'",
729 with, callid);
731 foreach_unconfirmed_message(sipe_private, session, callid, with,
732 cancel_callback, alias ? alias : with);
733 g_free(alias);
736 static void reenqueue_callback(SIPE_UNUSED_PARAMETER struct sipe_core_private *sipe_private,
737 struct sip_session *session,
738 const gchar *body,
739 SIPE_UNUSED_PARAMETER const gchar *with)
741 sipe_session_enqueue_message(session, body, NULL);
744 void sipe_im_reenqueue_unconfirmed(struct sipe_core_private *sipe_private,
745 struct sip_session *session,
746 const gchar *callid,
747 const gchar *with)
749 /* Remember original list, start with an empty list */
750 GSList *first = session->outgoing_message_queue;
751 session->outgoing_message_queue = NULL;
753 SIPE_DEBUG_INFO("sipe_im_reenqueue_unconfirmed: with %s callid '%s'",
754 with, callid);
756 /* Enqueue unconfirmed messages */
757 foreach_unconfirmed_message(sipe_private, session, callid, with,
758 reenqueue_callback, NULL);
760 /* Append or restore original list */
761 if (session->outgoing_message_queue) {
762 GSList *last = g_slist_last(session->outgoing_message_queue);
763 last->next = first;
764 } else {
765 session->outgoing_message_queue = first;
769 void sipe_core_im_send(struct sipe_core_public *sipe_public,
770 const gchar *who,
771 const gchar *what)
773 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
774 struct sip_session *session;
775 struct sip_dialog *dialog;
776 gchar *uri = sip_uri(who);
778 SIPE_DEBUG_INFO("sipe_core_im_send: '%s'", what);
780 session = sipe_session_find_or_add_im(sipe_private, uri);
781 dialog = sipe_dialog_find(session, uri);
783 /* Queue the message */
784 sipe_session_enqueue_message(session, what, NULL);
786 if (dialog && !dialog->outgoing_invite) {
787 sipe_im_process_queue(sipe_private, session);
788 } else if (!dialog || !dialog->outgoing_invite) {
789 /* Need to send the INVITE to get the outgoing dialog setup */
790 sipe_im_invite(sipe_private, session, uri, what, NULL, NULL, FALSE);
793 g_free(uri);
796 void sipe_im_cancel_dangling(struct sipe_core_private *sipe_private,
797 struct sip_session *session,
798 struct sip_dialog *dialog,
799 const gchar *with,
800 unconfirmed_callback callback)
802 SIPE_DEBUG_INFO_NOFORMAT("sipe_im_cancel_dangling: assuming dangling IM session, dropping it.");
803 sip_transport_bye(sipe_private, dialog);
805 (*callback)(sipe_private, session, dialog->callid, with);
807 /* We might not get a valid reply to our BYE,
808 so make sure the dialog is removed for sure. */
809 sipe_dialog_remove(session, with);
810 /* dialog is no longer valid */
813 void sipe_im_topic(struct sipe_core_private *sipe_private,
814 struct sip_session *session,
815 const gchar *topic)
817 g_free(session->subject);
818 session->subject = g_strdup(topic);
819 sipe_backend_im_topic(SIPE_CORE_PUBLIC, session->with, topic);
822 void process_incoming_info_conversation(struct sipe_core_private *sipe_private,
823 struct sipmsg *msg)
825 sipe_xml *xml = sipe_xml_parse(msg->body, msg->bodylen);
826 const gchar *from = NULL;
827 const gchar *subject = NULL;
830 if (!xml)
831 return;
833 if (sipe_strequal(sipe_xml_name(xml), "ConversationInfo")) {
834 const sipe_xml *node = sipe_xml_child(xml, "From");
835 if (node)
836 from = sipe_xml_attribute(node, "uri");
838 node = sipe_xml_child(xml, "Subject");
839 if (node)
840 subject = sipe_xml_data(node);
843 if (from && subject) {
844 struct sip_session *session;
845 session = sipe_session_find_im(sipe_private, from);
847 if (session)
848 sipe_im_topic(sipe_private, session, subject);
851 sipe_xml_free(xml);
853 sip_transport_response(sipe_private, msg, 200, "OK", NULL);
857 Local Variables:
858 mode: c
859 c-file-style: "bsd"
860 indent-tabs-mode: t
861 tab-width: 8
862 End: