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
34 #include "sipe-conf.h"
35 #include "sipe-dialog.h"
37 #include "sipe-utils.h"
40 * AddUser request to Focus.
42 * focus_URI, from, request_id, focus_URI, from, endpoint_GUID
44 #define SIPE_SEND_CONF_ADD_USER \
45 "<?xml version=\"1.0\"?>"\
46 "<request xmlns=\"urn:ietf:params:xml:ns:cccp\" xmlns:mscp=\"http://schemas.microsoft.com/rtc/2005/08/cccpextensions\" "\
52 "<conferenceKeys confEntity=\"%s\"/>"\
53 "<ci:user xmlns:ci=\"urn:ietf:params:xml:ns:conference-info\" entity=\"%s\">"\
55 "<ci:entry>attendee</ci:entry>"\
57 "<ci:endpoint entity=\"{%s}\" xmlns:msci=\"http://schemas.microsoft.com/rtc/2005/08/confinfoextensions\"/>"\
62 static struct sip_im_session
*
63 find_conf_session(struct sipe_account_data
*sip
,
64 const char *focus_uri
)
66 struct sip_im_session
*session
;
68 if (sip
== NULL
|| focus_uri
== NULL
) {
72 entry
= sip
->im_sessions
;
74 session
= entry
->data
;
75 if (session
->focus_uri
&& !g_strcasecmp(focus_uri
, session
->focus_uri
)) {
84 * Generates random GUID.
85 * This method is borrowed from pidgin's msnutils.c
90 return g_strdup_printf("%4X%4X-%4X-%4X-%4X-%4X%4X%4X",
91 rand() % 0xAAFF + 0x1111,
92 rand() % 0xAAFF + 0x1111,
93 rand() % 0xAAFF + 0x1111,
94 rand() % 0xAAFF + 0x1111,
95 rand() % 0xAAFF + 0x1111,
96 rand() % 0xAAFF + 0x1111,
97 rand() % 0xAAFF + 0x1111,
98 rand() % 0xAAFF + 0x1111);
102 sipe_subscribe_conference(struct sipe_account_data
*sip
,
103 struct sip_im_session
*session
)
105 gchar
*contact
= get_contact(sip
);
106 gchar
*hdr
= g_strdup_printf(
107 "Event: conference\r\n"
108 "Accept: application/conference-info+xml\r\n"
109 "Supported: com.microsoft.autoextend\r\n"
110 "Supported: ms-benotify\r\n"
111 "Proxy-Require: ms-benotify\r\n"
112 "Supported: ms-piggyback-first-notify\r\n"
117 send_sip_request(sip
->gc
,
124 process_subscribe_response
);
128 /** Invite us to the focus callback */
130 process_invite_conf_focus_response(struct sipe_account_data
*sip
,
132 struct transaction
*trans
)
134 struct sip_im_session
* session
= NULL
;
135 char *focus_uri
= parse_from(sipmsg_find_header(msg
, "To"));
137 session
= find_conf_session(sip
, focus_uri
);
140 purple_debug_info("sipe", "process_invite_conf_focus_response: unable to find conf session with focus=%s\n", focus_uri
);
145 if (!session
->focus_dialog
) {
146 purple_debug_info("sipe", "process_invite_response: session's focus_dialog is NULL\n");
151 sipe_dialog_parse(session
->focus_dialog
, msg
, TRUE
);
153 if (msg
->response
>= 200) {
154 /* send ACK to focus */
155 session
->focus_dialog
->cseq
= 0;
156 send_sip_request(sip
->gc
, "ACK", session
->focus_dialog
->with
, session
->focus_dialog
->with
, NULL
, NULL
, session
->focus_dialog
, NULL
);
157 session
->focus_dialog
->outgoing_invite
= NULL
;
158 session
->focus_dialog
->is_established
= TRUE
;
161 if (msg
->response
>= 400) {
162 purple_debug_info("sipe", "process_invite_conf_focus_response: INVITE response is not 200. Failed to join focus.\n");
163 /* @TODO notify user of failure to join focus */
164 im_session_destroy(sip
, session
);
167 } else if (msg
->response
== 200) {
168 xmlnode
*xn_response
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
169 const gchar
*code
= xmlnode_get_attrib(xn_response
, "code");
170 if (!strcmp(code
, "success")) {
171 /* subscribe to focus */
172 sipe_subscribe_conference(sip
, session
);
174 xmlnode_free(xn_response
);
181 /** Invite us to the focus */
183 sipe_invite_conf_focus(struct sipe_account_data
*sip
,
184 struct sip_im_session
*session
)
191 if (session
->focus_dialog
&& session
->focus_dialog
->is_established
) {
192 purple_debug_info("sipe", "session with %s already has a dialog open\n", session
->focus_uri
);
196 if(!session
->focus_dialog
) {
197 session
->focus_dialog
= g_new0(struct sip_dialog
, 1);
198 session
->focus_dialog
->callid
= gencallid();
199 session
->focus_dialog
->with
= g_strdup(session
->focus_uri
);
200 session
->focus_dialog
->endpoint_GUID
= rand_guid();
202 if (!(session
->focus_dialog
->ourtag
)) {
203 session
->focus_dialog
->ourtag
= gentag();
206 contact
= get_contact(sip
);
207 hdr
= g_strdup_printf(
208 "Supported: ms-sender\r\n"
210 "Content-Type: application/cccp+xml\r\n",
214 /* @TODO put request_id to queue to further compare with incoming one */
215 /* focus_URI, from, request_id, focus_URI, from, endpoint_GUID */
216 self
= g_strdup_printf("sip:%s", sip
->username
);
217 body
= g_strdup_printf(
218 SIPE_SEND_CONF_ADD_USER
,
219 session
->focus_dialog
->with
,
221 session
->request_id
++,
222 session
->focus_dialog
->with
,
224 session
->focus_dialog
->endpoint_GUID
);
227 session
->focus_dialog
->outgoing_invite
= send_sip_request(sip
->gc
,
229 session
->focus_dialog
->with
,
230 session
->focus_dialog
->with
,
233 session
->focus_dialog
,
234 process_invite_conf_focus_response
);
239 void process_incoming_invite_conf(struct sipe_account_data
*sip
,
242 struct sip_im_session
*session
= NULL
;
243 struct sip_dialog
*dialog
= NULL
;
244 gchar
*from
= parse_from(sipmsg_find_header(msg
, "From"));
245 gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
246 xmlnode
*xn_conferencing
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
247 xmlnode
*xn_focus_uri
= xmlnode_get_child(xn_conferencing
, "focus-uri");
248 char *focus_uri
= xmlnode_get_data(xn_focus_uri
);
250 xmlnode_free(xn_conferencing
);
253 purple_debug_info("sipe", "We have received invitation to Conference. Focus URI=%s\n", focus_uri
);
254 send_sip_response(sip
->gc
, msg
, 200, "OK", NULL
);
256 session
= create_chat_session(sip
);
257 session
->focus_uri
= focus_uri
;
258 session
->is_multiparty
= FALSE
;
260 /* temporaty dialog with invitor */
261 dialog
= g_new0(struct sip_dialog
, 1);
262 dialog
->callid
= g_strdup(callid
);
264 sipe_dialog_parse(dialog
, msg
, FALSE
);
266 /* send BYE to invitor */
267 send_sip_request(sip
->gc
, "BYE", dialog
->with
, dialog
->with
, NULL
, NULL
, dialog
, NULL
);
268 sipe_dialog_free(dialog
);
271 sipe_invite_conf_focus(sip
, session
);
274 void sipe_process_conference(struct sipe_account_data
*sip
,
277 xmlnode
*xn_conference_info
;
279 const gchar
*focus_uri
;
280 struct sip_im_session
*session
;
281 struct sip_dialog
*dialog
;
283 if (msg
->response
!= 0 && msg
->response
!= 200) return;
285 if (msg
->bodylen
== 0 || msg
->body
== NULL
|| strcmp(sipmsg_find_header(msg
, "Event"), "conference")) return;
287 xn_conference_info
= xmlnode_from_str(msg
->body
, msg
->bodylen
);
288 if (!xn_conference_info
) return;
290 focus_uri
= xmlnode_get_attrib(xn_conference_info
, "entity");
291 session
= find_conf_session(sip
, focus_uri
);
294 purple_debug_info("sipe", "sipe_process_conference: unable to find conf session with focus=%s\n", focus_uri
);
298 if (session
->focus_uri
&& !session
->conv
) {
299 gchar
*chat_name
= g_strdup_printf(_("Chat #%d"), ++sip
->chat_seq
);
300 /* create prpl chat */
301 session
->conv
= serv_got_joined_chat(sip
->gc
, session
->chat_id
, chat_name
);
302 session
->chat_name
= chat_name
;
303 /* @TODO ask for full state (re-subscribe) if it was a partial one -
304 * this is to obtain full list of conference participants.
309 if (!session
->im_mcu_uri
) {
310 for (node
= xmlnode_get_descendant(xn_conference_info
, "conference-description", "conf-uris", "entry", NULL
);
312 node
= xmlnode_get_next_twin(node
))
314 gchar
*purpose
= xmlnode_get_data(xmlnode_get_child(node
, "purpose"));
316 if (purpose
&& !strcmp("chat", purpose
)) {
318 session
->im_mcu_uri
= xmlnode_get_data(xmlnode_get_child(node
, "uri"));
319 purple_debug_info("sipe", "sipe_process_conference: im_mcu_uri=%s\n", session
->im_mcu_uri
);
327 for (node
= xmlnode_get_descendant(xn_conference_info
, "users", "user", NULL
); node
; node
= xmlnode_get_next_twin(node
)) {
328 xmlnode
*endpoint
= NULL
;
329 const gchar
*user_uri
= xmlnode_get_attrib(node
, "entity");
330 const gchar
*state
= xmlnode_get_attrib(node
, "state");
332 if (!strcmp("deleted", state
)) {
333 purple_conv_chat_remove_user(PURPLE_CONV_CHAT(session
->conv
),
334 user_uri
, NULL
/* reason */);
337 for (endpoint
= xmlnode_get_child(node
, "endpoint"); endpoint
; endpoint
= xmlnode_get_next_twin(endpoint
)) {
338 if (!strcmp("chat", xmlnode_get_attrib(endpoint
, "session-type"))) {
339 gchar
*status
= xmlnode_get_data(xmlnode_get_child(endpoint
, "status"));
340 if (!strcmp("connected", status
)) {
341 purple_conv_chat_add_user(PURPLE_CONV_CHAT(session
->conv
),
343 PURPLE_CBFLAGS_NONE
, FALSE
);
350 xmlnode_free(xn_conference_info
);
352 dialog
= sipe_dialog_find(session
, session
->im_mcu_uri
);
354 dialog
= sipe_dialog_add(session
);
356 dialog
->callid
= g_strdup(session
->callid
);
357 dialog
->with
= g_strdup(session
->im_mcu_uri
);
359 /* send INVITE to IMMCU */
360 sipe_invite(sip
, session
, dialog
->with
, NULL
, NULL
, FALSE
);