media: fix relay-info with Farstream 0.2
[siplcs.git] / src / core / sipe-subscriptions.c
blob72ad1c0fed1f2cd7d26ac2dc8ed9d2f0257a77d3
1 /**
2 * @file sipe-subscriptions.c
4 * pidgin-sipe
6 * Copyright (C) 2010-2013 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>
24 #include <string.h>
26 #include <glib.h>
28 #include "sipe-common.h"
29 #include "sipmsg.h"
30 #include "sip-transport.h"
31 #include "sipe-backend.h"
32 #include "sipe-buddy.h"
33 #include "sipe-core.h"
34 #include "sipe-core-private.h"
35 #include "sipe-dialog.h"
36 #include "sipe-mime.h"
37 #include "sipe-notify.h"
38 #include "sipe-schedule.h"
39 #include "sipe-subscriptions.h"
40 #include "sipe-utils.h"
41 #include "sipe-xml.h"
43 /* RFC3265 subscription */
44 struct sip_subscription {
45 struct sip_dialog dialog;
46 gchar *event;
47 GSList *buddies; /* batched subscriptions */
50 static void sipe_subscription_free(struct sip_subscription *subscription)
53 if (!subscription) return;
55 g_free(subscription->event);
56 sipe_utils_slist_free_full(subscription->buddies, g_free);
58 /* NOTE: use cast to prevent BAD_FREE warning from Coverity */
59 sipe_dialog_free((struct sip_dialog *) subscription);
62 void sipe_subscriptions_init(struct sipe_core_private *sipe_private)
64 sipe_private->subscriptions = g_hash_table_new_full(g_str_hash,
65 g_str_equal,
66 g_free,
67 (GDestroyNotify)sipe_subscription_free);
70 static void sipe_unsubscribe_cb(SIPE_UNUSED_PARAMETER gpointer key,
71 gpointer value, gpointer user_data)
73 struct sip_subscription *subscription = value;
74 struct sip_dialog *dialog = &subscription->dialog;
75 struct sipe_core_private *sipe_private = user_data;
76 gchar *contact = get_contact(sipe_private);
77 gchar *hdr = g_strdup_printf(
78 "Event: %s\r\n"
79 "Expires: 0\r\n"
80 "Contact: %s\r\n", subscription->event, contact);
81 g_free(contact);
83 /* Rate limit to max. 25 requests per seconds */
84 g_usleep(1000000 / 25);
86 sip_transport_subscribe(sipe_private,
87 dialog->with,
88 hdr,
89 NULL,
90 dialog,
91 NULL);
93 g_free(hdr);
96 void sipe_subscriptions_unsubscribe(struct sipe_core_private *sipe_private)
98 /* unsubscribe all */
99 g_hash_table_foreach(sipe_private->subscriptions,
100 sipe_unsubscribe_cb,
101 sipe_private);
105 void sipe_subscriptions_destroy(struct sipe_core_private *sipe_private)
107 g_hash_table_destroy(sipe_private->subscriptions);
110 static void sipe_subscription_remove(struct sipe_core_private *sipe_private,
111 const gchar *key)
113 if (g_hash_table_lookup(sipe_private->subscriptions, key)) {
114 g_hash_table_remove(sipe_private->subscriptions, key);
115 SIPE_DEBUG_INFO("sipe_subscription_remove: %s", key);
120 * Generate subscription key
122 * @param event event name (must not by @c NULL)
123 * @param uri presence URI (ignored if @c event != "presence")
125 * @return key string. Must be g_free()'d after use.
127 static gchar *sipe_subscription_key(const gchar *event,
128 const gchar *uri)
130 if (!g_ascii_strcasecmp(event, "presence"))
131 /* Subscription is identified by <presence><uri> key */
132 return(sipe_utils_presence_key(uri));
133 else
134 /* Subscription is identified by <event> key */
135 return(g_strdup_printf("<%s>", event));
138 static struct sip_dialog *sipe_subscribe_dialog(struct sipe_core_private *sipe_private,
139 const gchar *key)
141 struct sip_dialog *dialog = g_hash_table_lookup(sipe_private->subscriptions,
142 key);
143 SIPE_DEBUG_INFO("sipe_subscribe_dialog: dialog for '%s' is %s", key, dialog ? "not NULL" : "NULL");
144 return(dialog);
147 static void sipe_subscription_expiration(struct sipe_core_private *sipe_private,
148 struct sipmsg *msg,
149 const gchar *event);
150 static gboolean process_subscribe_response(struct sipe_core_private *sipe_private,
151 struct sipmsg *msg,
152 struct transaction *trans)
154 gchar *with = parse_from(sipmsg_find_header(msg, "To"));
155 const gchar *event = sipmsg_find_header(msg, "Event");
157 /* The case with 2005 Public IM Connectivity (PIC) - no Event header */
158 if (!event) {
159 struct sipmsg *request_msg = trans->msg;
160 event = sipmsg_find_header(request_msg, "Event");
163 if (event) {
164 const gchar *subscription_state = sipmsg_find_header(msg, "subscription-state");
165 gboolean terminated = subscription_state && strstr(subscription_state, "terminated");
166 gchar *key = sipe_subscription_key(event, with);
169 * @TODO: does the server send this only for one-off
170 * subscriptions, i.e. the ones which anyway
171 * have "Expires: 0"?
173 if (terminated)
174 SIPE_DEBUG_INFO("process_subscribe_response: subscription '%s' to '%s' was terminated",
175 event, with);
177 /* 481 Call Leg Does Not Exist */
178 if ((msg->response == 481) || terminated) {
179 sipe_subscription_remove(sipe_private, key);
181 /* create/store subscription dialog if not yet */
182 } else if (msg->response == 200) {
183 struct sip_dialog *dialog = sipe_subscribe_dialog(sipe_private, key);
185 if (!dialog) {
186 struct sip_subscription *subscription = g_new0(struct sip_subscription, 1);
188 SIPE_DEBUG_INFO("process_subscribe_response: subscription dialog added for event '%s'",
189 key);
191 g_hash_table_insert(sipe_private->subscriptions,
192 key,
193 subscription);
194 key = NULL; /* table takes ownership of key */
196 subscription->dialog.callid = g_strdup(sipmsg_find_header(msg, "Call-ID"));
197 subscription->dialog.cseq = sipmsg_parse_cseq(msg);
198 subscription->dialog.with = g_strdup(with);
199 subscription->event = g_strdup(event);
201 dialog = &subscription->dialog;
204 sipe_dialog_parse(dialog, msg, TRUE);
206 sipe_subscription_expiration(sipe_private, msg, event);
208 g_free(key);
210 g_free(with);
212 if (sipmsg_find_header(msg, "ms-piggyback-cseq"))
213 process_incoming_notify(sipe_private, msg);
215 return(TRUE);
219 * common subscription code
221 static void sipe_subscribe(struct sipe_core_private *sipe_private,
222 const gchar *uri,
223 const gchar *event,
224 const gchar *accept,
225 const gchar *addheaders,
226 const gchar *body,
227 struct sip_dialog *dialog)
229 gchar *contact = get_contact(sipe_private);
230 gchar *hdr = g_strdup_printf(
231 "Event: %s\r\n"
232 "Accept: %s\r\n"
233 "Supported: com.microsoft.autoextend\r\n"
234 "Supported: ms-benotify\r\n"
235 "Proxy-Require: ms-benotify\r\n"
236 "Supported: ms-piggyback-first-notify\r\n"
237 "%s"
238 "Contact: %s\r\n",
239 event,
240 accept,
241 addheaders ? addheaders : "",
242 contact);
243 g_free(contact);
245 sip_transport_subscribe(sipe_private,
246 uri,
247 hdr,
248 body,
249 dialog,
250 process_subscribe_response);
251 g_free(hdr);
255 * common subscription code for self-subscriptions
257 static void sipe_subscribe_self(struct sipe_core_private *sipe_private,
258 const gchar *event,
259 const gchar *accept,
260 const gchar *addheaders,
261 const gchar *body)
263 gchar *self = sip_uri_self(sipe_private);
264 gchar *key = sipe_subscription_key(event, self);
265 struct sip_dialog *dialog = sipe_subscribe_dialog(sipe_private, key);
267 sipe_subscribe(sipe_private,
268 self,
269 event,
270 accept,
271 addheaders,
272 body,
273 dialog);
274 g_free(key);
275 g_free(self);
278 static void sipe_subscribe_presence_wpending(struct sipe_core_private *sipe_private,
279 SIPE_UNUSED_PARAMETER void *unused)
281 sipe_subscribe_self(sipe_private,
282 "presence.wpending",
283 "text/xml+msrtc.wpending",
284 NULL,
285 NULL);
289 * Subscribe roaming ACL
291 static void sipe_subscribe_roaming_acl(struct sipe_core_private *sipe_private,
292 SIPE_UNUSED_PARAMETER void *unused)
294 sipe_subscribe_self(sipe_private,
295 "vnd-microsoft-roaming-ACL",
296 "application/vnd-microsoft-roaming-acls+xml",
297 NULL,
298 NULL);
302 * Subscribe roaming contacts
304 static void sipe_subscribe_roaming_contacts(struct sipe_core_private *sipe_private,
305 SIPE_UNUSED_PARAMETER void *unused)
307 sipe_subscribe_self(sipe_private,
308 "vnd-microsoft-roaming-contacts",
309 "application/vnd-microsoft-roaming-contacts+xml",
310 NULL,
311 NULL);
315 * OCS 2005 version
317 static void sipe_subscribe_roaming_provisioning(struct sipe_core_private *sipe_private,
318 SIPE_UNUSED_PARAMETER void *unused)
320 sipe_subscribe_self(sipe_private,
321 "vnd-microsoft-provisioning",
322 "application/vnd-microsoft-roaming-provisioning+xml",
323 "Expires: 0\r\n",
324 NULL);
328 * Subscription for provisioning information to help with initial
329 * configuration. This subscription is a one-time query (denoted by the
330 * Expires header, which asks for 0 seconds for the subscription lifetime).
331 * This subscription asks for server configuration, meeting policies, and
332 * policy settings that Communicator must enforce.
334 static void sipe_subscribe_roaming_provisioning_v2(struct sipe_core_private *sipe_private,
335 SIPE_UNUSED_PARAMETER void *unused)
337 sipe_subscribe_self(sipe_private,
338 "vnd-microsoft-provisioning-v2",
339 "application/vnd-microsoft-roaming-provisioning-v2+xml",
340 "Expires: 0\r\n"
341 "Content-Type: application/vnd-microsoft-roaming-provisioning-v2+xml\r\n",
342 "<provisioningGroupList xmlns=\"http://schemas.microsoft.com/2006/09/sip/provisioninggrouplist\">"
343 " <provisioningGroup name=\"ServerConfiguration\"/>"
344 " <provisioningGroup name=\"meetingPolicy\"/>"
345 " <provisioningGroup name=\"persistentChatConfiguration\"/>"
346 " <provisioningGroup name=\"ucPolicy\"/>"
347 "</provisioningGroupList>");
351 * To request for presence information about the user, access level settings
352 * that have already been configured by the user to control who has access to
353 * what information, and the list of contacts who currently have outstanding
354 * subscriptions.
356 * We wait for (BE)NOTIFY messages with some info change (categories,
357 * containers, subscribers)
359 static void sipe_subscribe_roaming_self(struct sipe_core_private *sipe_private,
360 SIPE_UNUSED_PARAMETER void *unused)
362 sipe_subscribe_self(sipe_private,
363 "vnd-microsoft-roaming-self",
364 "application/vnd-microsoft-roaming-self+xml",
365 "Content-Type: application/vnd-microsoft-roaming-self+xml\r\n",
366 "<roamingList xmlns=\"http://schemas.microsoft.com/2006/09/sip/roaming-self\">"
367 "<roaming type=\"categories\"/>"
368 "<roaming type=\"containers\"/>"
369 "<roaming type=\"subscribers\"/></roamingList>");
372 static void sipe_presence_timeout_mime_cb(gpointer user_data,
373 SIPE_UNUSED_PARAMETER const GSList *fields,
374 const gchar *body,
375 gsize length)
377 GSList **buddies = user_data;
378 sipe_xml *xml = sipe_xml_parse(body, length);
380 if (xml && !sipe_strequal(sipe_xml_name(xml), "list")) {
381 const gchar *uri = sipe_xml_attribute(xml, "uri");
382 const sipe_xml *xn_category;
385 * automaton: presence is never expected to change
387 * see: http://msdn.microsoft.com/en-us/library/ee354295(office.13).aspx
389 for (xn_category = sipe_xml_child(xml, "category");
390 xn_category;
391 xn_category = sipe_xml_twin(xn_category)) {
392 if (sipe_strequal(sipe_xml_attribute(xn_category, "name"),
393 "contactCard")) {
394 const sipe_xml *node = sipe_xml_child(xn_category, "contactCard/automaton");
395 if (node) {
396 char *boolean = sipe_xml_data(node);
397 if (sipe_strequal(boolean, "true")) {
398 SIPE_DEBUG_INFO("sipe_process_presence_timeout: %s is an automaton: - not subscribing to presence updates",
399 uri);
400 uri = NULL;
402 g_free(boolean);
404 break;
408 if (uri) {
409 *buddies = g_slist_append(*buddies, sip_uri(uri));
413 sipe_xml_free(xml);
416 static void sipe_subscribe_presence_batched_schedule(struct sipe_core_private *sipe_private,
417 const gchar *action_name,
418 const gchar *who,
419 GSList *buddies,
420 int timeout);
421 static void sipe_process_presence_timeout(struct sipe_core_private *sipe_private,
422 struct sipmsg *msg,
423 const gchar *who,
424 int timeout)
426 const char *ctype = sipmsg_find_header(msg, "Content-Type");
427 gchar *action_name = sipe_utils_presence_key(who);
429 SIPE_DEBUG_INFO("sipe_process_presence_timeout: Content-Type: %s", ctype ? ctype : "");
431 if (ctype &&
432 strstr(ctype, "multipart") &&
433 (strstr(ctype, "application/rlmi+xml") ||
434 strstr(ctype, "application/msrtc-event-categories+xml"))) {
435 GSList *buddies = NULL;
437 sipe_mime_parts_foreach(ctype, msg->body, sipe_presence_timeout_mime_cb, &buddies);
439 if (buddies)
440 sipe_subscribe_presence_batched_schedule(sipe_private,
441 action_name,
442 who,
443 buddies,
444 timeout);
446 } else {
447 sipe_schedule_seconds(sipe_private,
448 action_name,
449 g_strdup(who),
450 timeout,
451 sipe_subscribe_presence_single_cb,
452 g_free);
453 SIPE_DEBUG_INFO("Resubscription single contact with batched support(%s) in %d seconds", who, timeout);
455 g_free(action_name);
459 * @param expires not respected if set to negative value (E.g. -1)
461 void sipe_subscribe_conference(struct sipe_core_private *sipe_private,
462 const gchar *id,
463 gboolean expires)
465 sipe_subscribe(sipe_private,
467 "conference",
468 "application/conference-info+xml",
469 expires ? "Expires: 0\r\n" : NULL,
470 NULL,
471 NULL);
475 * code for presence subscription
477 static void sipe_subscribe_presence_buddy(struct sipe_core_private *sipe_private,
478 const gchar *uri,
479 const gchar *request,
480 const gchar *body)
482 gchar *key = sipe_utils_presence_key(uri);
484 sip_transport_subscribe(sipe_private,
485 uri,
486 request,
487 body,
488 sipe_subscribe_dialog(sipe_private, key),
489 process_subscribe_response);
491 g_free(key);
495 * if to == NULL: initial single subscription
496 * OCS2005: send to URI
497 * OCS2007: send to self URI
499 * if to != NULL:
500 * Single Category SUBSCRIBE [MS-PRES] ; To send when the server returns a 200 OK message with state="resubscribe" in response.
501 * The user sends a single SUBSCRIBE request to the subscribed contact.
502 * The To-URI and the URI listed in the resource list MUST be the same for a single category SUBSCRIBE request.
505 void sipe_subscribe_presence_single(struct sipe_core_private *sipe_private,
506 const gchar *uri,
507 const gchar *to)
509 gchar *self = NULL;
510 gchar *contact = get_contact(sipe_private);
511 gchar *request;
512 gchar *content = NULL;
513 const gchar *additional = "";
514 const gchar *content_type = "";
515 struct sipe_buddy *sbuddy = sipe_buddy_find_by_uri(sipe_private,
516 uri);
518 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
519 content_type = "Content-Type: application/msrtc-adrl-categorylist+xml\r\n";
520 content = g_strdup_printf("<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
521 "<action name=\"subscribe\" id=\"63792024\"><adhocList>\n"
522 "<resource uri=\"%s\"%s\n"
523 "</adhocList>\n"
524 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
525 "<category name=\"calendarData\"/>\n"
526 "<category name=\"contactCard\"/>\n"
527 "<category name=\"note\"/>\n"
528 "<category name=\"state\"/>\n"
529 "</categoryList>\n"
530 "</action>\n"
531 "</batchSub>",
532 sipe_private->username,
533 uri,
534 sbuddy && sbuddy->just_added ? "><context/></resource>" : "/>");
535 if (!to) {
536 additional = "Require: adhoclist, categoryList\r\n" \
537 "Supported: eventlist\r\n";
538 to = self = sip_uri_self(sipe_private);
541 } else {
542 additional = "Supported: com.microsoft.autoextend\r\n";
543 if (!to)
544 to = uri;
547 if (sbuddy)
548 sbuddy->just_added = FALSE;
550 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"
551 "Supported: ms-piggyback-first-notify\r\n"
552 "%s%sSupported: ms-benotify\r\n"
553 "Proxy-Require: ms-benotify\r\n"
554 "Event: presence\r\n"
555 "Contact: %s\r\n",
556 additional,
557 content_type,
558 contact);
559 g_free(contact);
561 sipe_subscribe_presence_buddy(sipe_private, to, request, content);
563 g_free(content);
564 g_free(self);
565 g_free(request);
568 void sipe_subscribe_presence_single_cb(struct sipe_core_private *sipe_private,
569 gpointer uri)
571 sipe_subscribe_presence_single(sipe_private, uri, NULL);
576 * Support for Batch Category SUBSCRIBE [MS-PRES] - msrtc-event-categories+xml OCS 2007
577 * Support for Batch Category SUBSCRIBE [MS-SIP] - adrl+xml LCS 2005
578 * The user sends an initial batched category SUBSCRIBE request against all contacts on his roaming list in only a request
579 * A batch category SUBSCRIBE request MUST have the same To-URI and From-URI.
580 * This header will be send only if adhoclist there is a "Supported: adhoclist" in REGISTER answer else will be send a Single Category SUBSCRIBE
582 static void sipe_subscribe_presence_batched_to(struct sipe_core_private *sipe_private,
583 gchar *resources_uri,
584 const gchar *to)
586 gchar *contact = get_contact(sipe_private);
587 gchar *request;
588 gchar *content;
589 const gchar *require = "";
590 const gchar *accept = "";
591 const gchar *autoextend = "";
592 const gchar *content_type;
594 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
595 require = ", categoryList";
596 accept = ", application/msrtc-event-categories+xml, application/xpidf+xml, application/pidf+xml";
597 content_type = "application/msrtc-adrl-categorylist+xml";
598 content = g_strdup_printf("<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
599 "<action name=\"subscribe\" id=\"63792024\">\n"
600 "<adhocList>\n%s</adhocList>\n"
601 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
602 "<category name=\"calendarData\"/>\n"
603 "<category name=\"contactCard\"/>\n"
604 "<category name=\"note\"/>\n"
605 "<category name=\"state\"/>\n"
606 "</categoryList>\n"
607 "</action>\n"
608 "</batchSub>",
609 sipe_private->username,
610 resources_uri);
611 } else {
612 autoextend = "Supported: com.microsoft.autoextend\r\n";
613 content_type = "application/adrl+xml";
614 content = g_strdup_printf("<adhoclist xmlns=\"urn:ietf:params:xml:ns:adrl\" uri=\"sip:%s\" name=\"sip:%s\">\n"
615 "<create xmlns=\"\">\n%s</create>\n"
616 "</adhoclist>\n",
617 sipe_private->username,
618 sipe_private->username,
619 resources_uri);
621 g_free(resources_uri);
623 request = g_strdup_printf("Require: adhoclist%s\r\n"
624 "Supported: eventlist\r\n"
625 "Accept: application/rlmi+xml, multipart/related, text/xml+msrtc.pidf%s\r\n"
626 "Supported: ms-piggyback-first-notify\r\n"
627 "%sSupported: ms-benotify\r\n"
628 "Proxy-Require: ms-benotify\r\n"
629 "Event: presence\r\n"
630 "Content-Type: %s\r\n"
631 "Contact: %s\r\n",
632 require,
633 accept,
634 autoextend,
635 content_type,
636 contact);
637 g_free(contact);
639 sipe_subscribe_presence_buddy(sipe_private, to, request, content);
641 g_free(content);
642 g_free(request);
645 struct presence_batched_routed {
646 gchar *host;
647 const GSList *buddies; /* points to subscription->buddies */
650 static void sipe_subscribe_presence_batched_routed_free(gpointer payload)
652 struct presence_batched_routed *data = payload;
653 g_free(data->host);
654 g_free(payload);
657 static void sipe_subscribe_presence_batched_routed(struct sipe_core_private *sipe_private,
658 gpointer payload)
660 struct presence_batched_routed *data = payload;
661 const GSList *buddies = data->buddies;
662 gchar *resources_uri = g_strdup("");
663 while (buddies) {
664 gchar *tmp = resources_uri;
665 resources_uri = g_strdup_printf("%s<resource uri=\"%s\"/>\n", tmp, (char *) buddies->data);
666 g_free(tmp);
667 buddies = buddies->next;
669 sipe_subscribe_presence_batched_to(sipe_private,
670 resources_uri,
671 data->host);
674 static void sipe_subscribe_presence_batched_schedule(struct sipe_core_private *sipe_private,
675 const gchar *action_name,
676 const gchar *who,
677 GSList *buddies,
678 int timeout)
680 struct sip_subscription *subscription = g_hash_table_lookup(sipe_private->subscriptions,
681 action_name);
682 struct presence_batched_routed *payload = g_malloc(sizeof(struct presence_batched_routed));
684 if (subscription->buddies) {
685 /* merge old and new list */
686 GSList *entry = buddies;
687 while (entry) {
688 subscription->buddies = sipe_utils_slist_insert_unique_sorted(subscription->buddies,
689 g_strdup(entry->data),
690 (GCompareFunc) g_ascii_strcasecmp,
691 g_free);
692 entry = entry->next;
694 sipe_utils_slist_free_full(buddies, g_free);
695 } else {
696 /* no list yet, simply take ownership of whole list */
697 subscription->buddies = buddies;
700 payload->host = g_strdup(who);
701 payload->buddies = subscription->buddies;
702 sipe_schedule_seconds(sipe_private,
703 action_name,
704 payload,
705 timeout,
706 sipe_subscribe_presence_batched_routed,
707 sipe_subscribe_presence_batched_routed_free);
708 SIPE_DEBUG_INFO("Resubscription multiple contacts with batched support & route(%s) in %d", who, timeout);
711 static void sipe_subscribe_resource_uri_with_context(const gchar *name,
712 gpointer value,
713 gchar **resources_uri)
715 struct sipe_buddy *sbuddy = (struct sipe_buddy *)value;
716 gchar *context = sbuddy && sbuddy->just_added ? "><context/></resource>" : "/>";
717 gchar *tmp = *resources_uri;
719 /* should be enough to include context one time */
720 if (sbuddy)
721 sbuddy->just_added = FALSE;
723 *resources_uri = g_strdup_printf("%s<resource uri=\"%s\"%s\n", tmp, name, context);
724 g_free(tmp);
727 static void sipe_subscribe_resource_uri(const char *name,
728 SIPE_UNUSED_PARAMETER gpointer value,
729 gchar **resources_uri)
731 gchar *tmp = *resources_uri;
732 *resources_uri = g_strdup_printf("%s<resource uri=\"%s\"/>\n", tmp, name);
733 g_free(tmp);
737 * A callback for g_hash_table_foreach
739 static void schedule_buddy_resubscription_cb(char *buddy_name,
740 SIPE_UNUSED_PARAMETER struct sipe_buddy *buddy,
741 struct sipe_core_private *sipe_private)
743 guint time_range = (sipe_buddy_count(sipe_private) * 1000) / 25; /* time interval for 25 requests per sec. In msec. */
746 * g_hash_table_size() can never return 0, otherwise this function
747 * wouldn't be called :-) But to keep Coverity happy...
749 if (time_range) {
750 gchar *action_name = sipe_utils_presence_key(buddy_name);
751 guint timeout = ((guint) rand()) / (RAND_MAX / time_range) + 1; /* random period within the range but never 0! */
753 sipe_schedule_mseconds(sipe_private,
754 action_name,
755 g_strdup(buddy_name),
756 timeout,
757 sipe_subscribe_presence_single_cb,
758 g_free);
759 g_free(action_name);
763 void sipe_subscribe_presence_initial(struct sipe_core_private *sipe_private)
766 * Subscribe to buddies, but only do it once.
767 * We'll resubsribe to them based on the Expire field values.
769 if (!SIPE_CORE_PRIVATE_FLAG_IS(SUBSCRIBED_BUDDIES)) {
771 if (SIPE_CORE_PRIVATE_FLAG_IS(BATCHED_SUPPORT)) {
772 gchar *to = sip_uri_self(sipe_private);
773 gchar *resources_uri = g_strdup("");
774 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
775 sipe_buddy_foreach(sipe_private,
776 (GHFunc) sipe_subscribe_resource_uri_with_context,
777 &resources_uri);
778 } else {
779 sipe_buddy_foreach(sipe_private,
780 (GHFunc) sipe_subscribe_resource_uri,
781 &resources_uri);
783 sipe_subscribe_presence_batched_to(sipe_private, resources_uri, to);
784 g_free(to);
786 } else {
787 sipe_buddy_foreach(sipe_private,
788 (GHFunc) schedule_buddy_resubscription_cb,
789 sipe_private);
792 SIPE_CORE_PRIVATE_FLAG_SET(SUBSCRIBED_BUDDIES);
796 void sipe_subscribe_poolfqdn_resource_uri(const char *host,
797 GSList *server,
798 struct sipe_core_private *sipe_private)
800 struct presence_batched_routed *payload = g_malloc(sizeof(struct presence_batched_routed));
801 SIPE_DEBUG_INFO("process_incoming_notify_rlmi_resub: pool(%s)", host);
802 payload->host = g_strdup(host);
803 payload->buddies = server;
804 sipe_subscribe_presence_batched_routed(sipe_private,
805 payload);
806 sipe_subscribe_presence_batched_routed_free(payload);
807 sipe_utils_slist_free_full(server, g_free);
812 * subscription expiration handling
814 struct event_subscription_data {
815 const gchar *event;
816 sipe_schedule_action callback;
817 guint flags;
820 #define EVENT_OCS2005 0x00000001
821 #define EVENT_OCS2007 0x00000002
823 static const struct event_subscription_data events_table[] =
826 * For 2007+ it does not make sence to subscribe to:
828 * presence.wpending
829 * vnd-microsoft-roaming-ACL
830 * vnd-microsoft-provisioning (not v2)
832 * These are only needed as backward compatibility for older clients
834 * For 2005- we publish our initial statuses only after we received
835 * our existing UserInfo data in response to self subscription.
836 * Only in this case we won't override existing UserInfo data
837 * set earlier or by other client on our behalf.
839 * For 2007+ we publish our initial statuses and calendar data only
840 * after we received our existing publications in roaming_self.
841 * Only in this case we know versions of current publications made
842 * on our behalf.
844 { "presence.wpending", sipe_subscribe_presence_wpending,
845 EVENT_OCS2005 },
846 { "vnd-microsoft-roaming-ACL", sipe_subscribe_roaming_acl,
847 EVENT_OCS2005 },
848 { "vnd-microsoft-roaming-contacts", sipe_subscribe_roaming_contacts,
849 EVENT_OCS2005 | EVENT_OCS2007 },
850 { "vnd-microsoft-provisioning", sipe_subscribe_roaming_provisioning,
851 EVENT_OCS2005 },
852 { "vnd-microsoft-provisioning-v2", sipe_subscribe_roaming_provisioning_v2,
853 EVENT_OCS2007 },
854 { "vnd-microsoft-roaming-self", sipe_subscribe_roaming_self,
855 EVENT_OCS2007 },
856 { NULL, NULL, 0 }
859 static void sipe_subscription_expiration(struct sipe_core_private *sipe_private,
860 struct sipmsg *msg,
861 const gchar *event)
863 const gchar *expires_header = sipmsg_find_header(msg, "Expires");
864 guint timeout = expires_header ? strtol(expires_header, NULL, 10) : 0;
866 if (timeout) {
867 /* 2 min ahead of expiration */
868 if (timeout > 240) timeout -= 120;
870 if (sipe_strcase_equal(event, "presence")) {
871 gchar *who = parse_from(sipmsg_find_header(msg, "To"));
873 if (SIPE_CORE_PRIVATE_FLAG_IS(BATCHED_SUPPORT)) {
874 sipe_process_presence_timeout(sipe_private, msg, who, timeout);
875 } else {
876 gchar *action_name = sipe_utils_presence_key(who);
877 sipe_schedule_seconds(sipe_private,
878 action_name,
879 g_strdup(who),
880 timeout,
881 sipe_subscribe_presence_single_cb,
882 g_free);
883 g_free(action_name);
884 SIPE_DEBUG_INFO("Resubscription single contact '%s' in %d seconds", who, timeout);
886 g_free(who);
888 } else {
889 const struct event_subscription_data *esd;
891 for (esd = events_table; esd->event; esd++) {
892 if (sipe_strcase_equal(event, esd->event)) {
893 gchar *action_name = g_strdup_printf("<%s>", event);
894 sipe_schedule_seconds(sipe_private,
895 action_name,
896 NULL,
897 timeout,
898 esd->callback,
899 NULL);
900 g_free(action_name);
901 SIPE_DEBUG_INFO("Resubscription to event '%s' in %d seconds", event, timeout);
902 break;
910 * Initial event subscription
912 void sipe_subscription_self_events(struct sipe_core_private *sipe_private)
914 const guint mask = SIPE_CORE_PRIVATE_FLAG_IS(OCS2007) ? EVENT_OCS2007 : EVENT_OCS2005;
915 const struct event_subscription_data *esd;
917 /* subscribe to those events which are selected for
918 * this version and are allowed by the server */
919 for (esd = events_table; esd->event; esd++)
920 if ((esd->flags & mask) &&
921 (g_slist_find_custom(sipe_private->allowed_events,
922 esd->event,
923 (GCompareFunc) g_ascii_strcasecmp) != NULL))
924 (*esd->callback)(sipe_private, NULL);
928 Local Variables:
929 mode: c
930 c-file-style: "bsd"
931 indent-tabs-mode: t
932 tab-width: 8
933 End: