2 * @file sipe-subscriptions.c
6 * Copyright (C) 2010-2019 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
32 #include "sipe-common.h"
34 #include "sip-transport.h"
35 #include "sipe-backend.h"
36 #include "sipe-buddy.h"
37 #include "sipe-core.h"
38 #include "sipe-core-private.h"
39 #include "sipe-dialog.h"
40 #include "sipe-mime.h"
42 #include "sipe-notify.h"
43 #include "sipe-schedule.h"
44 #include "sipe-subscriptions.h"
46 #include "sipe-utils.h"
49 /* RFC3265 subscription */
50 struct sip_subscription
{
51 struct sip_dialog dialog
;
53 GSList
*buddies
; /* batched subscriptions */
56 static void sipe_subscription_free(struct sip_subscription
*subscription
)
59 if (!subscription
) return;
61 g_free(subscription
->event
);
62 sipe_utils_slist_free_full(subscription
->buddies
, g_free
);
64 /* NOTE: use cast to prevent BAD_FREE warning from Coverity */
65 sipe_dialog_free((struct sip_dialog
*) subscription
);
68 void sipe_subscriptions_init(struct sipe_core_private
*sipe_private
)
70 sipe_private
->subscriptions
= g_hash_table_new_full(g_str_hash
,
73 (GDestroyNotify
)sipe_subscription_free
);
76 static void sipe_unsubscribe_cb(SIPE_UNUSED_PARAMETER gpointer key
,
77 gpointer value
, gpointer user_data
)
79 struct sip_subscription
*subscription
= value
;
80 struct sip_dialog
*dialog
= &subscription
->dialog
;
81 struct sipe_core_private
*sipe_private
= user_data
;
82 gchar
*contact
= get_contact(sipe_private
);
83 gchar
*hdr
= g_strdup_printf(
86 "Contact: %s\r\n", subscription
->event
, contact
);
89 /* Rate limit to max. 25 requests per seconds */
90 g_usleep(1000000 / 25);
92 sip_transport_subscribe(sipe_private
,
102 void sipe_subscriptions_unsubscribe(struct sipe_core_private
*sipe_private
)
104 /* unsubscribe all */
105 g_hash_table_foreach(sipe_private
->subscriptions
,
111 void sipe_subscriptions_destroy(struct sipe_core_private
*sipe_private
)
113 g_hash_table_destroy(sipe_private
->subscriptions
);
116 static void sipe_subscription_remove(struct sipe_core_private
*sipe_private
,
119 if (g_hash_table_lookup(sipe_private
->subscriptions
, key
)) {
120 g_hash_table_remove(sipe_private
->subscriptions
, key
);
121 SIPE_DEBUG_INFO("sipe_subscription_remove: %s", key
);
126 * Generate subscription key
128 * @param event event name (must not by @c NULL)
129 * @param uri presence URI (ignored if @c event != "presence")
131 * @return key string. Must be g_free()'d after use.
133 static gchar
*sipe_subscription_key(const gchar
*event
,
136 if (!g_ascii_strcasecmp(event
, "presence"))
137 /* Subscription is identified by <presence><uri> key */
138 return(sipe_utils_presence_key(uri
));
140 /* Subscription is identified by <event> key */
141 return(g_strdup_printf("<%s>", event
));
144 static struct sip_dialog
*sipe_subscribe_dialog(struct sipe_core_private
*sipe_private
,
147 struct sip_dialog
*dialog
= g_hash_table_lookup(sipe_private
->subscriptions
,
149 SIPE_DEBUG_INFO("sipe_subscribe_dialog: dialog for '%s' is %s", key
, dialog
? "not NULL" : "NULL");
153 static void sipe_subscription_expiration(struct sipe_core_private
*sipe_private
,
156 static gboolean
process_subscribe_response(struct sipe_core_private
*sipe_private
,
158 struct transaction
*trans
)
160 const gchar
*event
= sipmsg_find_event_header(msg
);
162 /* No Event header - error or 2005 Public IM Connectivity (PIC) */
164 struct sipmsg
*request_msg
= trans
->msg
;
165 event
= sipmsg_find_event_header(request_msg
);
169 gchar
*with
= sipmsg_parse_to_address(msg
);
170 const gchar
*subscription_state
= sipmsg_find_header(msg
, "subscription-state");
171 gboolean terminated
= subscription_state
&& strstr(subscription_state
, "terminated");
172 gchar
*key
= sipe_subscription_key(event
, with
);
175 * @TODO: does the server send this only for one-off
176 * subscriptions, i.e. the ones which anyway
180 SIPE_DEBUG_INFO("process_subscribe_response: subscription '%s' to '%s' was terminated",
183 /* 400 Bad request */
184 if (msg
->response
== 400) {
186 SIPE_DEBUG_INFO("process_subscribe_response: subscription '%s' to '%s' failed",
189 sipe_subscription_remove(sipe_private
, key
);
191 if (sipe_strcase_equal(event
, "presence")) {
192 sipe_backend_notify_error(SIPE_CORE_PUBLIC
,
193 _("Presence subscription failed!"),
194 _("One or more buddies will therefore permanently show as offline.\n\nPlease check that there are no corrupted SIP URIs in your contacts list."));
197 /* 481 Call Leg Does Not Exist */
198 } else if ((msg
->response
== 481) || terminated
) {
199 sipe_subscription_remove(sipe_private
, key
);
201 /* 488 Not acceptable here */
202 } else if (msg
->response
== 488) {
204 SIPE_DEBUG_INFO("process_subscribe_response: subscription '%s' to '%s' was rejected",
207 sipe_subscription_remove(sipe_private
, key
);
209 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
) &&
210 sipe_strcase_equal(event
, "vnd-microsoft-roaming-contacts")) {
211 SIPE_DEBUG_INFO_NOFORMAT("no contact list available - assuming Lync 2013+ and Unified Contact Store (UCS)");
212 SIPE_CORE_PRIVATE_FLAG_SET(LYNC2013
);
213 sipe_ucs_init(sipe_private
, TRUE
);
216 /* create/store subscription dialog if not yet */
217 } else if (msg
->response
== 200) {
218 struct sip_dialog
*dialog
= sipe_subscribe_dialog(sipe_private
, key
);
221 struct sip_subscription
*subscription
= g_new0(struct sip_subscription
, 1);
223 SIPE_DEBUG_INFO("process_subscribe_response: subscription dialog added for event '%s'",
226 g_hash_table_insert(sipe_private
->subscriptions
,
229 key
= NULL
; /* table takes ownership of key */
231 subscription
->dialog
.callid
= g_strdup(sipmsg_find_call_id_header(msg
));
232 subscription
->dialog
.cseq
= sipmsg_parse_cseq(msg
);
233 subscription
->dialog
.with
= g_strdup(with
);
234 subscription
->event
= g_strdup(event
);
236 dialog
= &subscription
->dialog
;
239 sipe_dialog_parse(dialog
, msg
, TRUE
);
241 sipe_subscription_expiration(sipe_private
, msg
, event
);
247 if (sipmsg_find_header(msg
, "ms-piggyback-cseq"))
248 process_incoming_notify(sipe_private
, msg
);
254 * common subscription code
256 static void sipe_subscribe(struct sipe_core_private
*sipe_private
,
260 const gchar
*addheaders
,
262 struct sip_dialog
*dialog
)
264 gchar
*contact
= get_contact(sipe_private
);
265 gchar
*hdr
= g_strdup_printf(
268 "Supported: com.microsoft.autoextend\r\n"
269 "Supported: ms-benotify\r\n"
270 "Proxy-Require: ms-benotify\r\n"
271 "Supported: ms-piggyback-first-notify\r\n"
276 addheaders
? addheaders
: "",
280 sip_transport_subscribe(sipe_private
,
285 process_subscribe_response
);
290 * common subscription code for self-subscriptions
292 static void sipe_subscribe_self(struct sipe_core_private
*sipe_private
,
295 const gchar
*addheaders
,
298 gchar
*self
= sip_uri_self(sipe_private
);
299 gchar
*key
= sipe_subscription_key(event
, self
);
300 struct sip_dialog
*dialog
= sipe_subscribe_dialog(sipe_private
, key
);
302 sipe_subscribe(sipe_private
,
313 static void sipe_subscribe_presence_wpending(struct sipe_core_private
*sipe_private
,
314 SIPE_UNUSED_PARAMETER
void *unused
)
316 sipe_subscribe_self(sipe_private
,
318 "text/xml+msrtc.wpending",
324 * Subscribe roaming ACL
326 static void sipe_subscribe_roaming_acl(struct sipe_core_private
*sipe_private
,
327 SIPE_UNUSED_PARAMETER
void *unused
)
329 sipe_subscribe_self(sipe_private
,
330 "vnd-microsoft-roaming-ACL",
331 "application/vnd-microsoft-roaming-acls+xml",
337 * Subscribe roaming contacts
339 static void sipe_subscribe_roaming_contacts(struct sipe_core_private
*sipe_private
,
340 SIPE_UNUSED_PARAMETER
void *unused
)
342 const gchar
*addheaders
= NULL
;
344 /* indicate that we support Unified Contact Store (UCS) */
345 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
))
346 addheaders
= "Supported: ms-ucs\r\n";
347 sipe_subscribe_self(sipe_private
,
348 "vnd-microsoft-roaming-contacts",
349 "application/vnd-microsoft-roaming-contacts+xml",
357 static void sipe_subscribe_roaming_provisioning(struct sipe_core_private
*sipe_private
,
358 SIPE_UNUSED_PARAMETER
void *unused
)
360 sipe_subscribe_self(sipe_private
,
361 "vnd-microsoft-provisioning",
362 "application/vnd-microsoft-roaming-provisioning+xml",
368 * Subscription for provisioning information to help with initial
369 * configuration. This subscription is a one-time query (denoted by the
370 * Expires header, which asks for 0 seconds for the subscription lifetime).
371 * This subscription asks for server configuration, meeting policies, and
372 * policy settings that Communicator must enforce.
374 static void sipe_subscribe_roaming_provisioning_v2(struct sipe_core_private
*sipe_private
,
375 SIPE_UNUSED_PARAMETER
void *unused
)
377 sipe_subscribe_self(sipe_private
,
378 "vnd-microsoft-provisioning-v2",
379 "application/vnd-microsoft-roaming-provisioning-v2+xml",
381 "Content-Type: application/vnd-microsoft-roaming-provisioning-v2+xml\r\n",
382 "<provisioningGroupList xmlns=\"http://schemas.microsoft.com/2006/09/sip/provisioninggrouplist\">"
383 " <provisioningGroup name=\"ServerConfiguration\"/>"
384 " <provisioningGroup name=\"meetingPolicy\"/>"
385 " <provisioningGroup name=\"persistentChatConfiguration\"/>"
386 " <provisioningGroup name=\"ucPolicy\"/>"
387 "</provisioningGroupList>");
391 * To request for presence information about the user, access level settings
392 * that have already been configured by the user to control who has access to
393 * what information, and the list of contacts who currently have outstanding
396 * We wait for (BE)NOTIFY messages with some info change (categories,
397 * containers, subscribers)
399 static void sipe_subscribe_roaming_self(struct sipe_core_private
*sipe_private
,
400 SIPE_UNUSED_PARAMETER
void *unused
)
402 sipe_subscribe_self(sipe_private
,
403 "vnd-microsoft-roaming-self",
404 "application/vnd-microsoft-roaming-self+xml",
405 "Content-Type: application/vnd-microsoft-roaming-self+xml\r\n",
406 "<roamingList xmlns=\"http://schemas.microsoft.com/2006/09/sip/roaming-self\">"
407 "<roaming type=\"categories\"/>"
408 "<roaming type=\"containers\"/>"
409 "<roaming type=\"subscribers\"/></roamingList>");
412 static void sipe_presence_timeout_mime_cb(gpointer user_data
,
413 SIPE_UNUSED_PARAMETER
const GSList
*fields
,
417 GSList
**buddies
= user_data
;
418 sipe_xml
*xml
= sipe_xml_parse(body
, length
);
420 if (xml
&& !sipe_strequal(sipe_xml_name(xml
), "list")) {
421 const gchar
*uri
= sipe_xml_attribute(xml
, "uri");
422 const sipe_xml
*xn_category
;
425 * automaton: presence is never expected to change
427 * see: http://msdn.microsoft.com/en-us/library/ee354295(office.13).aspx
429 for (xn_category
= sipe_xml_child(xml
, "category");
431 xn_category
= sipe_xml_twin(xn_category
)) {
432 if (sipe_strequal(sipe_xml_attribute(xn_category
, "name"),
434 const sipe_xml
*node
= sipe_xml_child(xn_category
, "contactCard/automaton");
436 char *boolean
= sipe_xml_data(node
);
437 if (sipe_strequal(boolean
, "true")) {
438 SIPE_DEBUG_INFO("sipe_process_presence_timeout: %s is an automaton: - not subscribing to presence updates",
449 *buddies
= g_slist_append(*buddies
, sip_uri(uri
));
456 static void sipe_subscribe_presence_batched_schedule(struct sipe_core_private
*sipe_private
,
457 const gchar
*action_name
,
461 static void sipe_process_presence_timeout(struct sipe_core_private
*sipe_private
,
466 const char *ctype
= sipmsg_find_content_type_header(msg
);
467 gchar
*action_name
= sipe_utils_presence_key(who
);
469 SIPE_DEBUG_INFO("sipe_process_presence_timeout: Content-Type: %s", ctype
? ctype
: "");
472 strstr(ctype
, "multipart") &&
473 (strstr(ctype
, "application/rlmi+xml") ||
474 strstr(ctype
, "application/msrtc-event-categories+xml"))) {
475 GSList
*buddies
= NULL
;
477 sipe_mime_parts_foreach(ctype
, msg
->body
, sipe_presence_timeout_mime_cb
, &buddies
);
480 sipe_subscribe_presence_batched_schedule(sipe_private
,
487 sipe_schedule_seconds(sipe_private
,
491 sipe_subscribe_presence_single_cb
,
493 SIPE_DEBUG_INFO("Resubscription single contact with batched support(%s) in %d seconds", who
, timeout
);
499 * @param expires not respected if set to negative value (E.g. -1)
501 void sipe_subscribe_conference(struct sipe_core_private
*sipe_private
,
505 sipe_subscribe(sipe_private
,
508 "application/conference-info+xml",
509 expires
? "Expires: 0\r\n" : NULL
,
515 * code for presence subscription
517 static void sipe_subscribe_presence_buddy(struct sipe_core_private
*sipe_private
,
519 const gchar
*request
,
522 gchar
*key
= sipe_utils_presence_key(uri
);
524 sip_transport_subscribe(sipe_private
,
528 sipe_subscribe_dialog(sipe_private
, key
),
529 process_subscribe_response
);
535 * if to == NULL: initial single subscription
536 * OCS2005: send to URI
537 * OCS2007: send to self URI
540 * Single Category SUBSCRIBE [MS-PRES] ; To send when the server returns a 200 OK message with state="resubscribe" in response.
541 * The user sends a single SUBSCRIBE request to the subscribed contact.
542 * The To-URI and the URI listed in the resource list MUST be the same for a single category SUBSCRIBE request.
545 void sipe_subscribe_presence_single(struct sipe_core_private
*sipe_private
,
550 gchar
*contact
= get_contact(sipe_private
);
552 gchar
*content
= NULL
;
553 const gchar
*additional
= "";
554 const gchar
*content_type
= "";
555 struct sipe_buddy
*sbuddy
= sipe_buddy_find_by_uri(sipe_private
,
558 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
559 content_type
= "Content-Type: application/msrtc-adrl-categorylist+xml\r\n";
560 content
= g_strdup_printf("<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
561 "<action name=\"subscribe\" id=\"63792024\"><adhocList>\n"
562 "<resource uri=\"%s\"%s\n"
564 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
565 "<category name=\"calendarData\"/>\n"
566 "<category name=\"contactCard\"/>\n"
567 "<category name=\"note\"/>\n"
568 "<category name=\"state\"/>\n"
572 sipe_private
->username
,
574 sbuddy
&& sbuddy
->just_added
? "><context/></resource>" : "/>");
576 additional
= "Require: adhoclist, categoryList\r\n" \
577 "Supported: eventlist\r\n";
578 to
= self
= sip_uri_self(sipe_private
);
582 additional
= "Supported: com.microsoft.autoextend\r\n";
588 sbuddy
->just_added
= FALSE
;
590 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"
591 "Supported: ms-piggyback-first-notify\r\n"
592 "%s%sSupported: ms-benotify\r\n"
593 "Proxy-Require: ms-benotify\r\n"
594 "Event: presence\r\n"
601 sipe_subscribe_presence_buddy(sipe_private
, to
, request
, content
);
608 void sipe_subscribe_presence_single_cb(struct sipe_core_private
*sipe_private
,
611 sipe_subscribe_presence_single(sipe_private
, uri
, NULL
);
616 * Support for Batch Category SUBSCRIBE [MS-PRES] - msrtc-event-categories+xml OCS 2007
617 * Support for Batch Category SUBSCRIBE [MS-SIP] - adrl+xml LCS 2005
618 * The user sends an initial batched category SUBSCRIBE request against all contacts on his roaming list in only a request
619 * A batch category SUBSCRIBE request MUST have the same To-URI and From-URI.
620 * This header will be send only if adhoclist there is a "Supported: adhoclist" in REGISTER answer else will be send a Single Category SUBSCRIBE
622 static void sipe_subscribe_presence_batched_to(struct sipe_core_private
*sipe_private
,
623 gchar
*resources_uri
,
626 gchar
*contact
= get_contact(sipe_private
);
629 const gchar
*require
= "";
630 const gchar
*accept
= "";
631 const gchar
*autoextend
= "";
632 const gchar
*content_type
;
634 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
635 require
= ", categoryList";
636 accept
= ", application/msrtc-event-categories+xml, application/xpidf+xml, application/pidf+xml";
637 content_type
= "application/msrtc-adrl-categorylist+xml";
638 content
= g_strdup_printf("<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
639 "<action name=\"subscribe\" id=\"63792024\">\n"
640 "<adhocList>\n%s</adhocList>\n"
641 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
642 "<category name=\"calendarData\"/>\n"
643 "<category name=\"contactCard\"/>\n"
644 "<category name=\"note\"/>\n"
645 "<category name=\"state\"/>\n"
649 sipe_private
->username
,
652 autoextend
= "Supported: com.microsoft.autoextend\r\n";
653 content_type
= "application/adrl+xml";
654 content
= g_strdup_printf("<adhoclist xmlns=\"urn:ietf:params:xml:ns:adrl\" uri=\"sip:%s\" name=\"sip:%s\">\n"
655 "<create xmlns=\"\">\n%s</create>\n"
657 sipe_private
->username
,
658 sipe_private
->username
,
661 g_free(resources_uri
);
663 request
= g_strdup_printf("Require: adhoclist%s\r\n"
664 "Supported: eventlist\r\n"
665 "Accept: application/rlmi+xml, multipart/related, text/xml+msrtc.pidf%s\r\n"
666 "Supported: ms-piggyback-first-notify\r\n"
667 "%sSupported: ms-benotify\r\n"
668 "Proxy-Require: ms-benotify\r\n"
669 "Event: presence\r\n"
670 "Content-Type: %s\r\n"
679 sipe_subscribe_presence_buddy(sipe_private
, to
, request
, content
);
685 struct presence_batched_routed
{
687 const GSList
*buddies
; /* points to subscription->buddies */
690 static void sipe_subscribe_presence_batched_routed_free(gpointer payload
)
692 struct presence_batched_routed
*data
= payload
;
697 static void sipe_subscribe_presence_batched_routed(struct sipe_core_private
*sipe_private
,
700 struct presence_batched_routed
*data
= payload
;
701 const GSList
*buddies
= data
->buddies
;
702 gchar
*resources_uri
= g_strdup("");
704 gchar
*tmp
= resources_uri
;
705 resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"/>\n", tmp
, (char *) buddies
->data
);
707 buddies
= buddies
->next
;
709 sipe_subscribe_presence_batched_to(sipe_private
,
714 static void sipe_subscribe_presence_batched_schedule(struct sipe_core_private
*sipe_private
,
715 const gchar
*action_name
,
720 struct sip_subscription
*subscription
= g_hash_table_lookup(sipe_private
->subscriptions
,
722 struct presence_batched_routed
*payload
= g_malloc(sizeof(struct presence_batched_routed
));
724 if (subscription
->buddies
) {
725 /* merge old and new list */
726 GSList
*entry
= buddies
;
728 subscription
->buddies
= sipe_utils_slist_insert_unique_sorted(subscription
->buddies
,
729 g_strdup(entry
->data
),
730 (GCompareFunc
) g_ascii_strcasecmp
,
734 sipe_utils_slist_free_full(buddies
, g_free
);
736 /* no list yet, simply take ownership of whole list */
737 subscription
->buddies
= buddies
;
740 payload
->host
= g_strdup(who
);
741 payload
->buddies
= subscription
->buddies
;
742 sipe_schedule_seconds(sipe_private
,
746 sipe_subscribe_presence_batched_routed
,
747 sipe_subscribe_presence_batched_routed_free
);
748 SIPE_DEBUG_INFO("Resubscription multiple contacts with batched support & route(%s) in %d", who
, timeout
);
751 static void sipe_subscribe_resource_uri_with_context(const gchar
*name
,
753 gchar
**resources_uri
)
755 struct sipe_buddy
*sbuddy
= (struct sipe_buddy
*)value
;
756 const gchar
*context
= sbuddy
&& sbuddy
->just_added
? "><context/></resource>" : "/>";
757 gchar
*tmp
= *resources_uri
;
759 /* should be enough to include context one time */
761 sbuddy
->just_added
= FALSE
;
763 *resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"%s\n", tmp
, name
, context
);
767 static void sipe_subscribe_resource_uri(const char *name
,
768 SIPE_UNUSED_PARAMETER gpointer value
,
769 gchar
**resources_uri
)
771 gchar
*tmp
= *resources_uri
;
772 *resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"/>\n", tmp
, name
);
777 * A callback for g_hash_table_foreach
779 static void schedule_buddy_resubscription_cb(char *buddy_name
,
780 SIPE_UNUSED_PARAMETER
struct sipe_buddy
*buddy
,
781 struct sipe_core_private
*sipe_private
)
783 guint time_range
= (sipe_buddy_count(sipe_private
) * 1000) / 25; /* time interval for 25 requests per sec. In msec. */
786 * g_hash_table_size() can never return 0, otherwise this function
787 * wouldn't be called :-) But to keep Coverity happy...
790 gchar
*action_name
= sipe_utils_presence_key(buddy_name
);
791 guint timeout
= ((guint
) rand()) / (RAND_MAX
/ time_range
) + 1; /* random period within the range but never 0! */
793 sipe_schedule_mseconds(sipe_private
,
795 g_strdup(buddy_name
),
797 sipe_subscribe_presence_single_cb
,
803 void sipe_subscribe_presence_initial(struct sipe_core_private
*sipe_private
)
806 * Subscribe to buddies, but only do it once.
807 * We'll resubsribe to them based on the Expire field values.
809 if (!SIPE_CORE_PRIVATE_FLAG_IS(SUBSCRIBED_BUDDIES
)) {
811 /* Only try to subscribe if we have any buddies */
812 if (sipe_buddy_count(sipe_private
) > 0) {
813 if (SIPE_CORE_PRIVATE_FLAG_IS(BATCHED_SUPPORT
)) {
814 gchar
*to
= sip_uri_self(sipe_private
);
815 gchar
*resources_uri
= g_strdup("");
816 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
817 sipe_buddy_foreach(sipe_private
,
818 (GHFunc
) sipe_subscribe_resource_uri_with_context
,
821 sipe_buddy_foreach(sipe_private
,
822 (GHFunc
) sipe_subscribe_resource_uri
,
825 sipe_subscribe_presence_batched_to(sipe_private
, resources_uri
, to
);
829 sipe_buddy_foreach(sipe_private
,
830 (GHFunc
) schedule_buddy_resubscription_cb
,
835 SIPE_CORE_PRIVATE_FLAG_SET(SUBSCRIBED_BUDDIES
);
839 void sipe_subscribe_poolfqdn_resource_uri(const char *host
,
841 struct sipe_core_private
*sipe_private
)
843 struct presence_batched_routed
*payload
= g_malloc(sizeof(struct presence_batched_routed
));
844 SIPE_DEBUG_INFO("process_incoming_notify_rlmi_resub: pool(%s)", host
);
845 payload
->host
= g_strdup(host
);
846 payload
->buddies
= server
;
847 sipe_subscribe_presence_batched_routed(sipe_private
,
849 sipe_subscribe_presence_batched_routed_free(payload
);
850 sipe_utils_slist_free_full(server
, g_free
);
855 * subscription expiration handling
857 struct event_subscription_data
{
859 sipe_schedule_action callback
;
863 #define EVENT_OCS2005 0x00000001
864 #define EVENT_OCS2007 0x00000002
866 static const struct event_subscription_data events_table
[] =
869 * For 2007+ it does not make sence to subscribe to:
872 * vnd-microsoft-roaming-ACL
873 * vnd-microsoft-provisioning (not v2)
875 * These are only needed as backward compatibility for older clients
877 * For 2005- we publish our initial statuses only after we received
878 * our existing UserInfo data in response to self subscription.
879 * Only in this case we won't override existing UserInfo data
880 * set earlier or by other client on our behalf.
882 * For 2007+ we publish our initial statuses and calendar data only
883 * after we received our existing publications in roaming_self.
884 * Only in this case we know versions of current publications made
887 { "presence.wpending", sipe_subscribe_presence_wpending
,
889 { "vnd-microsoft-roaming-ACL", sipe_subscribe_roaming_acl
,
891 { "vnd-microsoft-roaming-contacts", sipe_subscribe_roaming_contacts
,
892 EVENT_OCS2005
| EVENT_OCS2007
},
893 { "vnd-microsoft-provisioning", sipe_subscribe_roaming_provisioning
,
895 { "vnd-microsoft-provisioning-v2", sipe_subscribe_roaming_provisioning_v2
,
897 { "vnd-microsoft-roaming-self", sipe_subscribe_roaming_self
,
902 static void sipe_subscription_expiration(struct sipe_core_private
*sipe_private
,
906 const gchar
*expires_header
= sipmsg_find_expires_header(msg
);
907 guint timeout
= expires_header
? strtol(expires_header
, NULL
, 10) : 0;
910 /* 2 min ahead of expiration */
911 if (timeout
> 240) timeout
-= 120;
913 if (sipe_strcase_equal(event
, "presence")) {
914 gchar
*who
= sipmsg_parse_to_address(msg
);
916 if (SIPE_CORE_PRIVATE_FLAG_IS(BATCHED_SUPPORT
)) {
917 sipe_process_presence_timeout(sipe_private
, msg
, who
, timeout
);
919 gchar
*action_name
= sipe_utils_presence_key(who
);
920 sipe_schedule_seconds(sipe_private
,
924 sipe_subscribe_presence_single_cb
,
927 SIPE_DEBUG_INFO("Resubscription single contact '%s' in %d seconds", who
, timeout
);
932 const struct event_subscription_data
*esd
;
934 for (esd
= events_table
; esd
->event
; esd
++) {
935 if (sipe_strcase_equal(event
, esd
->event
)) {
936 gchar
*action_name
= g_strdup_printf("<%s>", event
);
937 sipe_schedule_seconds(sipe_private
,
944 SIPE_DEBUG_INFO("Resubscription to event '%s' in %d seconds", event
, timeout
);
953 * Initial event subscription
955 void sipe_subscription_self_events(struct sipe_core_private
*sipe_private
)
957 const guint mask
= SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
) ? EVENT_OCS2007
: EVENT_OCS2005
;
958 const struct event_subscription_data
*esd
;
960 /* subscribe to those events which are selected for
961 * this version and are allowed by the server */
962 for (esd
= events_table
; esd
->event
; esd
++)
963 if ((esd
->flags
& mask
) &&
964 (g_slist_find_custom(sipe_private
->allowed_events
,
966 (GCompareFunc
) g_ascii_strcasecmp
) != NULL
))
967 (*esd
->callback
)(sipe_private
, NULL
);