filetransfer: use 'deallocate' callback in sipe_file_transfer
[siplcs.git] / src / core / sipe-chat.c
blobc4566c97cdf399b609c72d08671ddd2ab5eaa2a4
1 /**
2 * @file sipe-chat.c
4 * pidgin-sipe
6 * Copyright (C) 2009-2015 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>
29 #include <time.h>
31 #include <glib.h>
33 #include "sipe-common.h"
34 #include "sipmsg.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"
43 #include "sipe-im.h"
44 #include "sipe-nls.h"
45 #include "sipe-schedule.h"
46 #include "sipe-session.h"
47 #include "sipe-utils.h"
48 #include "sipe-xml.h"
50 /**
51 * Invite @who to chat
53 * @param sipe_private SIPE core private data
54 * @param session SIPE session for chat
55 * @param who URI whom to invite to chat.
57 static void
58 sipe_invite_to_chat(struct sipe_core_private *sipe_private,
59 struct sip_session *session,
60 const gchar *who);
62 static GList *chat_sessions = NULL;
64 struct sipe_chat_session *sipe_chat_create_session(enum sipe_chat_type type,
65 const gchar *id,
66 const gchar *title)
68 struct sipe_chat_session *session = g_new0(struct sipe_chat_session, 1);
69 if (id)
70 session->id = g_strdup(id);
71 session->title = g_strdup(title);
72 session->type = type;
73 chat_sessions = g_list_prepend(chat_sessions, session);
74 return(session);
77 void sipe_chat_remove_session(struct sipe_chat_session *session)
79 chat_sessions = g_list_remove(chat_sessions, session);
80 sipe_backend_chat_session_destroy(session->backend);
81 g_free(session->title);
82 g_free(session->id);
83 g_free(session->join_url);
84 g_free(session->dial_in_conf_id);
85 g_free(session->organizer);
86 g_free(session);
89 void sipe_chat_destroy(void)
91 while (chat_sessions) {
92 struct sipe_chat_session *chat_session = chat_sessions->data;
93 SIPE_DEBUG_INFO("sipe_chat_destroy: '%s' (%s)",
94 chat_session->title, chat_session->id);
95 sipe_chat_remove_session(chat_session);
99 const gchar *sipe_core_chat_id(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public,
100 struct sipe_chat_session *chat_session)
102 return(chat_session->id);
105 void sipe_core_chat_invite(struct sipe_core_public *sipe_public,
106 struct sipe_chat_session *chat_session,
107 const char *name)
109 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
111 SIPE_DEBUG_INFO("sipe_core_chat_create: who '%s'", name);
113 switch (chat_session->type) {
114 case SIPE_CHAT_TYPE_MULTIPARTY:
115 case SIPE_CHAT_TYPE_CONFERENCE:
117 struct sip_session *session = sipe_session_find_chat(sipe_private,
118 chat_session);
120 if (session) {
121 gchar *uri = sip_uri(name);
122 sipe_invite_to_chat(sipe_private, session, uri);
123 g_free(uri);
126 break;
127 case SIPE_CHAT_TYPE_GROUPCHAT:
128 /* @TODO */
129 SIPE_DEBUG_INFO_NOFORMAT("GROUP CHAT: INVITE NOT IMPLEMENTED!");
130 break;
131 default:
132 break;
136 void sipe_core_chat_rejoin(struct sipe_core_public *sipe_public,
137 struct sipe_chat_session *chat_session)
139 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
141 SIPE_DEBUG_INFO("sipe_core_chat_rejoin: '%s'", chat_session->title);
143 switch (chat_session->type) {
144 case SIPE_CHAT_TYPE_MULTIPARTY:
146 struct sip_session *session = sipe_session_add_chat(sipe_private,
147 chat_session,
148 TRUE,
149 NULL);
150 gchar *self = sip_uri_self(sipe_private);
152 sipe_invite_to_chat(sipe_private, session, self);
153 sipe_backend_chat_rejoin(SIPE_CORE_PUBLIC,
154 chat_session->backend,
155 self,
156 chat_session->title);
157 g_free(self);
159 break;
160 case SIPE_CHAT_TYPE_CONFERENCE:
161 sipe_conf_create(sipe_private, chat_session, NULL);
162 break;
163 case SIPE_CHAT_TYPE_GROUPCHAT:
164 sipe_groupchat_rejoin(sipe_private, chat_session);
165 break;
166 default:
167 break;
171 void sipe_core_chat_leave(struct sipe_core_public *sipe_public,
172 struct sipe_chat_session *chat_session)
174 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
176 SIPE_DEBUG_INFO("sipe_core_chat_leave: '%s'", chat_session->title);
178 switch (chat_session->type) {
179 case SIPE_CHAT_TYPE_MULTIPARTY:
180 case SIPE_CHAT_TYPE_CONFERENCE:
182 struct sip_session *session = sipe_session_find_chat(sipe_private,
183 chat_session);
185 if (session) {
186 sipe_session_close(sipe_private, session);
189 break;
190 case SIPE_CHAT_TYPE_GROUPCHAT:
191 sipe_groupchat_leave(sipe_private, chat_session);
192 break;
193 default:
194 break;
198 void sipe_core_chat_send(struct sipe_core_public *sipe_public,
199 struct sipe_chat_session *chat_session,
200 const char *what)
202 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
204 SIPE_DEBUG_INFO("sipe_core_chat_send: '%s' to '%s'",
205 what, chat_session->title);
207 switch (chat_session->type) {
208 case SIPE_CHAT_TYPE_MULTIPARTY:
209 case SIPE_CHAT_TYPE_CONFERENCE:
211 struct sip_session *session = sipe_session_find_chat(sipe_private,
212 chat_session);
214 if (session) {
215 sipe_session_enqueue_message(session,
216 what,
217 NULL);
218 sipe_im_process_queue(sipe_private, session);
221 break;
222 case SIPE_CHAT_TYPE_GROUPCHAT:
223 sipe_groupchat_send(sipe_private, chat_session, what);
224 break;
225 default:
226 break;
230 gchar *
231 sipe_chat_get_name(void)
234 * A non-volatile ID counter.
235 * Should survive connection drop & reconnect.
237 static guint chat_seq = 0;
239 /* Generate next ID */
240 gchar *chat_name = g_strdup_printf(_("Chat #%d"), ++chat_seq);
241 SIPE_DEBUG_INFO("sipe_chat_get_name: added new: %s", chat_name);
243 return chat_name;
246 static void
247 sipe_refer(struct sipe_core_private *sipe_private,
248 struct sip_session *session,
249 const gchar *who)
251 gchar *hdr;
252 gchar *contact;
253 gchar *epid = get_epid(sipe_private);
254 struct sip_dialog *dialog = sipe_dialog_find(session,
255 session->chat_session->id);
256 const char *ourtag = dialog && dialog->ourtag ? dialog->ourtag : NULL;
258 contact = get_contact(sipe_private);
259 hdr = g_strdup_printf(
260 "Contact: %s\r\n"
261 "Refer-to: <%s>\r\n"
262 "Referred-By: <sip:%s>%s%s;epid=%s\r\n"
263 "Require: com.microsoft.rtc-multiparty\r\n",
264 contact,
265 who,
266 sipe_private->username,
267 ourtag ? ";tag=" : "",
268 ourtag ? ourtag : "",
269 epid);
270 g_free(epid);
272 sip_transport_request(sipe_private,
273 "REFER",
274 session->chat_session->id,
275 session->chat_session->id,
276 hdr,
277 NULL,
278 dialog,
279 NULL);
281 g_free(hdr);
282 g_free(contact);
285 static gboolean
286 sipe_is_election_finished(struct sip_session *session)
288 gboolean res = TRUE;
290 SIPE_DIALOG_FOREACH {
291 if (dialog->election_vote == 0) {
292 res = FALSE;
293 break;
295 } SIPE_DIALOG_FOREACH_END;
297 if (res) {
298 session->is_voting_in_progress = FALSE;
300 return res;
303 static gboolean
304 process_info_response(struct sipe_core_private *sipe_private,
305 struct sipmsg *msg,
306 struct transaction *trans);
308 static void
309 sipe_send_election_set_rm(struct sipe_core_private *sipe_private,
310 struct sip_dialog *dialog)
312 const gchar *hdr = "Content-Type: application/x-ms-mim\r\n";
314 gchar *body = g_strdup_printf(
315 "<?xml version=\"1.0\"?>\r\n"
316 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
317 "<SetRM uri=\"sip:%s\"/></action>\r\n",
318 sipe_private->username);
320 sip_transport_info(sipe_private,
321 hdr,
322 body,
323 dialog,
324 process_info_response);
326 g_free(body);
329 void
330 sipe_process_pending_invite_queue(struct sipe_core_private *sipe_private,
331 struct sip_session *session)
333 gchar *invitee;
334 GSList *entry = session->pending_invite_queue;
336 while (entry) {
337 invitee = entry->data;
338 sipe_invite_to_chat(sipe_private, session, invitee);
339 entry = session->pending_invite_queue = g_slist_remove(session->pending_invite_queue, invitee);
340 g_free(invitee);
344 void sipe_chat_set_roster_manager(struct sip_session *session,
345 const gchar *roster_manager)
347 struct sipe_chat_session *chat_session = session->chat_session;
349 g_free(chat_session->id);
350 chat_session->id = NULL;
351 if (roster_manager)
352 chat_session->id = g_strdup(roster_manager);
355 static void
356 sipe_election_result(struct sipe_core_private *sipe_private,
357 void *sess)
359 struct sip_session *session = (struct sip_session *)sess;
360 const gchar *rival = NULL;
362 if (session->chat_session->id) {
363 SIPE_DEBUG_INFO(
364 "sipe_election_result: RM has already been elected in the meantime. It is %s",
365 session->chat_session->id);
366 return;
369 session->is_voting_in_progress = FALSE;
371 SIPE_DIALOG_FOREACH {
372 if (dialog->election_vote < 0) {
373 rival = dialog->with;
374 break;
376 } SIPE_DIALOG_FOREACH_END;
378 if (rival) {
379 SIPE_DEBUG_INFO("sipe_election_result: we loose RM election to %s", rival);
380 } else {
381 gchar *self = sip_uri_self(sipe_private);
383 SIPE_DEBUG_INFO_NOFORMAT("sipe_election_result: we have won RM election!");
385 sipe_chat_set_roster_manager(session, self);
386 g_free(self);
388 SIPE_DIALOG_FOREACH {
389 /* send SetRM to each chat participant*/
390 sipe_send_election_set_rm(sipe_private, dialog);
391 } SIPE_DIALOG_FOREACH_END;
393 session->bid = 0;
395 sipe_process_pending_invite_queue(sipe_private, session);
398 static gboolean
399 process_info_response(struct sipe_core_private *sipe_private,
400 struct sipmsg *msg,
401 SIPE_UNUSED_PARAMETER struct transaction *trans)
403 const gchar *contenttype = sipmsg_find_header(msg, "Content-Type");
404 const gchar *callid = sipmsg_find_header(msg, "Call-ID");
405 struct sip_dialog *dialog;
406 struct sip_session *session;
408 session = sipe_session_find_chat_by_callid(sipe_private, callid);
409 if (!session) {
410 SIPE_DEBUG_INFO("process_info_response: failed find dialog for callid %s, exiting.", callid);
411 return FALSE;
414 if (msg->response == 200 && g_str_has_prefix(contenttype, "application/x-ms-mim")) {
415 sipe_xml *xn_action = sipe_xml_parse(msg->body, msg->bodylen);
416 const sipe_xml *xn_request_rm_response = sipe_xml_child(xn_action, "RequestRMResponse");
417 const sipe_xml *xn_set_rm_response = sipe_xml_child(xn_action, "SetRMResponse");
419 if (xn_request_rm_response) {
420 const char *with = sipe_xml_attribute(xn_request_rm_response, "uri");
421 const char *allow = sipe_xml_attribute(xn_request_rm_response, "allow");
423 dialog = sipe_dialog_find(session, with);
424 if (!dialog) {
425 SIPE_DEBUG_INFO("process_info_response: failed find dialog for %s, exiting.", with);
426 sipe_xml_free(xn_action);
427 return FALSE;
430 if (allow && !g_ascii_strcasecmp(allow, "true")) {
431 SIPE_DEBUG_INFO("process_info_response: %s has voted PRO", with);
432 dialog->election_vote = 1;
433 } else if (allow && !g_ascii_strcasecmp(allow, "false")) {
434 SIPE_DEBUG_INFO("process_info_response: %s has voted CONTRA", with);
435 dialog->election_vote = -1;
438 if (sipe_is_election_finished(session)) {
439 sipe_election_result(sipe_private,
440 session);
443 } else if (xn_set_rm_response) {
446 sipe_xml_free(xn_action);
450 return TRUE;
453 static void
454 sipe_send_election_request_rm(struct sipe_core_private *sipe_private,
455 struct sip_dialog *dialog,
456 int bid)
458 const gchar *hdr = "Content-Type: application/x-ms-mim\r\n";
460 gchar *body = g_strdup_printf(
461 "<?xml version=\"1.0\"?>\r\n"
462 "<action xmlns=\"http://schemas.microsoft.com/sip/multiparty/\">"
463 "<RequestRM uri=\"sip:%s\" bid=\"%d\"/></action>\r\n",
464 sipe_private->username, bid);
466 sip_transport_info(sipe_private,
467 hdr,
468 body,
469 dialog,
470 process_info_response);
472 g_free(body);
475 static void
476 sipe_election_start(struct sipe_core_private *sipe_private,
477 struct sip_session *session)
479 if (session->is_voting_in_progress) {
480 SIPE_DEBUG_INFO_NOFORMAT("sipe_election_start: other election is in progress, exiting.");
481 return;
482 } else {
483 session->is_voting_in_progress = TRUE;
485 session->bid = rand();
487 SIPE_DEBUG_INFO("sipe_election_start: RM election has initiated. Our bid=%d", session->bid);
489 SIPE_DIALOG_FOREACH {
490 /* reset election_vote for each chat participant */
491 dialog->election_vote = 0;
493 /* send RequestRM to each chat participant*/
494 sipe_send_election_request_rm(sipe_private, dialog, session->bid);
495 } SIPE_DIALOG_FOREACH_END;
497 sipe_schedule_seconds(sipe_private,
498 "<+election-result>",
499 session,
501 sipe_election_result,
502 NULL);
505 static void
506 sipe_invite_to_chat(struct sipe_core_private *sipe_private,
507 struct sip_session *session,
508 const gchar *who)
510 /* a conference */
511 if (session->chat_session->type == SIPE_CHAT_TYPE_CONFERENCE)
513 sipe_invite_conf(sipe_private, session, who);
515 else /* a multi-party chat */
517 gchar *self = sip_uri_self(sipe_private);
518 if (session->chat_session->id) {
519 if (sipe_strcase_equal(session->chat_session->id, self)) {
520 sipe_im_invite(sipe_private, session, who, NULL, NULL, NULL, FALSE);
521 } else {
522 sipe_refer(sipe_private, session, who);
524 } else {
525 SIPE_DEBUG_INFO_NOFORMAT("sipe_invite_to_chat: no RM available");
527 session->pending_invite_queue = sipe_utils_slist_insert_unique_sorted(session->pending_invite_queue,
528 g_strdup(who),
529 (GCompareFunc)strcmp,
530 g_free);
531 sipe_election_start(sipe_private, session);
533 g_free(self);
538 Local Variables:
539 mode: c
540 c-file-style: "bsd"
541 indent-tabs-mode: t
542 tab-width: 8
543 End: