core cleanup: start cleanup up status handling
[siplcs.git] / src / core / sipe-subscriptions.c
blob6355365510abd5b542c5faf5fe42de43f792f772
1 /**
2 * @file sipe-subscriptions.c
4 * pidgin-sipe
6 * Copyright (C) 2010-11 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 #include <stdlib.h>
25 #include <glib.h>
27 #include "sipe-common.h"
28 #include "sipmsg.h"
29 #include "sip-transport.h"
30 #include "sipe-backend.h"
31 #include "sipe-buddy.h"
32 #include "sipe-core.h"
33 #include "sipe-core-private.h"
34 #include "sipe-dialog.h"
35 #include "sipe-notify.h"
36 #include "sipe-schedule.h"
37 #include "sipe-subscriptions.h"
38 #include "sipe-utils.h"
40 /* RFC3265 subscription */
41 struct sip_subscription {
42 struct sip_dialog dialog;
43 gchar *event;
46 static void sipe_subscription_free(struct sip_subscription *subscription)
48 if (!subscription) return;
50 g_free(subscription->event);
51 /* NOTE: use cast to prevent BAD_FREE warning from Coverity */
52 sipe_dialog_free((struct sip_dialog *) subscription);
55 void sipe_subscriptions_init(struct sipe_core_private *sipe_private)
57 sipe_private->subscriptions = g_hash_table_new_full(g_str_hash,
58 g_str_equal,
59 g_free,
60 (GDestroyNotify)sipe_subscription_free);
63 static void sipe_unsubscribe_cb(SIPE_UNUSED_PARAMETER gpointer key,
64 gpointer value, gpointer user_data)
66 struct sip_subscription *subscription = value;
67 struct sip_dialog *dialog = &subscription->dialog;
68 struct sipe_core_private *sipe_private = user_data;
69 gchar *contact = get_contact(sipe_private);
70 gchar *hdr = g_strdup_printf(
71 "Event: %s\r\n"
72 "Expires: 0\r\n"
73 "Contact: %s\r\n", subscription->event, contact);
74 g_free(contact);
76 /* Rate limit to max. 25 requests per seconds */
77 g_usleep(1000000 / 25);
79 sip_transport_subscribe(sipe_private,
80 dialog->with,
81 hdr,
82 NULL,
83 dialog,
84 NULL);
86 g_free(hdr);
89 void sipe_subscriptions_unsubscribe(struct sipe_core_private *sipe_private)
91 /* unsubscribe all */
92 g_hash_table_foreach(sipe_private->subscriptions,
93 sipe_unsubscribe_cb,
94 sipe_private);
98 void sipe_subscriptions_destroy(struct sipe_core_private *sipe_private)
100 g_hash_table_destroy(sipe_private->subscriptions);
103 void sipe_subscriptions_remove(struct sipe_core_private *sipe_private,
104 const gchar *key)
106 if (g_hash_table_lookup(sipe_private->subscriptions, key)) {
107 g_hash_table_remove(sipe_private->subscriptions, key);
108 SIPE_DEBUG_INFO("sipe_subscriptions_remove: %s", key);
112 static gboolean process_subscribe_response(struct sipe_core_private *sipe_private,
113 struct sipmsg *msg,
114 struct transaction *trans)
116 gchar *with = parse_from(sipmsg_find_header(msg, "To"));
117 const gchar *event = sipmsg_find_header(msg, "Event");
118 gchar *key;
120 /* The case with 2005 Public IM Connectivity (PIC) - no Event header */
121 if (!event) {
122 struct sipmsg *request_msg = trans->msg;
123 event = sipmsg_find_header(request_msg, "Event");
126 key = sipe_utils_subscription_key(event, with);
128 /* 200 OK; 481 Call Leg Does Not Exist */
129 if (key && (msg->response == 200 || msg->response == 481)) {
130 sipe_subscriptions_remove(sipe_private, key);
133 /* create/store subscription dialog if not yet */
134 if (key && (msg->response == 200)) {
135 struct sip_subscription *subscription = g_new0(struct sip_subscription, 1);
136 g_hash_table_insert(sipe_private->subscriptions,
137 g_strdup(key),
138 subscription);
140 subscription->dialog.callid = g_strdup(sipmsg_find_header(msg, "Call-ID"));
141 subscription->dialog.cseq = sipmsg_parse_cseq(msg);
142 subscription->dialog.with = g_strdup(with);
143 subscription->event = g_strdup(event);
144 sipe_dialog_parse(&subscription->dialog, msg, TRUE);
146 SIPE_DEBUG_INFO("process_subscribe_response: subscription dialog added for: %s", key);
149 g_free(key);
150 g_free(with);
152 if (sipmsg_find_header(msg, "ms-piggyback-cseq"))
154 process_incoming_notify(sipe_private, msg, FALSE, FALSE);
156 return TRUE;
160 * common subscription code
162 void sipe_subscribe(struct sipe_core_private *sipe_private,
163 const gchar *uri,
164 const gchar *event,
165 const gchar *accept,
166 const gchar *addheaders,
167 const gchar *body,
168 struct sip_dialog *dialog)
170 gchar *contact = get_contact(sipe_private);
171 gchar *hdr = g_strdup_printf(
172 "Event: %s\r\n"
173 "Accept: %s\r\n"
174 "Supported: com.microsoft.autoextend\r\n"
175 "Supported: ms-benotify\r\n"
176 "Proxy-Require: ms-benotify\r\n"
177 "Supported: ms-piggyback-first-notify\r\n"
178 "%s"
179 "Contact: %s\r\n",
180 event,
181 accept,
182 addheaders ? addheaders : "",
183 contact);
184 g_free(contact);
187 sip_transport_subscribe(sipe_private,
188 uri,
189 hdr,
190 body,
191 dialog,
192 process_subscribe_response);
194 g_free(hdr);
198 * common subscription code for self-subscriptions
200 static void sipe_subscribe_self(struct sipe_core_private *sipe_private,
201 const gchar *event,
202 const gchar *accept,
203 const gchar *addheaders,
204 const gchar *body,
205 struct sip_dialog *dialog)
207 gchar *self = sip_uri_self(sipe_private);
209 sipe_subscribe(sipe_private,
210 self,
211 event,
212 accept,
213 addheaders,
214 body,
215 dialog);
217 g_free(self);
220 static struct sip_dialog *sipe_subscribe_dialog(struct sipe_core_private *sipe_private,
221 const gchar *key)
223 struct sip_dialog *dialog = g_hash_table_lookup(sipe_private->subscriptions,
224 key);
225 SIPE_DEBUG_INFO("sipe_subscribe_dialog: dialog for '%s' is %s", key, dialog ? "not NULL" : "NULL");
226 return dialog;
229 static void sipe_subscribe_presence_buddy(struct sipe_core_private *sipe_private,
230 const gchar *uri,
231 const gchar *request,
232 const gchar *body)
234 gchar *key = sipe_utils_presence_key(uri);
236 sip_transport_subscribe(sipe_private,
237 uri,
238 request,
239 body,
240 sipe_subscribe_dialog(sipe_private, key),
241 process_subscribe_response);
243 g_free(key);
246 void sipe_subscribe_presence_wpending(struct sipe_core_private *sipe_private,
247 SIPE_UNUSED_PARAMETER void *unused)
249 gchar *key = sipe_utils_subscription_key("presence.wpending", NULL);
251 sipe_subscribe_self(sipe_private,
252 "presence.wpending",
253 "text/xml+msrtc.wpending",
254 NULL,
255 NULL,
256 sipe_subscribe_dialog(sipe_private, key));
258 g_free(key);
262 * Subscribe roaming ACL
264 void sipe_subscribe_roaming_acl(struct sipe_core_private *sipe_private)
266 sipe_subscribe_self(sipe_private,
267 "vnd-microsoft-roaming-ACL",
268 "application/vnd-microsoft-roaming-acls+xml",
269 NULL,
270 NULL,
271 NULL);
275 * Subscribe roaming contacts
277 void sipe_subscribe_roaming_contacts(struct sipe_core_private *sipe_private)
279 sipe_subscribe_self(sipe_private,
280 "vnd-microsoft-roaming-contacts",
281 "application/vnd-microsoft-roaming-contacts+xml",
282 NULL,
283 NULL,
284 NULL);
288 * OCS 2005 version
290 void sipe_subscribe_roaming_provisioning(struct sipe_core_private *sipe_private)
292 sipe_subscribe_self(sipe_private,
293 "vnd-microsoft-provisioning",
294 "application/vnd-microsoft-roaming-provisioning+xml",
295 "Expires: 0\r\n",
296 NULL,
297 NULL);
301 * Subscription for provisioning information to help with initial
302 * configuration. This subscription is a one-time query (denoted by the
303 * Expires header, which asks for 0 seconds for the subscription lifetime).
304 * This subscription asks for server configuration, meeting policies, and
305 * policy settings that Communicator must enforce.
307 * @TODO: for what do we need this information?
309 void sipe_subscribe_roaming_provisioning_v2(struct sipe_core_private *sipe_private)
311 sipe_subscribe_self(sipe_private,
312 "vnd-microsoft-provisioning-v2",
313 "application/vnd-microsoft-roaming-provisioning-v2+xml",
314 "Expires: 0\r\n"
315 "Content-Type: application/vnd-microsoft-roaming-provisioning-v2+xml\r\n",
316 "<provisioningGroupList xmlns=\"http://schemas.microsoft.com/2006/09/sip/provisioninggrouplist\">"
317 "<provisioningGroup name=\"ServerConfiguration\"/><provisioningGroup name=\"meetingPolicy\"/>"
318 "<provisioningGroup name=\"ucPolicy\"/>"
319 "</provisioningGroupList>",
320 NULL);
324 * To request for presence information about the user, access level settings
325 * that have already been configured by the user to control who has access to
326 * what information, and the list of contacts who currently have outstanding
327 * subscriptions.
329 * We wait for (BE)NOTIFY messages with some info change (categories,
330 * containers, subscribers)
332 void sipe_subscribe_roaming_self(struct sipe_core_private *sipe_private)
334 sipe_subscribe_self(sipe_private,
335 "vnd-microsoft-roaming-self",
336 "application/vnd-microsoft-roaming-self+xml",
337 "Content-Type: application/vnd-microsoft-roaming-self+xml\r\n",
338 "<roamingList xmlns=\"http://schemas.microsoft.com/2006/09/sip/roaming-self\">"
339 "<roaming type=\"categories\"/>"
340 "<roaming type=\"containers\"/>"
341 "<roaming type=\"subscribers\"/></roamingList>",
342 NULL);
346 * Single Category SUBSCRIBE [MS-PRES] ; To send when the server returns a 200 OK message with state="resubscribe" in response.
347 * The user sends a single SUBSCRIBE request to the subscribed contact.
348 * The To-URI and the URI listed in the resource list MUST be the same for a single category SUBSCRIBE request.
351 void sipe_subscribe_presence_single(struct sipe_core_private *sipe_private,
352 gpointer buddy_name)
354 gchar *to = sip_uri((gchar *)buddy_name);
355 gchar *tmp = get_contact(sipe_private);
356 gchar *request;
357 gchar *content = NULL;
358 gchar *autoextend = "";
359 gchar *content_type = "";
360 struct sipe_buddy *sbuddy = g_hash_table_lookup(sipe_private->buddies, to);
361 gchar *context = sbuddy && sbuddy->just_added ? "><context/></resource>" : "/>";
363 if (sbuddy) sbuddy->just_added = FALSE;
365 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
366 content_type = "Content-Type: application/msrtc-adrl-categorylist+xml\r\n";
367 } else {
368 autoextend = "Supported: com.microsoft.autoextend\r\n";
371 request = g_strdup_printf("Accept: application/msrtc-event-categories+xml, text/xml+msrtc.pidf, application/xpidf+xml, application/pidf+xml, application/rlmi+xml, multipart/related\r\n"
372 "Supported: ms-piggyback-first-notify\r\n"
373 "%s%sSupported: ms-benotify\r\n"
374 "Proxy-Require: ms-benotify\r\n"
375 "Event: presence\r\n"
376 "Contact: %s\r\n",
377 autoextend,
378 content_type,
379 tmp);
381 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
382 content = g_strdup_printf("<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
383 "<action name=\"subscribe\" id=\"63792024\"><adhocList>\n"
384 "<resource uri=\"%s\"%s\n"
385 "</adhocList>\n"
386 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
387 "<category name=\"calendarData\"/>\n"
388 "<category name=\"contactCard\"/>\n"
389 "<category name=\"note\"/>\n"
390 "<category name=\"state\"/>\n"
391 "</categoryList>\n"
392 "</action>\n"
393 "</batchSub>",
394 sipe_private->username,
396 context);
399 g_free(tmp);
401 sipe_subscribe_presence_buddy(sipe_private, to, request, content);
403 g_free(content);
404 g_free(to);
405 g_free(request);
409 * Support for Batch Category SUBSCRIBE [MS-PRES] - msrtc-event-categories+xml OCS 2007
410 * Support for Batch Category SUBSCRIBE [MS-SIP] - adrl+xml LCS 2005
411 * The user sends an initial batched category SUBSCRIBE request against all contacts on his roaming list in only a request
412 * A batch category SUBSCRIBE request MUST have the same To-URI and From-URI.
413 * This header will be send only if adhoclist there is a "Supported: adhoclist" in REGISTER answer else will be send a Single Category SUBSCRIBE
415 static void sipe_subscribe_presence_batched_to(struct sipe_core_private *sipe_private,
416 gchar *resources_uri,
417 gchar *to)
419 gchar *contact = get_contact(sipe_private);
420 gchar *request;
421 gchar *content;
422 gchar *require = "";
423 gchar *accept = "";
424 gchar *autoextend = "";
425 gchar *content_type;
427 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
428 require = ", categoryList";
429 accept = ", application/msrtc-event-categories+xml, application/xpidf+xml, application/pidf+xml";
430 content_type = "application/msrtc-adrl-categorylist+xml";
431 content = g_strdup_printf("<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
432 "<action name=\"subscribe\" id=\"63792024\">\n"
433 "<adhocList>\n%s</adhocList>\n"
434 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
435 "<category name=\"calendarData\"/>\n"
436 "<category name=\"contactCard\"/>\n"
437 "<category name=\"note\"/>\n"
438 "<category name=\"state\"/>\n"
439 "</categoryList>\n"
440 "</action>\n"
441 "</batchSub>",
442 sipe_private->username,
443 resources_uri);
444 } else {
445 autoextend = "Supported: com.microsoft.autoextend\r\n";
446 content_type = "application/adrl+xml";
447 content = g_strdup_printf("<adhoclist xmlns=\"urn:ietf:params:xml:ns:adrl\" uri=\"sip:%s\" name=\"sip:%s\">\n"
448 "<create xmlns=\"\">\n%s</create>\n"
449 "</adhoclist>\n",
450 sipe_private->username,
451 sipe_private->username,
452 resources_uri);
454 g_free(resources_uri);
456 request = g_strdup_printf("Require: adhoclist%s\r\n"
457 "Supported: eventlist\r\n"
458 "Accept: application/rlmi+xml, multipart/related, text/xml+msrtc.pidf%s\r\n"
459 "Supported: ms-piggyback-first-notify\r\n"
460 "%sSupported: ms-benotify\r\n"
461 "Proxy-Require: ms-benotify\r\n"
462 "Event: presence\r\n"
463 "Content-Type: %s\r\n"
464 "Contact: %s\r\n",
465 require,
466 accept,
467 autoextend,
468 content_type,
469 contact);
470 g_free(contact);
472 sipe_subscribe_presence_buddy(sipe_private, to, request, content);
474 g_free(content);
475 g_free(to);
476 g_free(request);
479 struct presence_batched_routed {
480 gchar *host;
481 GSList *buddies;
484 static void sipe_subscribe_presence_batched_routed_free(gpointer payload)
486 struct presence_batched_routed *data = payload;
487 GSList *buddies = data->buddies;
488 while (buddies) {
489 g_free(buddies->data);
490 buddies = buddies->next;
492 g_slist_free(data->buddies);
493 g_free(data->host);
494 g_free(payload);
497 static void sipe_subscribe_presence_batched_routed(struct sipe_core_private *sipe_private,
498 gpointer payload)
500 struct presence_batched_routed *data = payload;
501 GSList *buddies = data->buddies;
502 gchar *resources_uri = g_strdup("");
503 while (buddies) {
504 gchar *tmp = resources_uri;
505 resources_uri = g_strdup_printf("%s<resource uri=\"%s\"/>\n", tmp, (char *) buddies->data);
506 g_free(tmp);
507 buddies = buddies->next;
509 sipe_subscribe_presence_batched_to(sipe_private, resources_uri,
510 g_strdup(data->host));
513 void sipe_subscribe_presence_batched_schedule(struct sipe_core_private *sipe_private,
514 const gchar *action_name,
515 const gchar *who,
516 GSList *buddies,
517 int timeout)
519 struct presence_batched_routed *payload = g_malloc(sizeof(struct presence_batched_routed));
520 payload->host = g_strdup(who);
521 payload->buddies = buddies;
522 sipe_schedule_seconds(sipe_private,
523 action_name,
524 payload,
525 timeout,
526 sipe_subscribe_presence_batched_routed,
527 sipe_subscribe_presence_batched_routed_free);
528 SIPE_DEBUG_INFO("Resubscription multiple contacts with batched support & route(%s) in %d", who, timeout);
531 static void sipe_subscribe_resource_uri_with_context(const gchar *name,
532 gpointer value,
533 gchar **resources_uri)
535 struct sipe_buddy *sbuddy = (struct sipe_buddy *)value;
536 gchar *context = sbuddy && sbuddy->just_added ? "><context/></resource>" : "/>";
537 gchar *tmp = *resources_uri;
539 if (sbuddy) sbuddy->just_added = FALSE; /* should be enought to include context one time */
541 *resources_uri = g_strdup_printf("%s<resource uri=\"%s\"%s\n", tmp, name, context);
542 g_free(tmp);
545 static void sipe_subscribe_resource_uri(const char *name,
546 SIPE_UNUSED_PARAMETER gpointer value,
547 gchar **resources_uri)
549 gchar *tmp = *resources_uri;
550 *resources_uri = g_strdup_printf("%s<resource uri=\"%s\"/>\n", tmp, name);
551 g_free(tmp);
554 void sipe_subscribe_presence_batched(struct sipe_core_private *sipe_private)
556 gchar *to = sip_uri_self(sipe_private);
557 gchar *resources_uri = g_strdup("");
558 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
559 g_hash_table_foreach(sipe_private->buddies, (GHFunc) sipe_subscribe_resource_uri_with_context , &resources_uri);
560 } else {
561 g_hash_table_foreach(sipe_private->buddies, (GHFunc) sipe_subscribe_resource_uri, &resources_uri);
564 sipe_subscribe_presence_batched_to(sipe_private, resources_uri, to);
567 void sipe_subscribe_poolfqdn_resource_uri(const char *host,
568 GSList *server,
569 struct sipe_core_private *sipe_private)
571 struct presence_batched_routed *payload = g_malloc(sizeof(struct presence_batched_routed));
572 SIPE_DEBUG_INFO("process_incoming_notify_rlmi_resub: pool(%s)", host);
573 payload->host = g_strdup(host);
574 payload->buddies = server;
575 sipe_subscribe_presence_batched_routed(sipe_private,
576 payload);
577 sipe_subscribe_presence_batched_routed_free(payload);
581 Local Variables:
582 mode: c
583 c-file-style: "bsd"
584 indent-tabs-mode: t
585 tab-width: 8
586 End: