6 * Copyright (C) 2009-2017 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
33 #include "sipe-common.h"
35 #include "sip-transport.h"
36 #include "sipe-backend.h"
37 #include "sipe-chat.h"
38 #include "sipe-conf.h"
39 #include "sipe-core.h"
40 #include "sipe-core-private.h"
41 #include "sipe-dialog.h"
42 #include "sipe-groupchat.h"
45 #include "sipe-schedule.h"
46 #include "sipe-session.h"
47 #include "sipe-user.h"
48 #include "sipe-utils.h"
54 * @param sipe_private SIPE core private data
55 * @param session SIPE session for chat
56 * @param who URI whom to invite to chat.
59 sipe_invite_to_chat(struct sipe_core_private
*sipe_private
,
60 struct sip_session
*session
,
63 static GList
*chat_sessions
= NULL
;
65 struct sipe_chat_session
*sipe_chat_create_session(guint type
,
69 struct sipe_chat_session
*session
= g_new0(struct sipe_chat_session
, 1);
71 session
->id
= g_strdup(id
);
72 session
->title
= g_strdup(title
);
74 chat_sessions
= g_list_prepend(chat_sessions
, session
);
78 void sipe_chat_remove_session(struct sipe_chat_session
*session
)
80 chat_sessions
= g_list_remove(chat_sessions
, session
);
81 sipe_backend_chat_session_destroy(session
->backend
);
82 g_free(session
->title
);
84 g_free(session
->join_url
);
85 g_free(session
->dial_in_conf_id
);
86 g_free(session
->organizer
);
88 if (session
->appshare_ask_ctx
) {
89 sipe_user_close_ask(session
->appshare_ask_ctx
);
95 void sipe_chat_destroy(void)
97 while (chat_sessions
) {
98 struct sipe_chat_session
*chat_session
= chat_sessions
->data
;
99 SIPE_DEBUG_INFO("sipe_chat_destroy: '%s' (%s)",
100 chat_session
->title
, chat_session
->id
);
101 sipe_chat_remove_session(chat_session
);
105 const gchar
*sipe_core_chat_id(SIPE_UNUSED_PARAMETER
struct sipe_core_public
*sipe_public
,
106 struct sipe_chat_session
*chat_session
)
108 return(chat_session
->id
);
111 guint
sipe_core_chat_type(struct sipe_chat_session
*chat_session
)
113 return(chat_session
? chat_session
->type
: SIPE_CHAT_TYPE_UNKNOWN
);
116 void sipe_core_chat_invite(struct sipe_core_public
*sipe_public
,
117 struct sipe_chat_session
*chat_session
,
120 struct sipe_core_private
*sipe_private
= SIPE_CORE_PRIVATE
;
122 SIPE_DEBUG_INFO("sipe_core_chat_create: who '%s'", name
);
124 switch (chat_session
->type
) {
125 case SIPE_CHAT_TYPE_MULTIPARTY
:
126 case SIPE_CHAT_TYPE_CONFERENCE
:
128 struct sip_session
*session
= sipe_session_find_chat(sipe_private
,
132 gchar
*uri
= sip_uri(name
);
133 sipe_invite_to_chat(sipe_private
, session
, uri
);
138 case SIPE_CHAT_TYPE_GROUPCHAT
:
140 SIPE_DEBUG_INFO_NOFORMAT("GROUP CHAT: INVITE NOT IMPLEMENTED!");
147 void sipe_core_chat_rejoin(struct sipe_core_public
*sipe_public
,
148 struct sipe_chat_session
*chat_session
)
150 struct sipe_core_private
*sipe_private
= SIPE_CORE_PRIVATE
;
152 SIPE_DEBUG_INFO("sipe_core_chat_rejoin: '%s'", chat_session
->title
);
154 switch (chat_session
->type
) {
155 case SIPE_CHAT_TYPE_MULTIPARTY
:
157 struct sip_session
*session
= sipe_session_add_chat(sipe_private
,
161 gchar
*self
= sip_uri_self(sipe_private
);
163 sipe_invite_to_chat(sipe_private
, session
, self
);
164 sipe_backend_chat_rejoin(SIPE_CORE_PUBLIC
,
165 chat_session
->backend
,
167 chat_session
->title
);
171 case SIPE_CHAT_TYPE_CONFERENCE
:
172 sipe_conf_create(sipe_private
, chat_session
, NULL
);
174 case SIPE_CHAT_TYPE_GROUPCHAT
:
175 sipe_groupchat_rejoin(sipe_private
, chat_session
);
182 void sipe_core_chat_leave(struct sipe_core_public
*sipe_public
,
183 struct sipe_chat_session
*chat_session
)
185 struct sipe_core_private
*sipe_private
= SIPE_CORE_PRIVATE
;
187 SIPE_DEBUG_INFO("sipe_core_chat_leave: '%s'", chat_session
->title
);
189 switch (chat_session
->type
) {
190 case SIPE_CHAT_TYPE_MULTIPARTY
:
191 case SIPE_CHAT_TYPE_CONFERENCE
:
193 struct sip_session
*session
= sipe_session_find_chat(sipe_private
,
197 sipe_session_close(sipe_private
, session
);
201 case SIPE_CHAT_TYPE_GROUPCHAT
:
202 sipe_groupchat_leave(sipe_private
, chat_session
);
209 void sipe_core_chat_send(struct sipe_core_public
*sipe_public
,
210 struct sipe_chat_session
*chat_session
,
213 struct sipe_core_private
*sipe_private
= SIPE_CORE_PRIVATE
;
215 SIPE_DEBUG_INFO("sipe_core_chat_send: '%s' to '%s'",
216 what
, chat_session
->title
);
218 switch (chat_session
->type
) {
219 case SIPE_CHAT_TYPE_MULTIPARTY
:
220 case SIPE_CHAT_TYPE_CONFERENCE
:
222 struct sip_session
*session
= sipe_session_find_chat(sipe_private
,
226 sipe_session_enqueue_message(session
,
229 sipe_im_process_queue(sipe_private
, session
);
233 case SIPE_CHAT_TYPE_GROUPCHAT
:
234 sipe_groupchat_send(sipe_private
, chat_session
, what
);
242 sipe_chat_get_name(void)
245 * A non-volatile ID counter.
246 * Should survive connection drop & reconnect.
248 static guint chat_seq
= 0;
250 /* Generate next ID */
251 gchar
*chat_name
= g_strdup_printf(_("Chat #%d"), ++chat_seq
);
252 SIPE_DEBUG_INFO("sipe_chat_get_name: added new: %s", chat_name
);
258 sipe_refer(struct sipe_core_private
*sipe_private
,
259 struct sip_session
*session
,
264 struct sip_dialog
*dialog
= sipe_dialog_find(session
,
265 session
->chat_session
->id
);
266 const char *ourtag
= dialog
&& dialog
->ourtag
? dialog
->ourtag
: NULL
;
268 contact
= get_contact(sipe_private
);
269 hdr
= g_strdup_printf(
272 "Referred-By: <sip:%s>%s%s;epid=%s\r\n"
273 "Require: com.microsoft.rtc-multiparty\r\n",
276 sipe_private
->username
,
277 ourtag
? ";tag=" : "",
278 ourtag
? ourtag
: "",
279 sip_transport_epid(sipe_private
));
281 sip_transport_request(sipe_private
,
283 session
->chat_session
->id
,
284 session
->chat_session
->id
,
295 sipe_is_election_finished(struct sip_session
*session
)
299 SIPE_DIALOG_FOREACH
{
300 if (dialog
->election_vote
== 0) {
304 } SIPE_DIALOG_FOREACH_END
;
307 session
->is_voting_in_progress
= FALSE
;
313 process_info_response(struct sipe_core_private
*sipe_private
,
315 struct transaction
*trans
);
318 sipe_send_election_set_rm(struct sipe_core_private
*sipe_private
,
319 struct sip_dialog
*dialog
)
321 const gchar
*hdr
= "Content-Type: application/x-ms-mim\r\n";
323 gchar
*body
= g_strdup_printf(
324 "<?xml version=\"1.0\"?>\r\n"
325 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
326 "<SetRM uri=\"sip:%s\"/></action>\r\n",
327 sipe_private
->username
);
329 sip_transport_info(sipe_private
,
333 process_info_response
);
339 sipe_process_pending_invite_queue(struct sipe_core_private
*sipe_private
,
340 struct sip_session
*session
)
343 GSList
*entry
= session
->pending_invite_queue
;
346 invitee
= entry
->data
;
347 sipe_invite_to_chat(sipe_private
, session
, invitee
);
348 entry
= session
->pending_invite_queue
= g_slist_remove(session
->pending_invite_queue
, invitee
);
353 void sipe_chat_set_roster_manager(struct sip_session
*session
,
354 const gchar
*roster_manager
)
356 struct sipe_chat_session
*chat_session
= session
->chat_session
;
358 g_free(chat_session
->id
);
359 chat_session
->id
= NULL
;
361 chat_session
->id
= g_strdup(roster_manager
);
365 sipe_election_result(struct sipe_core_private
*sipe_private
,
368 struct sip_session
*session
= (struct sip_session
*)sess
;
369 const gchar
*rival
= NULL
;
371 if (session
->chat_session
->id
) {
373 "sipe_election_result: RM has already been elected in the meantime. It is %s",
374 session
->chat_session
->id
);
378 session
->is_voting_in_progress
= FALSE
;
380 SIPE_DIALOG_FOREACH
{
381 if (dialog
->election_vote
< 0) {
382 rival
= dialog
->with
;
385 } SIPE_DIALOG_FOREACH_END
;
388 SIPE_DEBUG_INFO("sipe_election_result: we loose RM election to %s", rival
);
390 gchar
*self
= sip_uri_self(sipe_private
);
392 SIPE_DEBUG_INFO_NOFORMAT("sipe_election_result: we have won RM election!");
394 sipe_chat_set_roster_manager(session
, self
);
397 SIPE_DIALOG_FOREACH
{
398 /* send SetRM to each chat participant*/
399 sipe_send_election_set_rm(sipe_private
, dialog
);
400 } SIPE_DIALOG_FOREACH_END
;
404 sipe_process_pending_invite_queue(sipe_private
, session
);
408 process_info_response(struct sipe_core_private
*sipe_private
,
410 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
412 const gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
413 const gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
414 struct sip_dialog
*dialog
;
415 struct sip_session
*session
;
417 session
= sipe_session_find_chat_by_callid(sipe_private
, callid
);
419 SIPE_DEBUG_INFO("process_info_response: failed find dialog for callid %s, exiting.", callid
);
423 if (msg
->response
== 200 && g_str_has_prefix(contenttype
, "application/x-ms-mim")) {
424 sipe_xml
*xn_action
= sipe_xml_parse(msg
->body
, msg
->bodylen
);
425 const sipe_xml
*xn_request_rm_response
= sipe_xml_child(xn_action
, "RequestRMResponse");
426 const sipe_xml
*xn_set_rm_response
= sipe_xml_child(xn_action
, "SetRMResponse");
428 if (xn_request_rm_response
) {
429 const char *with
= sipe_xml_attribute(xn_request_rm_response
, "uri");
430 const char *allow
= sipe_xml_attribute(xn_request_rm_response
, "allow");
432 dialog
= sipe_dialog_find(session
, with
);
434 SIPE_DEBUG_INFO("process_info_response: failed find dialog for %s, exiting.", with
);
435 sipe_xml_free(xn_action
);
439 if (allow
&& !g_ascii_strcasecmp(allow
, "true")) {
440 SIPE_DEBUG_INFO("process_info_response: %s has voted PRO", with
);
441 dialog
->election_vote
= 1;
442 } else if (allow
&& !g_ascii_strcasecmp(allow
, "false")) {
443 SIPE_DEBUG_INFO("process_info_response: %s has voted CONTRA", with
);
444 dialog
->election_vote
= -1;
447 if (sipe_is_election_finished(session
)) {
448 sipe_election_result(sipe_private
,
452 } else if (xn_set_rm_response
) {
455 sipe_xml_free(xn_action
);
463 sipe_send_election_request_rm(struct sipe_core_private
*sipe_private
,
464 struct sip_dialog
*dialog
,
467 const gchar
*hdr
= "Content-Type: application/x-ms-mim\r\n";
469 gchar
*body
= g_strdup_printf(
470 "<?xml version=\"1.0\"?>\r\n"
471 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
472 "<RequestRM uri=\"sip:%s\" bid=\"%d\"/></action>\r\n",
473 sipe_private
->username
, bid
);
475 sip_transport_info(sipe_private
,
479 process_info_response
);
485 sipe_election_start(struct sipe_core_private
*sipe_private
,
486 struct sip_session
*session
)
488 if (session
->is_voting_in_progress
) {
489 SIPE_DEBUG_INFO_NOFORMAT("sipe_election_start: other election is in progress, exiting.");
492 session
->is_voting_in_progress
= TRUE
;
494 session
->bid
= rand();
496 SIPE_DEBUG_INFO("sipe_election_start: RM election has initiated. Our bid=%d", session
->bid
);
498 SIPE_DIALOG_FOREACH
{
499 /* reset election_vote for each chat participant */
500 dialog
->election_vote
= 0;
502 /* send RequestRM to each chat participant*/
503 sipe_send_election_request_rm(sipe_private
, dialog
, session
->bid
);
504 } SIPE_DIALOG_FOREACH_END
;
506 sipe_schedule_seconds(sipe_private
,
507 "<+election-result>",
510 sipe_election_result
,
515 sipe_invite_to_chat(struct sipe_core_private
*sipe_private
,
516 struct sip_session
*session
,
520 if (session
->chat_session
->type
== SIPE_CHAT_TYPE_CONFERENCE
)
522 sipe_invite_conf(sipe_private
, session
, who
);
524 else /* a multi-party chat */
526 gchar
*self
= sip_uri_self(sipe_private
);
527 if (session
->chat_session
->id
) {
528 if (sipe_strcase_equal(session
->chat_session
->id
, self
)) {
529 sipe_im_invite(sipe_private
, session
, who
, NULL
, NULL
, NULL
, FALSE
);
531 sipe_refer(sipe_private
, session
, who
);
534 SIPE_DEBUG_INFO_NOFORMAT("sipe_invite_to_chat: no RM available");
536 session
->pending_invite_queue
= sipe_utils_slist_insert_unique_sorted(session
->pending_invite_queue
,
538 (GCompareFunc
)strcmp
,
540 sipe_election_start(sipe_private
, session
);