Refactor SIP session dialog API
[siplcs.git] / src / sipe-conf.c
blobd545d1f42e2f4c1e9f29c155828b988ab92dcfe1
1 /**
2 * @file sipe-conf.c
4 * pidgin-sipe
5 *
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 #ifdef _WIN32
30 #include "internal.h"
31 #endif
33 #include "sipe.h"
34 #include "sipe-conf.h"
35 #include "sipe-dialog.h"
36 #include "sipe-nls.h"
37 #include "sipe-utils.h"
39 /**
40 * AddUser request to Focus.
41 * Params:
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\" "\
47 "C3PVersion=\"1\" "\
48 "to=\"%s\" "\
49 "from=\"%s\" "\
50 "requestId=\"%d\">"\
51 "<addUser>"\
52 "<conferenceKeys confEntity=\"%s\"/>"\
53 "<ci:user xmlns:ci=\"urn:ietf:params:xml:ns:conference-info\" entity=\"%s\">"\
54 "<ci:roles>"\
55 "<ci:entry>attendee</ci:entry>"\
56 "</ci:roles>"\
57 "<ci:endpoint entity=\"{%s}\" xmlns:msci=\"http://schemas.microsoft.com/rtc/2005/08/confinfoextensions\"/>"\
58 "</ci:user>"\
59 "</addUser>"\
60 "</request>"
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;
67 GSList *entry;
68 if (sip == NULL || focus_uri == NULL) {
69 return NULL;
72 entry = sip->im_sessions;
73 while (entry) {
74 session = entry->data;
75 if (session->focus_uri && !g_strcasecmp(focus_uri, session->focus_uri)) {
76 return session;
78 entry = entry->next;
80 return NULL;
83 /**
84 * Generates random GUID.
85 * This method is borrowed from pidgin's msnutils.c
87 static char *
88 rand_guid()
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);
101 static void
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"
113 "Contact: %s\r\n",
114 contact);
115 g_free(contact);
117 send_sip_request(sip->gc,
118 "SUBSCRIBE",
119 session->focus_uri,
120 session->focus_uri,
121 hdr,
123 NULL,
124 process_subscribe_response);
125 g_free(hdr);
128 /** Invite us to the focus callback */
129 static gboolean
130 process_invite_conf_focus_response(struct sipe_account_data *sip,
131 struct sipmsg *msg,
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);
139 if (!session) {
140 purple_debug_info("sipe", "process_invite_conf_focus_response: unable to find conf session with focus=%s\n", focus_uri);
141 g_free(focus_uri);
142 return FALSE;
145 if (!session->focus_dialog) {
146 purple_debug_info("sipe", "process_invite_response: session's focus_dialog is NULL\n");
147 g_free(focus_uri);
148 return FALSE;
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);
165 g_free(focus_uri);
166 return FALSE;
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);
177 g_free(focus_uri);
178 return TRUE;
181 /** Invite us to the focus */
182 static void
183 sipe_invite_conf_focus(struct sipe_account_data *sip,
184 struct sip_im_session *session)
186 gchar *hdr;
187 gchar *contact;
188 gchar *body;
189 gchar *self;
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);
193 return;
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"
209 "Contact: %s\r\n"
210 "Content-Type: application/cccp+xml\r\n",
211 contact);
212 g_free(contact);
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,
220 self,
221 session->request_id++,
222 session->focus_dialog->with,
223 self,
224 session->focus_dialog->endpoint_GUID);
225 g_free(self);
227 session->focus_dialog->outgoing_invite = send_sip_request(sip->gc,
228 "INVITE",
229 session->focus_dialog->with,
230 session->focus_dialog->with,
231 hdr,
232 body,
233 session->focus_dialog,
234 process_invite_conf_focus_response);
235 g_free(body);
236 g_free(hdr);
239 void process_incoming_invite_conf(struct sipe_account_data *sip,
240 struct sipmsg *msg)
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);
252 /* send OK */
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);
263 dialog->with = from;
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);
270 //add self to conf
271 sipe_invite_conf_focus(sip, session);
274 void sipe_process_conference(struct sipe_account_data *sip,
275 struct sipmsg *msg)
277 xmlnode *xn_conference_info;
278 xmlnode *node;
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);
293 if (!session) {
294 purple_debug_info("sipe", "sipe_process_conference: unable to find conf session with focus=%s\n", focus_uri);
295 return;
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.
308 /* IMMCU URI */
309 if (!session->im_mcu_uri) {
310 for (node = xmlnode_get_descendant(xn_conference_info, "conference-description", "conf-uris", "entry", NULL);
311 node;
312 node = xmlnode_get_next_twin(node))
314 gchar *purpose = xmlnode_get_data(xmlnode_get_child(node, "purpose"));
316 if (purpose && !strcmp("chat", purpose)) {
317 g_free(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);
320 break;
322 g_free(purpose);
326 /* users */
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 */);
335 } else {
336 /* endpoints */
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),
342 user_uri, NULL,
343 PURPLE_CBFLAGS_NONE, FALSE);
345 break;
350 xmlnode_free(xn_conference_info);
352 dialog = sipe_dialog_find(session, session->im_mcu_uri);
353 if (!dialog) {
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);
365 Local Variables:
366 mode: c
367 c-file-style: "bsd"
368 indent-tabs-mode: t
369 tab-width: 8
370 End: