Free session focus dialog
[siplcs.git] / src / sipe-conf.c
blob58610dfb6ce67a812943484a66a2dd5d0313ee71
1 /**
2 * @file sipe-conf.c
4 * pidgin-sipe
6 * Copyright (C) 2009 pier11 <pier11@kinozal.tv>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 #include <string.h>
25 #include <glib.h>
27 #include "debug.h"
29 #include "sipe.h"
30 #include "sipe-conf.h"
31 #include "sipe-dialog.h"
32 #include "sipe-nls.h"
33 #include "sipe-session.h"
34 #include "sipe-utils.h"
36 /**
37 * Add Conference request to FocusFactory.
38 * @param focus_factory_uri (%s) Ex.: sip:bob7@boston.local;gruu;opaque=app:conf:focusfactory
39 * @param from (%s) Ex.: sip:bob7@boston.local
40 * @param request_id (%d) Ex.: 1094520
41 * @param conference_id (%s) Ex.: 8386E6AEAAA41E4AA6627BA76D43B6D1
42 * @param expiry_time (%s) Ex.: 2009-07-13T17:57:09Z , Default duration: 7 hours
44 #define SIPE_SEND_CONF_ADD \
45 "<?xml version=\"1.0\"?>"\
46 "<request xmlns=\"urn:ietf:params:xml:ns:cccp\" "\
47 "xmlns:mscp=\"http://schemas.microsoft.com/rtc/2005/08/cccpextensions\" "\
48 "C3PVersion=\"1\" "\
49 "to=\"%s\" "\
50 "from=\"%s\" "\
51 "requestId=\"%d\">"\
52 "<addConference>"\
53 "<ci:conference-info xmlns:ci=\"urn:ietf:params:xml:ns:conference-info\" entity=\"\" xmlns:msci=\"http://schemas.microsoft.com/rtc/2005/08/confinfoextensions\">"\
54 "<ci:conference-description>"\
55 "<ci:subject/>"\
56 "<msci:conference-id>%s</msci:conference-id>"\
57 "<msci:expiry-time>%s</msci:expiry-time>"\
58 "<msci:admission-policy>openAuthenticated</msci:admission-policy>"\
59 "</ci:conference-description>"\
60 "<msci:conference-view>"\
61 "<msci:entity-view entity=\"chat\"/>"\
62 "</msci:conference-view>"\
63 "</ci:conference-info>"\
64 "</addConference>"\
65 "</request>"
67 /**
68 * AddUser request to Focus.
69 * Params:
70 * focus_URI, from, request_id, focus_URI, from, endpoint_GUID
72 #define SIPE_SEND_CONF_ADD_USER \
73 "<?xml version=\"1.0\"?>"\
74 "<request xmlns=\"urn:ietf:params:xml:ns:cccp\" xmlns:mscp=\"http://schemas.microsoft.com/rtc/2005/08/cccpextensions\" "\
75 "C3PVersion=\"1\" "\
76 "to=\"%s\" "\
77 "from=\"%s\" "\
78 "requestId=\"%d\">"\
79 "<addUser>"\
80 "<conferenceKeys confEntity=\"%s\"/>"\
81 "<ci:user xmlns:ci=\"urn:ietf:params:xml:ns:conference-info\" entity=\"%s\">"\
82 "<ci:roles>"\
83 "<ci:entry>attendee</ci:entry>"\
84 "</ci:roles>"\
85 "<ci:endpoint entity=\"{%s}\" xmlns:msci=\"http://schemas.microsoft.com/rtc/2005/08/confinfoextensions\"/>"\
86 "</ci:user>"\
87 "</addUser>"\
88 "</request>"
90 /**
91 * ModifyUserRoles request to Focus. Makes user a leader.
92 * @param focus_uri (%s)
93 * @param from (%s)
94 * @param request_id (%d)
95 * @param focus_uri (%s)
96 * @param who (%s)
98 #define SIPE_SEND_CONF_MODIFY_USER_ROLES \
99 "<?xml version=\"1.0\"?>"\
100 "<request xmlns=\"urn:ietf:params:xml:ns:cccp\" xmlns:mscp=\"http://schemas.microsoft.com/rtc/2005/08/cccpextensions\" "\
101 "C3PVersion=\"1\" "\
102 "to=\"%s\" "\
103 "from=\"%s\" "\
104 "requestId=\"%d\">"\
105 "<modifyUserRoles>"\
106 "<userKeys confEntity=\"%s\" userEntity=\"%s\"/>"\
107 "<user-roles xmlns=\"urn:ietf:params:xml:ns:conference-info\">"\
108 "<entry>presenter</entry>"\
109 "</user-roles>"\
110 "</modifyUserRoles>"\
111 "</request>"
114 * ModifyConferenceLock request to Focus. Locks/unlocks conference.
115 * @param focus_uri (%s)
116 * @param from (%s)
117 * @param request_id (%d)
118 * @param focus_uri (%s)
119 * @param locked (%s) "true" or "false" values applicable
121 #define SIPE_SEND_CONF_MODIFY_CONF_LOCK \
122 "<?xml version=\"1.0\"?>"\
123 "<request xmlns=\"urn:ietf:params:xml:ns:cccp\" xmlns:mscp=\"http://schemas.microsoft.com/rtc/2005/08/cccpextensions\" "\
124 "C3PVersion=\"1\" "\
125 "to=\"%s\" "\
126 "from=\"%s\" "\
127 "requestId=\"%d\">"\
128 "<modifyConferenceLock>"\
129 "<conferenceKeys confEntity=\"%s\"/>"\
130 "<locked>%s</locked>"\
131 "</modifyConferenceLock>"\
132 "</request>"
135 * ModifyConferenceLock request to Focus. Locks/unlocks conference.
136 * @param focus_uri (%s)
137 * @param from (%s)
138 * @param request_id (%d)
139 * @param focus_uri (%s)
140 * @param who (%s)
142 #define SIPE_SEND_CONF_DELETE_USER \
143 "<?xml version=\"1.0\"?>"\
144 "<request xmlns=\"urn:ietf:params:xml:ns:cccp\" xmlns:mscp=\"http://schemas.microsoft.com/rtc/2005/08/cccpextensions\" "\
145 "C3PVersion=\"1\" "\
146 "to=\"%s\" "\
147 "from=\"%s\" "\
148 "requestId=\"%d\">"\
149 "<deleteUser>"\
150 "<userKeys confEntity=\"%s\" userEntity=\"%s\"/>"\
151 "</deleteUser>"\
152 "</request>"
155 * Invite counterparty to join conference.
156 * @param focus_uri (%s)
157 * @param subject (%s) of conference
159 #define SIPE_SEND_CONF_INVITE \
160 "<Conferencing version=\"2.0\">"\
161 "<focus-uri>%s</focus-uri>"\
162 "<subject>%s</subject>"\
163 "<im available=\"true\">"\
164 "<first-im/>"\
165 "</im>"\
166 "</Conferencing>"
169 * Generates random GUID.
170 * This method is borrowed from pidgin's msnutils.c
172 static char *
173 rand_guid()
175 return g_strdup_printf("%4X%4X-%4X-%4X-%4X-%4X%4X%4X",
176 rand() % 0xAAFF + 0x1111,
177 rand() % 0xAAFF + 0x1111,
178 rand() % 0xAAFF + 0x1111,
179 rand() % 0xAAFF + 0x1111,
180 rand() % 0xAAFF + 0x1111,
181 rand() % 0xAAFF + 0x1111,
182 rand() % 0xAAFF + 0x1111,
183 rand() % 0xAAFF + 0x1111);
187 * @param expires not respected if set to negative value (E.g. -1)
189 static void
190 sipe_subscribe_conference(struct sipe_account_data *sip,
191 struct sip_session *session,
192 const int expires)
194 gchar *expires_hdr = (expires >= 0) ? g_strdup_printf("Expires: %d\r\n", expires) : g_strdup("");
195 gchar *contact = get_contact(sip);
196 gchar *hdr = g_strdup_printf(
197 "Event: conference\r\n"
198 "%s"
199 "Accept: application/conference-info+xml\r\n"
200 "Supported: com.microsoft.autoextend\r\n"
201 "Supported: ms-benotify\r\n"
202 "Proxy-Require: ms-benotify\r\n"
203 "Supported: ms-piggyback-first-notify\r\n"
204 "Contact: %s\r\n",
205 expires_hdr,
206 contact);
207 g_free(expires_hdr);
208 g_free(contact);
210 send_sip_request(sip->gc,
211 "SUBSCRIBE",
212 session->focus_uri,
213 session->focus_uri,
214 hdr,
216 NULL,
217 process_subscribe_response);
218 g_free(hdr);
221 /** Invite us to the focus callback */
222 static gboolean
223 process_invite_conf_focus_response(struct sipe_account_data *sip,
224 struct sipmsg *msg,
225 struct transaction *trans)
227 struct sip_session *session = NULL;
228 char *focus_uri = parse_from(sipmsg_find_header(msg, "To"));
230 session = sipe_session_find_conference(sip, focus_uri);
232 if (!session) {
233 purple_debug_info("sipe", "process_invite_conf_focus_response: unable to find conf session with focus=%s\n", focus_uri);
234 g_free(focus_uri);
235 return FALSE;
238 if (!session->focus_dialog) {
239 purple_debug_info("sipe", "process_invite_conf_focus_response: session's focus_dialog is NULL\n");
240 g_free(focus_uri);
241 return FALSE;
244 sipe_dialog_parse(session->focus_dialog, msg, TRUE);
246 if (msg->response >= 200) {
247 /* send ACK to focus */
248 session->focus_dialog->cseq = 0;
249 send_sip_request(sip->gc, "ACK", session->focus_dialog->with, session->focus_dialog->with, NULL, NULL, session->focus_dialog, NULL);
250 session->focus_dialog->outgoing_invite = NULL;
251 session->focus_dialog->is_established = TRUE;
254 if (msg->response >= 400) {
255 purple_debug_info("sipe", "process_invite_conf_focus_response: INVITE response is not 200. Failed to join focus.\n");
256 /* @TODO notify user of failure to join focus */
257 sipe_session_remove(sip, session);
258 g_free(focus_uri);
259 return FALSE;
260 } else if (msg->response == 200) {
261 xmlnode *xn_response = xmlnode_from_str(msg->body, msg->bodylen);
262 const gchar *code = xmlnode_get_attrib(xn_response, "code");
263 if (!strcmp(code, "success")) {
264 /* subscribe to focus */
265 sipe_subscribe_conference(sip, session, -1);
267 xmlnode_free(xn_response);
270 g_free(focus_uri);
271 return TRUE;
274 /** Invite us to the focus */
275 static void
276 sipe_invite_conf_focus(struct sipe_account_data *sip,
277 struct sip_session *session)
279 gchar *hdr;
280 gchar *contact;
281 gchar *body;
282 gchar *self;
284 if (session->focus_dialog && session->focus_dialog->is_established) {
285 purple_debug_info("sipe", "session with %s already has a dialog open\n", session->focus_uri);
286 return;
289 if(!session->focus_dialog) {
290 session->focus_dialog = g_new0(struct sip_dialog, 1);
291 session->focus_dialog->callid = gencallid();
292 session->focus_dialog->with = g_strdup(session->focus_uri);
293 session->focus_dialog->endpoint_GUID = rand_guid();
295 if (!(session->focus_dialog->ourtag)) {
296 session->focus_dialog->ourtag = gentag();
299 contact = get_contact(sip);
300 hdr = g_strdup_printf(
301 "Supported: ms-sender\r\n"
302 "Contact: %s\r\n"
303 "Content-Type: application/cccp+xml\r\n",
304 contact);
305 g_free(contact);
307 /* @TODO put request_id to queue to further compare with incoming one */
308 /* focus_URI, from, request_id, focus_URI, from, endpoint_GUID */
309 self = sip_uri_self(sip);
310 body = g_strdup_printf(
311 SIPE_SEND_CONF_ADD_USER,
312 session->focus_dialog->with,
313 self,
314 session->request_id++,
315 session->focus_dialog->with,
316 self,
317 session->focus_dialog->endpoint_GUID);
318 g_free(self);
320 session->focus_dialog->outgoing_invite = send_sip_request(sip->gc,
321 "INVITE",
322 session->focus_dialog->with,
323 session->focus_dialog->with,
324 hdr,
325 body,
326 session->focus_dialog,
327 process_invite_conf_focus_response);
328 g_free(body);
329 g_free(hdr);
332 /** Modify User Role */
333 void
334 sipe_conf_modify_user_role(struct sipe_account_data *sip,
335 struct sip_session *session,
336 const gchar* who)
338 gchar *hdr;
339 gchar *body;
340 gchar *self;
342 if (!session->focus_dialog || !session->focus_dialog->is_established) {
343 purple_debug_info("sipe", "sipe_conf_modify_user_role: no dialog with focus, exiting.\n");
344 return;
347 hdr = g_strdup(
348 "Content-Type: application/cccp+xml\r\n");
350 /* @TODO put request_id to queue to further compare with incoming one */
351 self = sip_uri_self(sip);
352 body = g_strdup_printf(
353 SIPE_SEND_CONF_MODIFY_USER_ROLES,
354 session->focus_dialog->with,
355 self,
356 session->request_id++,
357 session->focus_dialog->with,
358 who);
359 g_free(self);
361 send_sip_request(sip->gc,
362 "INFO",
363 session->focus_dialog->with,
364 session->focus_dialog->with,
365 hdr,
366 body,
367 session->focus_dialog,
368 NULL);
369 g_free(body);
370 g_free(hdr);
373 /** Modify Conference Lock */
374 void
375 sipe_conf_modify_conference_lock(struct sipe_account_data *sip,
376 struct sip_session *session,
377 const gboolean locked)
379 gchar *hdr;
380 gchar *body;
381 gchar *self;
383 if (!session->focus_dialog || !session->focus_dialog->is_established) {
384 purple_debug_info("sipe", "sipe_conf_modify_conference_lock: no dialog with focus, exiting.\n");
385 return;
388 hdr = g_strdup(
389 "Content-Type: application/cccp+xml\r\n");
391 /* @TODO put request_id to queue to further compare with incoming one */
392 self = sip_uri_self(sip);
393 body = g_strdup_printf(
394 SIPE_SEND_CONF_MODIFY_CONF_LOCK,
395 session->focus_dialog->with,
396 self,
397 session->request_id++,
398 session->focus_dialog->with,
399 locked ? "true" : "false");
400 g_free(self);
402 send_sip_request(sip->gc,
403 "INFO",
404 session->focus_dialog->with,
405 session->focus_dialog->with,
406 hdr,
407 body,
408 session->focus_dialog,
409 NULL);
410 g_free(body);
411 g_free(hdr);
414 /** Modify Delete User */
415 void
416 sipe_conf_delete_user(struct sipe_account_data *sip,
417 struct sip_session *session,
418 const gchar* who)
420 gchar *hdr;
421 gchar *body;
422 gchar *self;
424 if (!session->focus_dialog || !session->focus_dialog->is_established) {
425 purple_debug_info("sipe", "sipe_conf_delete_user: no dialog with focus, exiting.\n");
426 return;
429 hdr = g_strdup(
430 "Content-Type: application/cccp+xml\r\n");
432 /* @TODO put request_id to queue to further compare with incoming one */
433 self = sip_uri_self(sip);
434 body = g_strdup_printf(
435 SIPE_SEND_CONF_DELETE_USER,
436 session->focus_dialog->with,
437 self,
438 session->request_id++,
439 session->focus_dialog->with,
440 who);
441 g_free(self);
443 send_sip_request(sip->gc,
444 "INFO",
445 session->focus_dialog->with,
446 session->focus_dialog->with,
447 hdr,
448 body,
449 session->focus_dialog,
450 NULL);
451 g_free(body);
452 g_free(hdr);
455 /** Invite counterparty to join conference callback */
456 static gboolean
457 process_invite_conf_response(struct sipe_account_data *sip,
458 struct sipmsg *msg,
459 struct transaction *trans)
461 struct sip_dialog *dialog = g_new0(struct sip_dialog, 1);
463 dialog->callid = g_strdup(sipmsg_find_header(msg, "Call-ID"));
464 dialog->cseq = parse_cseq(sipmsg_find_header(msg, "CSeq"));
465 dialog->with = parse_from(sipmsg_find_header(msg, "To"));
466 sipe_dialog_parse(dialog, msg, TRUE);
468 if (msg->response >= 200) {
469 /* send ACK to counterparty */
470 dialog->cseq--;
471 send_sip_request(sip->gc, "ACK", dialog->with, dialog->with, NULL, NULL, dialog, NULL);
472 dialog->outgoing_invite = NULL;
473 dialog->is_established = TRUE;
476 if (msg->response >= 400) {
477 purple_debug_info("sipe", "process_invite_conf_response: INVITE response is not 200. Failed to invite %s.\n", dialog->with);
478 /* @TODO notify user of failure to invite counterparty */
479 sipe_dialog_free(dialog);
480 return FALSE;
482 if (msg->response >= 200) {
483 /* send BYE to counterparty */
484 send_sip_request(sip->gc, "BYE", dialog->with, dialog->with, NULL, NULL, dialog, NULL);
487 sipe_dialog_free(dialog);
488 return TRUE;
492 * Invites counterparty to join conference.
494 void
495 sipe_invite_conf(struct sipe_account_data *sip,
496 struct sip_session *session,
497 const gchar* who)
499 gchar *hdr;
500 gchar *contact;
501 gchar *body;
502 struct sip_dialog *dialog = NULL;
504 /* It will be short lived special dialog.
505 * Will not be stored in session.
507 dialog = g_new0(struct sip_dialog, 1);
508 dialog->callid = gencallid();
509 dialog->with = g_strdup(who);
510 dialog->ourtag = gentag();
512 contact = get_contact(sip);
513 hdr = g_strdup_printf(
514 "Supported: ms-sender\r\n"
515 "Contact: %s\r\n"
516 "Content-Type: application/ms-conf-invite+xml\r\n",
517 contact);
518 g_free(contact);
520 body = g_strdup_printf(
521 SIPE_SEND_CONF_INVITE,
522 session->focus_uri,
523 session->subject ? session->subject : ""
526 send_sip_request( sip->gc,
527 "INVITE",
528 dialog->with,
529 dialog->with,
530 hdr,
531 body,
532 dialog,
533 process_invite_conf_response);
535 sipe_dialog_free(dialog);
536 g_free(body);
537 g_free(hdr);
540 /** Create conference callback */
541 static gboolean
542 process_conf_add_response(struct sipe_account_data *sip,
543 struct sipmsg *msg,
544 struct transaction *trans)
546 if (msg->response >= 400) {
547 purple_debug_info("sipe", "process_conf_add_response: SERVICE response is not 200. Failed to create conference.\n");
548 /* @TODO notify user of failure to create conference */
549 return FALSE;
551 if (msg->response == 200) {
552 xmlnode *xn_response = xmlnode_from_str(msg->body, msg->bodylen);
553 if (!strcmp("success", xmlnode_get_attrib(xn_response, "code")))
555 gchar *who = (gchar *)trans->payload;
556 struct sip_session *session;
557 xmlnode *xn_conference_info = xmlnode_get_descendant(xn_response, "addConference", "conference-info", NULL);
559 session = sipe_session_add_chat(sip);
560 session->is_multiparty = FALSE;
561 session->focus_uri = g_strdup(xmlnode_get_attrib(xn_conference_info, "entity"));
562 purple_debug_info("sipe", "process_conf_add_response: session->focus_uri=%s\n",
563 session->focus_uri ? session->focus_uri : "");
565 session->pending_invite_queue = slist_insert_unique_sorted(
566 session->pending_invite_queue, g_strdup(who), (GCompareFunc)strcmp);
568 /* add self to conf */
569 sipe_invite_conf_focus(sip, session);
571 xmlnode_free(xn_response);
574 return TRUE;
578 * Creates conference.
580 void
581 sipe_conf_add(struct sipe_account_data *sip,
582 const gchar* who)
584 gchar *hdr;
585 gchar *conference_id;
586 gchar *contact;
587 gchar *body;
588 gchar *self;
589 struct transaction * tr;
590 struct sip_dialog *dialog = NULL;
591 time_t expiry = time(NULL) + 7*60*60; /* 7 hours */
592 const char *expiry_time;
594 contact = get_contact(sip);
595 hdr = g_strdup_printf(
596 "Supported: ms-sender\r\n"
597 "Contact: %s\r\n"
598 "Content-Type: application/cccp+xml\r\n",
599 contact);
600 g_free(contact);
602 expiry_time = purple_utf8_strftime("%Y-%m-%dT%H:%M:%SZ", gmtime(&expiry));
603 self = sip_uri_self(sip);
604 conference_id = genconfid();
605 body = g_strdup_printf(
606 SIPE_SEND_CONF_ADD,
607 sip->focus_factory_uri,
608 self,
609 rand(),
610 conference_id,
611 expiry_time);
612 g_free(conference_id);
613 g_free(self);
615 tr = send_sip_request( sip->gc,
616 "SERVICE",
617 sip->focus_factory_uri,
618 sip->focus_factory_uri,
619 hdr,
620 body,
621 NULL,
622 process_conf_add_response);
623 tr->payload = g_strdup(who);
625 sipe_dialog_free(dialog);
626 g_free(body);
627 g_free(hdr);
630 void
631 process_incoming_invite_conf(struct sipe_account_data *sip,
632 struct sipmsg *msg)
634 struct sip_session *session = NULL;
635 struct sip_dialog *dialog = NULL;
636 xmlnode *xn_conferencing = xmlnode_from_str(msg->body, msg->bodylen);
637 xmlnode *xn_focus_uri = xmlnode_get_child(xn_conferencing, "focus-uri");
638 char *focus_uri = xmlnode_get_data(xn_focus_uri);
640 xmlnode_free(xn_conferencing);
642 /* send OK */
643 purple_debug_info("sipe", "We have received invitation to Conference. Focus URI=%s\n", focus_uri);
644 send_sip_response(sip->gc, msg, 200, "OK", NULL);
646 session = sipe_session_add_chat(sip);
647 session->focus_uri = focus_uri;
648 session->is_multiparty = FALSE;
650 /* temporary dialog with invitor */
651 dialog = g_new0(struct sip_dialog, 1);
652 dialog->callid = g_strdup(sipmsg_find_header(msg, "Call-ID"));
653 dialog->cseq = parse_cseq(sipmsg_find_header(msg, "CSeq"));
654 dialog->with = parse_from(sipmsg_find_header(msg, "From"));
655 sipe_dialog_parse(dialog, msg, FALSE);
657 /* send BYE to invitor */
658 send_sip_request(sip->gc, "BYE", dialog->with, dialog->with, NULL, NULL, dialog, NULL);
659 sipe_dialog_free(dialog);
661 /* add self to conf */
662 sipe_invite_conf_focus(sip, session);
665 void
666 sipe_process_conference(struct sipe_account_data *sip,
667 struct sipmsg *msg)
669 xmlnode *xn_conference_info;
670 xmlnode *node;
671 xmlnode *xn_subject;
672 const gchar *focus_uri;
673 struct sip_session *session;
674 gboolean just_joined = FALSE;
676 if (msg->response != 0 && msg->response != 200) return;
678 if (msg->bodylen == 0 || msg->body == NULL || strcmp(sipmsg_find_header(msg, "Event"), "conference")) return;
680 xn_conference_info = xmlnode_from_str(msg->body, msg->bodylen);
681 if (!xn_conference_info) return;
683 focus_uri = xmlnode_get_attrib(xn_conference_info, "entity");
684 session = sipe_session_find_conference(sip, focus_uri);
686 if (!session) {
687 purple_debug_info("sipe", "sipe_process_conference: unable to find conf session with focus=%s\n", focus_uri);
688 return;
691 if (session->focus_uri && !session->conv) {
692 gchar *chat_name = g_strdup_printf(_("Chat #%d"), ++sip->chat_seq);
693 gchar *self = sip_uri_self(sip);
694 /* create prpl chat */
695 session->conv = serv_got_joined_chat(sip->gc, session->chat_id, chat_name);
696 session->chat_name = chat_name;
697 purple_conv_chat_set_nick(PURPLE_CONV_CHAT(session->conv), self);
698 just_joined = TRUE;
699 /* @TODO ask for full state (re-subscribe) if it was a partial one -
700 * this is to obtain full list of conference participants.
702 g_free(self);
705 /* subject */
706 if ((xn_subject = xmlnode_get_descendant(xn_conference_info, "conference-description", "subject", NULL))) {
707 g_free(session->subject);
708 session->subject = xmlnode_get_data(xn_subject);
709 purple_conv_chat_set_topic(PURPLE_CONV_CHAT(session->conv), NULL, session->subject);
710 purple_debug_info("sipe", "sipe_process_conference: subject=%s\n", session->subject ? session->subject : "");
713 /* IM MCU URI */
714 if (!session->im_mcu_uri) {
715 for (node = xmlnode_get_descendant(xn_conference_info, "conference-description", "conf-uris", "entry", NULL);
716 node;
717 node = xmlnode_get_next_twin(node))
719 gchar *purpose = xmlnode_get_data(xmlnode_get_child(node, "purpose"));
721 if (purpose && !strcmp("chat", purpose)) {
722 g_free(purpose);
723 session->im_mcu_uri = xmlnode_get_data(xmlnode_get_child(node, "uri"));
724 purple_debug_info("sipe", "sipe_process_conference: im_mcu_uri=%s\n", session->im_mcu_uri);
725 break;
727 g_free(purpose);
731 /* users */
732 for (node = xmlnode_get_descendant(xn_conference_info, "users", "user", NULL); node; node = xmlnode_get_next_twin(node)) {
733 xmlnode *endpoint = NULL;
734 const gchar *user_uri = xmlnode_get_attrib(node, "entity");
735 const gchar *state = xmlnode_get_attrib(node, "state");
736 gchar *role = xmlnode_get_data(xmlnode_get_descendant(node, "roles", "entry", NULL));
737 PurpleConvChatBuddyFlags flags = PURPLE_CBFLAGS_NONE;
738 PurpleConvChat *chat = PURPLE_CONV_CHAT(session->conv);
739 gboolean is_in_im_mcu = FALSE;
740 gchar *self = sip_uri_self(sip);
742 if (role && !strcmp(role, "presenter")) {
743 flags |= PURPLE_CBFLAGS_OP;
746 if (!strcmp("deleted", state)) {
747 if (purple_conv_chat_find_user(chat, user_uri)) {
748 purple_conv_chat_remove_user(chat, user_uri, NULL /* reason */);
750 } else {
751 /* endpoints */
752 for (endpoint = xmlnode_get_child(node, "endpoint"); endpoint; endpoint = xmlnode_get_next_twin(endpoint)) {
753 if (!strcmp("chat", xmlnode_get_attrib(endpoint, "session-type"))) {
754 gchar *status = xmlnode_get_data(xmlnode_get_child(endpoint, "status"));
755 if (!strcmp("connected", status)) {
756 is_in_im_mcu = TRUE;
757 if (!purple_conv_chat_find_user(chat, user_uri)) {
758 purple_conv_chat_add_user(chat, user_uri, NULL, flags,
759 !just_joined && g_strcasecmp(user_uri, self));
760 } else {
761 purple_conv_chat_user_set_flags(chat, user_uri, flags);
764 g_free(status);
765 break;
768 if (!is_in_im_mcu) {
769 if (purple_conv_chat_find_user(chat, user_uri)) {
770 purple_conv_chat_remove_user(chat, user_uri, NULL /* reason */);
774 g_free(role);
775 g_free(self);
778 /* entity-view, locked */
779 for (node = xmlnode_get_descendant(xn_conference_info, "conference-view", "entity-view", NULL);
780 node;
781 node = xmlnode_get_next_twin(node)) {
783 xmlnode *xn_type = xmlnode_get_descendant(node, "entity-state", "media", "entry", "type", NULL);
784 gchar *tmp;
785 if (xn_type && !strcmp("chat", (tmp = xmlnode_get_data(xn_type)))) {
786 xmlnode *xn_locked = xmlnode_get_descendant(node, "entity-state", "locked", NULL);
787 if (xn_locked) {
788 gchar *locked = xmlnode_get_data(xn_locked);
789 gboolean prev_locked = session->locked;
790 session->locked = (locked && !strcmp(locked, "true")) ? TRUE : FALSE;
791 if (prev_locked && !session->locked) {
792 sipe_present_info(sip, session,
793 _("This conference is no longer locked. Additional participants can now join."));
795 if (!prev_locked && session->locked) {
796 sipe_present_info(sip, session,
797 _("This conference is locked. Nobody else can join the conference while it is locked."));
800 purple_debug_info("sipe", "sipe_process_conference: session->locked=%s\n",
801 session->locked ? "TRUE" : "FALSE");
802 g_free(locked);
804 g_free(tmp);
807 xmlnode_free(xn_conference_info);
809 if (session->im_mcu_uri) {
810 struct sip_dialog *dialog = sipe_dialog_find(session, session->im_mcu_uri);
811 if (!dialog) {
812 dialog = sipe_dialog_add(session);
814 dialog->callid = g_strdup(session->callid);
815 dialog->with = g_strdup(session->im_mcu_uri);
817 /* send INVITE to IM MCU */
818 sipe_invite(sip, session, dialog->with, NULL, NULL, FALSE);
822 sipe_process_pending_invite_queue(sip, session);
825 void
826 sipe_conf_immcu_closed(struct sipe_account_data *sip,
827 struct sip_session *session)
829 sipe_present_info(sip, session,
830 _("You have been disconnected from this conference."));
831 purple_conv_chat_clear_users(PURPLE_CONV_CHAT(session->conv));
834 void
835 conf_session_close(struct sipe_account_data *sip,
836 struct sip_session *session)
838 if (session) {
839 /* unsubscribe from focus */
840 sipe_subscribe_conference(sip, session, 0);
842 if (session->focus_dialog) {
843 /* send BYE to focus */
844 send_sip_request(sip->gc,
845 "BYE",
846 session->focus_dialog->with,
847 session->focus_dialog->with,
848 NULL,
849 NULL,
850 session->focus_dialog,
851 NULL);
856 void
857 sipe_process_imdn(struct sipe_account_data *sip,
858 struct sipmsg *msg)
860 gchar *call_id = sipmsg_find_header(msg, "Call-ID");
861 static struct sip_session *session;
862 xmlnode *xn_imdn;
863 xmlnode *node;
864 gchar *message_id;
865 gchar *message;
867 session = sipe_session_find_chat_by_callid(sip, call_id);
869 xn_imdn = xmlnode_from_str(msg->body, msg->bodylen);
870 message_id = xmlnode_get_data(xmlnode_get_child(xn_imdn, "message-id"));
872 message = g_hash_table_lookup(session->conf_unconfirmed_messages, message_id);
874 /* recipient */
875 for (node = xmlnode_get_child(xn_imdn, "recipient"); node; node = xmlnode_get_next_twin(node)) {
876 gchar *tmp = parse_from(xmlnode_get_attrib(node, "uri"));
877 gchar *uri = parse_from(tmp);
878 sipe_present_message_undelivered_err(sip, session, uri, message);
879 g_free(tmp);
880 g_free(uri);
883 xmlnode_free(xn_imdn);
885 g_hash_table_remove(session->conf_unconfirmed_messages, message_id);
886 purple_debug_info("sipe", "sipe_process_imdn: removed message %s from conf_unconfirmed_messages(count=%d)\n",
887 message_id, g_hash_table_size(session->conf_unconfirmed_messages));
888 g_free(message_id);
893 Local Variables:
894 mode: c
895 c-file-style: "bsd"
896 indent-tabs-mode: t
897 tab-width: 8
898 End: