Fix #3161273: Lost Connection Gives No Error Message (part V)
[siplcs.git] / src / core / sipe-im.c
blob10f9da6210d2b40671fafc6ad8ee2eac923f3829
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"
49 * Hash key template for unconfirmed messages
51 * Call-ID Recipient URI (or empty)
52 * | |
53 * | SIP method | CSeq
54 * | | | | */
55 #define UNCONFIRMED_KEY_TEMPLATE(method, cseq) "<%s><" method "><%s><" cseq
57 /* key must be g_free()'d */
58 static gchar *get_unconfirmed_message_key(const gchar *callid,
59 unsigned int cseq,
60 const gchar *with)
62 return(g_strdup_printf(UNCONFIRMED_KEY_TEMPLATE("%s", "%d>"),
63 callid,
64 with ? "MESSAGE" : "INVITE",
65 with ? with : "",
66 cseq));
69 static void insert_unconfirmed_message(struct sip_session *session,
70 struct sip_dialog *dialog,
71 const gchar *with,
72 const gchar *body,
73 const gchar *content_type)
75 gchar *key = get_unconfirmed_message_key(dialog->callid, dialog->cseq + 1, with);
76 struct queued_message *message = g_new0(struct queued_message, 1);
78 message->body = g_strdup(body);
79 if (content_type != NULL)
80 message->content_type = g_strdup(content_type);
81 message->cseq = dialog->cseq + 1;
83 g_hash_table_insert(session->unconfirmed_messages, key, message);
84 SIPE_DEBUG_INFO("insert_confirmed_message: added %s to list (count=%d)",
85 key, g_hash_table_size(session->unconfirmed_messages));
88 static void remove_unconfirmed_message(struct sip_session *session,
89 const gchar *key)
91 g_hash_table_remove(session->unconfirmed_messages, key);
92 SIPE_DEBUG_INFO("remove_unconfirmed_message: removed %s from list (count=%d)",
93 key, g_hash_table_size(session->unconfirmed_messages));
96 static void sipe_refer_notify(struct sipe_core_private *sipe_private,
97 struct sip_session *session,
98 const gchar *who,
99 int status,
100 const gchar *desc)
102 gchar *hdr;
103 gchar *body;
104 struct sip_dialog *dialog = sipe_dialog_find(session, who);
106 hdr = g_strdup_printf(
107 "Event: refer\r\n"
108 "Subscription-State: %s\r\n"
109 "Content-Type: message/sipfrag\r\n",
110 status >= 200 ? "terminated" : "active");
112 body = g_strdup_printf(
113 "SIP/2.0 %d %s\r\n",
114 status, desc);
116 sip_transport_request(sipe_private,
117 "NOTIFY",
118 who,
119 who,
120 hdr,
121 body,
122 dialog,
123 NULL);
125 g_free(hdr);
126 g_free(body);
129 static gchar *get_buddy_alias(struct sipe_core_private *sipe_private,
130 const gchar *with)
132 sipe_backend_buddy pbuddy;
133 gchar *alias = NULL;
134 if ((pbuddy = sipe_backend_buddy_find(SIPE_CORE_PUBLIC, with, NULL))) {
135 alias = sipe_backend_buddy_get_alias(SIPE_CORE_PUBLIC, pbuddy);
137 return(alias);
140 static gboolean process_invite_response(struct sipe_core_private *sipe_private,
141 struct sipmsg *msg,
142 struct transaction *trans)
144 gchar *with = parse_from(sipmsg_find_header(msg, "To"));
145 struct sip_session *session;
146 struct sip_dialog *dialog;
147 gchar *key;
148 struct queued_message *message;
149 struct sipmsg *request_msg = trans->msg;
151 const gchar *callid = sipmsg_find_header(msg, "Call-ID");
152 gchar *referred_by;
154 session = sipe_session_find_chat_or_im(sipe_private, callid, with);
155 if (!session) {
156 SIPE_DEBUG_INFO_NOFORMAT("process_invite_response: unable to find IM session");
157 g_free(with);
158 return FALSE;
161 dialog = sipe_dialog_find(session, with);
162 if (!dialog) {
163 SIPE_DEBUG_INFO_NOFORMAT("process_invite_response: session outgoing dialog is NULL");
164 g_free(with);
165 return FALSE;
168 sipe_dialog_parse(dialog, msg, TRUE);
170 key = get_unconfirmed_message_key(dialog->callid, sipmsg_parse_cseq(msg), NULL);
171 message = g_hash_table_lookup(session->unconfirmed_messages, key);
173 if (msg->response != 200) {
174 gchar *alias = get_buddy_alias(sipe_private, with);
175 const char *warn_hdr = sipmsg_find_header(msg, "Warning");
176 int warning = -1;
178 SIPE_DEBUG_INFO_NOFORMAT("process_invite_response: INVITE response not 200");
180 if (warn_hdr) {
181 gchar **parts = g_strsplit(warn_hdr, " ", 2);
182 if (parts[0]) {
183 warning = atoi(parts[0]);
185 g_strfreev(parts);
188 /* cancel file transfer as rejected by server */
189 if (msg->response == 606 && /* Not acceptable all. */
190 warning == 309 && /* Message contents not allowed by policy */
191 message && g_str_has_prefix(message->content_type, "text/x-msmsgsinvite"))
193 GSList *parsed_body = sipe_ft_parse_msg_body(message->body);
194 sipe_ft_incoming_cancel(dialog, parsed_body);
195 sipe_utils_nameval_free(parsed_body);
198 if (message) {
199 sipe_user_present_message_undelivered(sipe_private, session, msg->response, warning, alias ? alias : with, message->body);
200 } else {
201 gchar *tmp_msg = g_strdup_printf(_("Failed to invite %s"), alias);
202 sipe_user_present_error(sipe_private, session, tmp_msg);
203 g_free(tmp_msg);
205 g_free(alias);
207 remove_unconfirmed_message(session, key);
208 /* message is no longer valid */
209 g_free(key);
211 sipe_dialog_remove(session, with);
212 g_free(with);
214 if (session->is_groupchat) {
215 sipe_groupchat_invite_failed(sipe_private, session);
216 /* session is no longer valid */
219 return FALSE;
222 dialog->cseq = 0;
223 sip_transport_ack(sipe_private, dialog);
224 dialog->outgoing_invite = NULL;
225 dialog->is_established = TRUE;
227 referred_by = parse_from(sipmsg_find_header(request_msg, "Referred-By"));
228 if (referred_by) {
229 sipe_refer_notify(sipe_private, session, referred_by, 200, "OK");
230 g_free(referred_by);
233 /* add user to chat if it is a multiparty session */
234 if (session->chat_session &&
235 (session->chat_session->type == SIPE_CHAT_TYPE_MULTIPARTY)) {
236 sipe_backend_chat_add(session->chat_session->backend,
237 with,
238 TRUE);
241 if (session->is_groupchat) {
242 sipe_groupchat_invite_response(sipe_private, dialog);
245 if(g_slist_find_custom(dialog->supported, "ms-text-format", (GCompareFunc)g_ascii_strcasecmp)) {
246 SIPE_DEBUG_INFO_NOFORMAT("process_invite_response: remote system accepted message in INVITE");
247 sipe_session_dequeue_message(session);
250 sipe_im_process_queue(sipe_private, session);
252 remove_unconfirmed_message(session, key);
254 g_free(key);
255 g_free(with);
256 return TRUE;
259 /* EndPoints: "alice alisson" <sip:alice@atlanta.local>, <sip:bob@atlanta.local>;epid=ebca82d94d, <sip:carol@atlanta.local> */
260 static gchar *get_end_points(struct sipe_core_private *sipe_private,
261 struct sip_session *session)
263 gchar *res;
265 if (session == NULL) {
266 return NULL;
269 res = g_strdup_printf("<sip:%s>", sipe_private->username);
271 SIPE_DIALOG_FOREACH {
272 gchar *tmp = res;
273 res = g_strdup_printf("%s, <%s>", res, dialog->with);
274 g_free(tmp);
276 if (dialog->theirepid) {
277 tmp = res;
278 res = g_strdup_printf("%s;epid=%s", res, dialog->theirepid);
279 g_free(tmp);
281 } SIPE_DIALOG_FOREACH_END;
283 return res;
286 void
287 sipe_im_invite(struct sipe_core_private *sipe_private,
288 struct sip_session *session,
289 const gchar *who,
290 const gchar *msg_body,
291 const gchar *content_type,
292 const gchar *referred_by,
293 const gboolean is_triggered)
295 gchar *hdr;
296 gchar *to;
297 gchar *contact;
298 gchar *body;
299 gchar *self;
300 char *ms_text_format = NULL;
301 char *ms_conversation_id = NULL;
302 gchar *roster_manager;
303 gchar *end_points;
304 gchar *referred_by_str;
305 gboolean is_multiparty =
306 session->chat_session &&
307 (session->chat_session->type == SIPE_CHAT_TYPE_MULTIPARTY);
308 struct sip_dialog *dialog = sipe_dialog_find(session, who);
310 if (dialog && dialog->is_established) {
311 SIPE_DEBUG_INFO("session with %s already has a dialog open", who);
312 return;
315 if (!dialog) {
316 dialog = sipe_dialog_add(session);
317 dialog->callid = session->callid ? g_strdup(session->callid) : gencallid();
318 dialog->with = g_strdup(who);
321 if (!(dialog->ourtag)) {
322 dialog->ourtag = gentag();
325 to = sip_uri(who);
327 if (msg_body) {
328 char *msgtext = NULL;
329 char *base64_msg;
330 const gchar *msgr = "";
331 gchar *tmp = NULL;
333 if (!g_str_has_prefix(content_type, "text/x-msmsgsinvite")) {
334 char *msgformat;
335 gchar *msgr_value;
337 sipe_parse_html(msg_body, &msgformat, &msgtext);
338 SIPE_DEBUG_INFO("sipe_invite: msgformat=%s", msgformat);
340 msgr_value = sipmsg_get_msgr_string(msgformat);
341 g_free(msgformat);
342 if (msgr_value) {
343 msgr = tmp = g_strdup_printf(";msgr=%s", msgr_value);
344 g_free(msgr_value);
347 /* When Sipe reconnects after a crash, we are not able
348 * to send messages to contacts with which we had open
349 * conversations when the crash occured. Server sends
350 * error response with reason="This client has an IM
351 * session with the same conversation ID"
353 * Setting random Ms-Conversation-ID prevents this problem
354 * so we can continue the conversation. */
355 ms_conversation_id = g_strdup_printf("Ms-Conversation-ID: %u\r\n",
356 rand() % 1000000000);
357 } else {
358 msgtext = g_strdup(msg_body);
361 base64_msg = g_base64_encode((guchar*) msgtext, strlen(msgtext));
362 ms_text_format = g_strdup_printf("ms-text-format: %s; charset=UTF-8%s;ms-body=%s\r\n",
363 content_type ? content_type : "text/plain",
364 msgr,
365 base64_msg);
366 g_free(msgtext);
367 g_free(tmp);
368 g_free(base64_msg);
370 insert_unconfirmed_message(session, dialog, NULL,
371 msg_body, content_type);
374 contact = get_contact(sipe_private);
375 end_points = get_end_points(sipe_private, session);
376 self = sip_uri_self(sipe_private);
377 roster_manager = g_strdup_printf(
378 "Roster-Manager: %s\r\n"
379 "EndPoints: %s\r\n",
380 self,
381 end_points);
382 referred_by_str = referred_by ?
383 g_strdup_printf(
384 "Referred-By: %s\r\n",
385 referred_by)
386 : g_strdup("");
387 hdr = g_strdup_printf(
388 "Supported: ms-sender\r\n"
389 "%s"
390 "%s"
391 "%s"
392 "%s"
393 "Contact: %s\r\n%s"
394 "%s"
395 "Content-Type: application/sdp\r\n",
396 is_multiparty && sipe_strcase_equal(session->chat_session->id, self) ? roster_manager : "",
397 referred_by_str,
398 is_triggered ? "TriggeredInvite: TRUE\r\n" : "",
399 is_triggered || is_multiparty ? "Require: com.microsoft.rtc-multiparty\r\n" : "",
400 contact,
401 ms_text_format ? ms_text_format : "",
402 ms_conversation_id ? ms_conversation_id : "");
403 g_free(ms_text_format);
404 g_free(ms_conversation_id);
405 g_free(self);
407 body = g_strdup_printf(
408 "v=0\r\n"
409 "o=- 0 0 IN IP4 %s\r\n"
410 "s=session\r\n"
411 "c=IN IP4 %s\r\n"
412 "t=0 0\r\n"
413 "m=%s %d sip null\r\n"
414 "a=accept-types:" SDP_ACCEPT_TYPES "\r\n",
415 sipe_backend_network_ip_address(),
416 sipe_backend_network_ip_address(),
417 SIPE_CORE_PRIVATE_FLAG_IS(OCS2007) ? "message" : "x-ms-message",
418 sip_transport_port(sipe_private));
420 dialog->outgoing_invite = sip_transport_request(sipe_private,
421 "INVITE",
424 hdr,
425 body,
426 dialog,
427 process_invite_response);
429 g_free(to);
430 g_free(roster_manager);
431 g_free(end_points);
432 g_free(referred_by_str);
433 g_free(body);
434 g_free(hdr);
435 g_free(contact);
438 static gboolean
439 process_message_response(struct sipe_core_private *sipe_private,
440 struct sipmsg *msg,
441 SIPE_UNUSED_PARAMETER struct transaction *trans)
443 gboolean ret = TRUE;
444 gchar *with = parse_from(sipmsg_find_header(msg, "To"));
445 struct sip_session *session = sipe_session_find_im(sipe_private, with);
446 struct sip_dialog *dialog;
447 gchar *key;
448 struct queued_message *message;
450 if (!session) {
451 SIPE_DEBUG_INFO_NOFORMAT("process_message_response: unable to find IM session");
452 g_free(with);
453 return FALSE;
456 dialog = sipe_dialog_find(session, with);
457 if (!dialog) {
458 SIPE_DEBUG_INFO_NOFORMAT("process_message_response: session outgoing dialog is NULL");
459 g_free(with);
460 return FALSE;
463 key = get_unconfirmed_message_key(sipmsg_find_header(msg, "Call-ID"), sipmsg_parse_cseq(msg), with);
464 message = g_hash_table_lookup(session->unconfirmed_messages, key);
466 if (msg->response >= 400) {
467 gchar *alias = get_buddy_alias(sipe_private, with);
468 const char *warn_hdr = sipmsg_find_header(msg, "Warning");
469 int warning = -1;
471 SIPE_DEBUG_INFO_NOFORMAT("process_message_response: MESSAGE response >= 400");
473 if (warn_hdr) {
474 gchar **parts = g_strsplit(warn_hdr, " ", 2);
475 if (parts[0]) {
476 warning = atoi(parts[0]);
478 g_strfreev(parts);
481 /* cancel file transfer as rejected by server */
482 if (msg->response == 606 && /* Not acceptable all. */
483 warning == 309 && /* Message contents not allowed by policy */
484 message && g_str_has_prefix(message->content_type, "text/x-msmsgsinvite"))
486 GSList *parsed_body = sipe_ft_parse_msg_body(msg->body);
487 sipe_ft_incoming_cancel(dialog, parsed_body);
488 sipe_utils_nameval_free(parsed_body);
491 sipe_user_present_message_undelivered(sipe_private, session,
492 msg->response, warning,
493 alias ? alias : with,
494 message ? message->body : NULL);
495 g_free(alias);
497 /* drop dangling IM sessions: assume that BYE from remote never reached us */
498 if (msg->response == 408 || /* Request timeout */
499 msg->response == 480 || /* Temporarily Unavailable */
500 msg->response == 481) { /* Call/Transaction Does Not Exist */
501 SIPE_DEBUG_INFO_NOFORMAT("process_message_response: assuming dangling IM session, dropping it.");
502 sip_transport_bye(sipe_private, dialog);
504 /* We might not get a valid reply to our BYE,
505 so make sure the dialog is removed for sure. */
506 sipe_dialog_remove(session, with);
507 dialog = NULL;
510 ret = FALSE;
511 } else {
512 const gchar *message_id = sipmsg_find_header(msg, "Message-Id");
513 if (message_id) {
514 g_hash_table_insert(session->conf_unconfirmed_messages, g_strdup(message_id), g_strdup(message->body));
515 SIPE_DEBUG_INFO("process_message_response: added message with id %s to conf_unconfirmed_messages(count=%d)",
516 message_id, g_hash_table_size(session->conf_unconfirmed_messages));
521 remove_unconfirmed_message(session, key);
522 g_free(key);
523 g_free(with);
525 if (ret) sipe_im_process_queue(sipe_private, session);
526 return ret;
529 static gboolean
530 process_message_timeout(struct sipe_core_private *sipe_private,
531 struct sipmsg *msg,
532 SIPE_UNUSED_PARAMETER struct transaction *trans)
534 gchar *with = parse_from(sipmsg_find_header(msg, "To"));
535 struct sip_session *session = sipe_session_find_im(sipe_private, with);
536 gchar *key;
537 gchar *alias = get_buddy_alias(sipe_private, with);
539 if (!session) {
540 SIPE_DEBUG_INFO_NOFORMAT("process_message_timeout: unable to find IM session");
541 g_free(with);
542 return TRUE;
545 /* Remove timed-out message from unconfirmed list */
546 key = get_unconfirmed_message_key(sipmsg_find_header(msg, "Call-ID"), sipmsg_parse_cseq(msg), with);
547 remove_unconfirmed_message(session, key);
548 g_free(key);
550 sipe_user_present_message_undelivered(sipe_private, session, -1, -1,
551 alias ? alias : with,
552 msg->body);
554 g_free(alias);
555 g_free(with);
556 return TRUE;
559 static void sipe_im_send_message(struct sipe_core_private *sipe_private,
560 struct sip_dialog *dialog,
561 const gchar *msg_body,
562 const gchar *content_type)
564 gchar *hdr;
565 gchar *tmp;
566 char *msgtext = NULL;
567 const gchar *msgr = "";
568 gchar *tmp2 = NULL;
570 if (content_type == NULL)
571 content_type = "text/plain";
573 if (!g_str_has_prefix(content_type, "text/x-msmsgsinvite")) {
574 char *msgformat;
575 gchar *msgr_value;
577 sipe_parse_html(msg_body, &msgformat, &msgtext);
578 SIPE_DEBUG_INFO("sipe_send_message: msgformat=%s", msgformat);
580 msgr_value = sipmsg_get_msgr_string(msgformat);
581 g_free(msgformat);
582 if (msgr_value) {
583 msgr = tmp2 = g_strdup_printf(";msgr=%s", msgr_value);
584 g_free(msgr_value);
586 } else {
587 msgtext = g_strdup(msg_body);
590 tmp = get_contact(sipe_private);
591 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8\r\n");
592 //hdr = g_strdup("Content-Type: text/rtf\r\n");
593 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8;msgr=WAAtAE0ATQBTAC....AoADQA\r\nSupported: timer\r\n");
595 hdr = g_strdup_printf("Contact: %s\r\nContent-Type: %s; charset=UTF-8%s\r\n", tmp, content_type, msgr);
596 g_free(tmp);
597 g_free(tmp2);
599 sip_transport_request_timeout(sipe_private,
600 "MESSAGE",
601 dialog->with,
602 dialog->with,
603 hdr,
604 msgtext,
605 dialog,
606 process_message_response,
608 process_message_timeout);
609 g_free(msgtext);
610 g_free(hdr);
613 void sipe_im_process_queue(struct sipe_core_private *sipe_private,
614 struct sip_session *session)
616 GSList *entry2 = session->outgoing_message_queue;
617 while (entry2) {
618 struct queued_message *msg = entry2->data;
620 /* for multiparty chat or conference */
621 if (session->chat_session) {
622 gchar *who = sip_uri_self(sipe_private);
623 sipe_backend_chat_message(SIPE_CORE_PUBLIC,
624 session->chat_session->backend,
625 who,
626 msg->body);
627 g_free(who);
630 SIPE_DIALOG_FOREACH {
631 if (dialog->outgoing_invite) continue; /* do not send messages as INVITE is not responded. */
633 insert_unconfirmed_message(session, dialog, dialog->with,
634 msg->body, msg->content_type);
636 sipe_im_send_message(sipe_private, dialog, msg->body, msg->content_type);
637 } SIPE_DIALOG_FOREACH_END;
639 entry2 = sipe_session_dequeue_message(session);
643 struct unconfirmed_callback_data {
644 const gchar *prefix;
645 GSList *list;
648 struct unconfirmed_message {
649 const gchar *key;
650 const struct queued_message *msg;
653 static gint compare_cseq(gconstpointer a,
654 gconstpointer b)
656 return(((struct unconfirmed_message *) a)->msg->cseq -
657 ((struct unconfirmed_message *) b)->msg->cseq);
660 static void unconfirmed_message_callback(gpointer key,
661 gpointer value,
662 gpointer user_data)
664 const gchar *message_key = key;
665 struct unconfirmed_callback_data *data = user_data;
667 /* Put messages with the same prefix on a list sorted by CSeq */
668 if (g_str_has_prefix(message_key, data->prefix)) {
669 struct unconfirmed_message *msg = g_malloc(sizeof(struct unconfirmed_message));
670 msg->key = message_key;
671 msg->msg = value;
672 data->list = g_slist_insert_sorted(data->list, msg,
673 compare_cseq);
677 static void foreach_unconfirmed_message(struct sipe_core_private *sipe_private,
678 struct sip_session *session,
679 const gchar *callid,
680 const gchar *with,
681 void (*callback)(struct sipe_core_private *,
682 struct sip_session *,
683 const gchar *,
684 const gchar *),
685 const gchar *callback_data)
687 gchar *prefix = g_strdup_printf(UNCONFIRMED_KEY_TEMPLATE("MESSAGE", ""),
688 callid, with);
689 struct unconfirmed_callback_data data = { prefix, NULL };
691 /* Generate list of matching unconfirmed messages */
692 g_hash_table_foreach(session->unconfirmed_messages,
693 unconfirmed_message_callback,
694 &data);
695 g_free(prefix);
697 /* Process list unconfirmed messages */
698 if (data.list) {
699 GSList *entry;
701 while ((entry = data.list) != NULL) {
702 struct unconfirmed_message *unconfirmed = entry->data;
703 data.list = g_slist_remove(data.list, unconfirmed);
705 SIPE_DEBUG_INFO("foreach_unconfirmed_message: %s", unconfirmed->key);
706 (*callback)(sipe_private, session, unconfirmed->msg->body, callback_data);
708 g_hash_table_remove(session->unconfirmed_messages, unconfirmed->key);
709 g_free(unconfirmed);
714 static void cancel_callback(struct sipe_core_private *sipe_private,
715 struct sip_session *session,
716 const gchar *body,
717 const gchar *with)
719 sipe_user_present_message_undelivered(sipe_private, session,
720 -1, -1, with, body);
723 void sipe_im_cancel_unconfirmed(struct sipe_core_private *sipe_private,
724 struct sip_session *session,
725 const gchar *callid,
726 const gchar *with)
728 gchar *alias = get_buddy_alias(sipe_private, with);
729 foreach_unconfirmed_message(sipe_private, session, callid, with,
730 cancel_callback, alias ? alias : with);
731 g_free(alias);
734 static void reenqueue_callback(SIPE_UNUSED_PARAMETER struct sipe_core_private *sipe_private,
735 struct sip_session *session,
736 const gchar *body,
737 SIPE_UNUSED_PARAMETER const gchar *with)
739 sipe_session_enqueue_message(session, body, NULL);
742 void sipe_im_reenqueue_unconfirmed(struct sipe_core_private *sipe_private,
743 struct sip_session *session,
744 const gchar *callid,
745 const gchar *with)
747 /* Remember original list, start with an empty list */
748 GSList *first = session->outgoing_message_queue;
749 session->outgoing_message_queue = NULL;
751 /* Enqueue unconfirmed messages */
752 foreach_unconfirmed_message(sipe_private, session, callid, with,
753 reenqueue_callback, NULL);
755 /* Append or restore original list */
756 if (session->outgoing_message_queue) {
757 GSList *last = g_slist_last(session->outgoing_message_queue);
758 last->next = first;
759 } else {
760 session->outgoing_message_queue = first;
764 void sipe_core_im_send(struct sipe_core_public *sipe_public,
765 const gchar *who,
766 const gchar *what)
768 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
769 struct sip_session *session;
770 struct sip_dialog *dialog;
771 gchar *uri = sip_uri(who);
773 SIPE_DEBUG_INFO("sipe_core_im_send: '%s'", what);
775 session = sipe_session_find_or_add_im(sipe_private, uri);
776 dialog = sipe_dialog_find(session, uri);
778 /* Queue the message */
779 sipe_session_enqueue_message(session, what, NULL);
781 if (dialog && !dialog->outgoing_invite) {
782 sipe_im_process_queue(sipe_private, session);
783 } else if (!dialog || !dialog->outgoing_invite) {
784 /* Need to send the INVITE to get the outgoing dialog setup */
785 sipe_im_invite(sipe_private, session, uri, what, NULL, NULL, FALSE);
788 g_free(uri);
792 Local Variables:
793 mode: c
794 c-file-style: "bsd"
795 indent-tabs-mode: t
796 tab-width: 8
797 End: