2 * @file sipe-subscriptions.c
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
28 #include "sipe-common.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"
43 /* RFC3265 subscription */
44 struct sip_subscription
{
45 struct sip_dialog dialog
;
49 static void sipe_subscription_free(struct sip_subscription
*subscription
)
51 if (!subscription
) return;
53 g_free(subscription
->event
);
54 /* NOTE: use cast to prevent BAD_FREE warning from Coverity */
55 sipe_dialog_free((struct sip_dialog
*) subscription
);
58 void sipe_subscriptions_init(struct sipe_core_private
*sipe_private
)
60 sipe_private
->subscriptions
= g_hash_table_new_full(g_str_hash
,
63 (GDestroyNotify
)sipe_subscription_free
);
66 static void sipe_unsubscribe_cb(SIPE_UNUSED_PARAMETER gpointer key
,
67 gpointer value
, gpointer user_data
)
69 struct sip_subscription
*subscription
= value
;
70 struct sip_dialog
*dialog
= &subscription
->dialog
;
71 struct sipe_core_private
*sipe_private
= user_data
;
72 gchar
*contact
= get_contact(sipe_private
);
73 gchar
*hdr
= g_strdup_printf(
76 "Contact: %s\r\n", subscription
->event
, contact
);
79 /* Rate limit to max. 25 requests per seconds */
80 g_usleep(1000000 / 25);
82 sip_transport_subscribe(sipe_private
,
92 void sipe_subscriptions_unsubscribe(struct sipe_core_private
*sipe_private
)
95 g_hash_table_foreach(sipe_private
->subscriptions
,
101 void sipe_subscriptions_destroy(struct sipe_core_private
*sipe_private
)
103 g_hash_table_destroy(sipe_private
->subscriptions
);
106 static void sipe_subscription_remove(struct sipe_core_private
*sipe_private
,
109 if (g_hash_table_lookup(sipe_private
->subscriptions
, key
)) {
110 g_hash_table_remove(sipe_private
->subscriptions
, key
);
111 SIPE_DEBUG_INFO("sipe_subscription_remove: %s", key
);
116 * Generate subscription key
118 * @param event event name (must not by @c NULL)
119 * @param uri presence URI (ignored if @c event != "presence")
121 * @return key string. Must be g_free()'d after use.
123 static gchar
*sipe_subscription_key(const gchar
*event
,
126 if (!g_ascii_strcasecmp(event
, "presence"))
127 /* Subscription is identified by <presence><uri> key */
128 return(sipe_utils_presence_key(uri
));
130 /* Subscription is identified by <event> key */
131 return(g_strdup_printf("<%s>", event
));
134 static gboolean
sipe_subscription_is_allowed(struct sipe_core_private
*sipe_private
,
137 return(g_slist_find_custom(sipe_private
->allowed_events
,
139 (GCompareFunc
) g_ascii_strcasecmp
) != NULL
);
142 static struct sip_dialog
*sipe_subscribe_dialog(struct sipe_core_private
*sipe_private
,
145 struct sip_dialog
*dialog
= g_hash_table_lookup(sipe_private
->subscriptions
,
147 SIPE_DEBUG_INFO("sipe_subscribe_dialog: dialog for '%s' is %s", key
, dialog
? "not NULL" : "NULL");
151 static void sipe_subscription_expiration(struct sipe_core_private
*sipe_private
,
154 static gboolean
process_subscribe_response(struct sipe_core_private
*sipe_private
,
156 struct transaction
*trans
)
158 gchar
*with
= parse_from(sipmsg_find_header(msg
, "To"));
159 const gchar
*event
= sipmsg_find_header(msg
, "Event");
161 /* The case with 2005 Public IM Connectivity (PIC) - no Event header */
163 struct sipmsg
*request_msg
= trans
->msg
;
164 event
= sipmsg_find_header(request_msg
, "Event");
168 const gchar
*subscription_state
= sipmsg_find_header(msg
, "subscription-state");
169 gboolean terminated
= subscription_state
&& strstr(subscription_state
, "terminated");
170 gchar
*key
= sipe_subscription_key(event
, with
);
173 * @TODO: does the server send this only for one-off
174 * subscriptions, i.e. the ones which anyway
178 SIPE_DEBUG_INFO("process_subscribe_response: subscription '%s' to '%s' was terminated",
181 /* 481 Call Leg Does Not Exist */
182 if ((msg
->response
== 481) || terminated
) {
183 sipe_subscription_remove(sipe_private
, key
);
185 /* create/store subscription dialog if not yet */
186 } else if (msg
->response
== 200) {
187 struct sip_dialog
*dialog
= sipe_subscribe_dialog(sipe_private
, key
);
190 struct sip_subscription
*subscription
= g_new0(struct sip_subscription
, 1);
191 g_hash_table_insert(sipe_private
->subscriptions
,
195 subscription
->dialog
.callid
= g_strdup(sipmsg_find_header(msg
, "Call-ID"));
196 subscription
->dialog
.cseq
= sipmsg_parse_cseq(msg
);
197 subscription
->dialog
.with
= g_strdup(with
);
198 subscription
->event
= g_strdup(event
);
200 dialog
= &subscription
->dialog
;
202 SIPE_DEBUG_INFO("process_subscribe_response: subscription dialog added for: %s",
206 sipe_dialog_parse(dialog
, msg
, TRUE
);
213 if (sipmsg_find_header(msg
, "ms-piggyback-cseq")) {
214 process_incoming_notify(sipe_private
, msg
);
216 sipe_subscription_expiration(sipe_private
, msg
, event
);
223 * common subscription code
225 static void sipe_subscribe(struct sipe_core_private
*sipe_private
,
229 const gchar
*addheaders
,
231 struct sip_dialog
*dialog
)
233 gchar
*contact
= get_contact(sipe_private
);
234 gchar
*hdr
= g_strdup_printf(
237 "Supported: com.microsoft.autoextend\r\n"
238 "Supported: ms-benotify\r\n"
239 "Proxy-Require: ms-benotify\r\n"
240 "Supported: ms-piggyback-first-notify\r\n"
245 addheaders
? addheaders
: "",
250 sip_transport_subscribe(sipe_private
,
255 process_subscribe_response
);
261 * common subscription code for self-subscriptions
263 static void sipe_subscribe_self(struct sipe_core_private
*sipe_private
,
266 const gchar
*addheaders
,
269 gchar
*self
= sip_uri_self(sipe_private
);
270 gchar
*key
= sipe_subscription_key(event
, self
);
271 struct sip_dialog
*dialog
= sipe_subscribe_dialog(sipe_private
, key
);
273 sipe_subscribe(sipe_private
,
285 static void sipe_subscribe_presence_wpending(struct sipe_core_private
*sipe_private
,
286 SIPE_UNUSED_PARAMETER
void *unused
)
288 sipe_subscribe_self(sipe_private
,
290 "text/xml+msrtc.wpending",
296 * Subscribe roaming ACL
298 static void sipe_subscribe_roaming_acl(struct sipe_core_private
*sipe_private
)
300 sipe_subscribe_self(sipe_private
,
301 "vnd-microsoft-roaming-ACL",
302 "application/vnd-microsoft-roaming-acls+xml",
308 * Subscribe roaming contacts
310 static void sipe_subscribe_roaming_contacts(struct sipe_core_private
*sipe_private
)
312 sipe_subscribe_self(sipe_private
,
313 "vnd-microsoft-roaming-contacts",
314 "application/vnd-microsoft-roaming-contacts+xml",
322 static void sipe_subscribe_roaming_provisioning(struct sipe_core_private
*sipe_private
)
324 sipe_subscribe_self(sipe_private
,
325 "vnd-microsoft-provisioning",
326 "application/vnd-microsoft-roaming-provisioning+xml",
332 * Subscription for provisioning information to help with initial
333 * configuration. This subscription is a one-time query (denoted by the
334 * Expires header, which asks for 0 seconds for the subscription lifetime).
335 * This subscription asks for server configuration, meeting policies, and
336 * policy settings that Communicator must enforce.
338 static void sipe_subscribe_roaming_provisioning_v2(struct sipe_core_private
*sipe_private
)
340 sipe_subscribe_self(sipe_private
,
341 "vnd-microsoft-provisioning-v2",
342 "application/vnd-microsoft-roaming-provisioning-v2+xml",
344 "Content-Type: application/vnd-microsoft-roaming-provisioning-v2+xml\r\n",
345 "<provisioningGroupList xmlns=\"http://schemas.microsoft.com/2006/09/sip/provisioninggrouplist\">"
346 "<provisioningGroup name=\"ServerConfiguration\"/><provisioningGroup name=\"meetingPolicy\"/>"
347 "<provisioningGroup name=\"ucPolicy\"/>"
348 "</provisioningGroupList>");
352 * To request for presence information about the user, access level settings
353 * that have already been configured by the user to control who has access to
354 * what information, and the list of contacts who currently have outstanding
357 * We wait for (BE)NOTIFY messages with some info change (categories,
358 * containers, subscribers)
360 static void sipe_subscribe_roaming_self(struct sipe_core_private
*sipe_private
)
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 void sipe_subscription_self_events(struct sipe_core_private
*sipe_private
)
374 if (sipe_subscription_is_allowed(sipe_private
,
375 "vnd-microsoft-roaming-contacts"))
376 sipe_subscribe_roaming_contacts(sipe_private
);
379 * For 2007+ it does not make sence to subscribe to:
381 * vnd-microsoft-roaming-ACL
382 * vnd-microsoft-provisioning (not v2)
385 * These are only needed as backward compatibility for older clients
387 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
388 if (sipe_subscription_is_allowed(sipe_private
,
389 "vnd-microsoft-roaming-self"))
390 sipe_subscribe_roaming_self(sipe_private
);
392 if (sipe_subscription_is_allowed(sipe_private
,
393 "vnd-microsoft-provisioning-v2"))
394 sipe_subscribe_roaming_provisioning_v2(sipe_private
);
397 * For 2007+ we publish our initial statuses and calendar data only after
398 * received our existing publications in sipe_process_roaming_self().
399 * Only in this case we know versions of current publications made
404 /* For 2005- servers */
405 //sipe_options_request(sip, sipe_private->public.sip_domain);
407 if (sipe_subscription_is_allowed(sipe_private
,
408 "vnd-microsoft-roaming-ACL"))
409 sipe_subscribe_roaming_acl(sipe_private
);
411 if (sipe_subscription_is_allowed(sipe_private
,
412 "vnd-microsoft-provisioning"))
413 sipe_subscribe_roaming_provisioning(sipe_private
);
415 if (sipe_subscription_is_allowed(sipe_private
,
416 "presence.wpending"))
417 sipe_subscribe_presence_wpending(sipe_private
, NULL
);
420 * For 2005- we publish our initial statuses only after
421 * received our existing UserInfo data in response to
423 * Only in this case we won't override existing UserInfo data
424 * set earlier or by other client on our behalf.
429 static void sipe_presence_timeout_mime_cb(gpointer user_data
,
430 SIPE_UNUSED_PARAMETER
const GSList
*fields
,
434 GSList
**buddies
= user_data
;
435 sipe_xml
*xml
= sipe_xml_parse(body
, length
);
437 if (xml
&& !sipe_strequal(sipe_xml_name(xml
), "list")) {
438 const gchar
*uri
= sipe_xml_attribute(xml
, "uri");
439 const sipe_xml
*xn_category
;
442 * automaton: presence is never expected to change
444 * see: http://msdn.microsoft.com/en-us/library/ee354295(office.13).aspx
446 for (xn_category
= sipe_xml_child(xml
, "category");
448 xn_category
= sipe_xml_twin(xn_category
)) {
449 if (sipe_strequal(sipe_xml_attribute(xn_category
, "name"),
451 const sipe_xml
*node
= sipe_xml_child(xn_category
, "contactCard/automaton");
453 char *boolean
= sipe_xml_data(node
);
454 if (sipe_strequal(boolean
, "true")) {
455 SIPE_DEBUG_INFO("sipe_process_presence_timeout: %s is an automaton: - not subscribing to presence updates",
466 *buddies
= g_slist_append(*buddies
, sip_uri(uri
));
473 static void sipe_process_presence_timeout(struct sipe_core_private
*sipe_private
,
478 const char *ctype
= sipmsg_find_header(msg
, "Content-Type");
479 gchar
*action_name
= sipe_utils_presence_key(who
);
481 SIPE_DEBUG_INFO("sipe_process_presence_timeout: Content-Type: %s", ctype
? ctype
: "");
484 strstr(ctype
, "multipart") &&
485 (strstr(ctype
, "application/rlmi+xml") ||
486 strstr(ctype
, "application/msrtc-event-categories+xml"))) {
487 GSList
*buddies
= NULL
;
489 sipe_mime_parts_foreach(ctype
, msg
->body
, sipe_presence_timeout_mime_cb
, &buddies
);
492 sipe_subscribe_presence_batched_schedule(sipe_private
,
499 sipe_schedule_seconds(sipe_private
,
503 sipe_subscribe_presence_single
,
505 SIPE_DEBUG_INFO("Resubscription single contact with batched support(%s) in %d", who
, timeout
);
510 static void sipe_subscription_expiration(struct sipe_core_private
*sipe_private
,
514 const gchar
*expires_header
= sipmsg_find_header(msg
, "Expires");
515 guint timeout
= expires_header
? strtol(expires_header
, NULL
, 10) : 0;
517 SIPE_DEBUG_INFO("sipe_subscription_expiration: subscription '%s' expires in %d seconds",
521 /* 2 min ahead of expiration */
522 if (timeout
> 240) timeout
-= 120;
524 if (sipe_strcase_equal(event
, "presence.wpending") &&
525 sipe_subscription_is_allowed(sipe_private
,
526 "presence.wpending")) {
527 gchar
*action_name
= g_strdup_printf("<%s>", "presence.wpending");
528 sipe_schedule_seconds(sipe_private
,
532 sipe_subscribe_presence_wpending
,
536 } else if (sipe_strcase_equal(event
, "presence") &&
537 sipe_subscription_is_allowed(sipe_private
,
539 gchar
*who
= parse_from(sipmsg_find_header(msg
, "To"));
540 gchar
*action_name
= sipe_utils_presence_key(who
);
542 if (SIPE_CORE_PRIVATE_FLAG_IS(BATCHED_SUPPORT
)) {
543 sipe_process_presence_timeout(sipe_private
, msg
, who
, timeout
);
545 sipe_schedule_seconds(sipe_private
,
549 sipe_subscribe_presence_single
,
551 SIPE_DEBUG_INFO("Resubscription single contact (%s) in %d", who
, timeout
);
561 * @param expires not respected if set to negative value (E.g. -1)
563 void sipe_subscribe_conference(struct sipe_core_private
*sipe_private
,
567 sipe_subscribe(sipe_private
,
570 "application/conference-info+xml",
571 expires
? "Expires: 0\r\n" : NULL
,
577 * code for presence subscription
579 static void sipe_subscribe_presence_buddy(struct sipe_core_private
*sipe_private
,
581 const gchar
*request
,
584 gchar
*key
= sipe_utils_presence_key(uri
);
586 sip_transport_subscribe(sipe_private
,
590 sipe_subscribe_dialog(sipe_private
, key
),
591 process_subscribe_response
);
597 * Single Category SUBSCRIBE [MS-PRES] ; To send when the server returns a 200 OK message with state="resubscribe" in response.
598 * The user sends a single SUBSCRIBE request to the subscribed contact.
599 * The To-URI and the URI listed in the resource list MUST be the same for a single category SUBSCRIBE request.
602 void sipe_subscribe_presence_single(struct sipe_core_private
*sipe_private
,
605 gchar
*to
= sip_uri((gchar
*)buddy_name
);
606 gchar
*tmp
= get_contact(sipe_private
);
608 gchar
*content
= NULL
;
609 gchar
*autoextend
= "";
610 gchar
*content_type
= "";
611 struct sipe_buddy
*sbuddy
= g_hash_table_lookup(sipe_private
->buddies
, to
);
612 gchar
*context
= sbuddy
&& sbuddy
->just_added
? "><context/></resource>" : "/>";
614 if (sbuddy
) sbuddy
->just_added
= FALSE
;
616 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
617 content_type
= "Content-Type: application/msrtc-adrl-categorylist+xml\r\n";
619 autoextend
= "Supported: com.microsoft.autoextend\r\n";
622 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"
623 "Supported: ms-piggyback-first-notify\r\n"
624 "%s%sSupported: ms-benotify\r\n"
625 "Proxy-Require: ms-benotify\r\n"
626 "Event: presence\r\n"
632 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
633 content
= g_strdup_printf("<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
634 "<action name=\"subscribe\" id=\"63792024\"><adhocList>\n"
635 "<resource uri=\"%s\"%s\n"
637 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
638 "<category name=\"calendarData\"/>\n"
639 "<category name=\"contactCard\"/>\n"
640 "<category name=\"note\"/>\n"
641 "<category name=\"state\"/>\n"
645 sipe_private
->username
,
652 sipe_subscribe_presence_buddy(sipe_private
, to
, request
, content
);
660 * Support for Batch Category SUBSCRIBE [MS-PRES] - msrtc-event-categories+xml OCS 2007
661 * Support for Batch Category SUBSCRIBE [MS-SIP] - adrl+xml LCS 2005
662 * The user sends an initial batched category SUBSCRIBE request against all contacts on his roaming list in only a request
663 * A batch category SUBSCRIBE request MUST have the same To-URI and From-URI.
664 * This header will be send only if adhoclist there is a "Supported: adhoclist" in REGISTER answer else will be send a Single Category SUBSCRIBE
666 static void sipe_subscribe_presence_batched_to(struct sipe_core_private
*sipe_private
,
667 gchar
*resources_uri
,
670 gchar
*contact
= get_contact(sipe_private
);
675 gchar
*autoextend
= "";
678 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
679 require
= ", categoryList";
680 accept
= ", application/msrtc-event-categories+xml, application/xpidf+xml, application/pidf+xml";
681 content_type
= "application/msrtc-adrl-categorylist+xml";
682 content
= g_strdup_printf("<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
683 "<action name=\"subscribe\" id=\"63792024\">\n"
684 "<adhocList>\n%s</adhocList>\n"
685 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
686 "<category name=\"calendarData\"/>\n"
687 "<category name=\"contactCard\"/>\n"
688 "<category name=\"note\"/>\n"
689 "<category name=\"state\"/>\n"
693 sipe_private
->username
,
696 autoextend
= "Supported: com.microsoft.autoextend\r\n";
697 content_type
= "application/adrl+xml";
698 content
= g_strdup_printf("<adhoclist xmlns=\"urn:ietf:params:xml:ns:adrl\" uri=\"sip:%s\" name=\"sip:%s\">\n"
699 "<create xmlns=\"\">\n%s</create>\n"
701 sipe_private
->username
,
702 sipe_private
->username
,
705 g_free(resources_uri
);
707 request
= g_strdup_printf("Require: adhoclist%s\r\n"
708 "Supported: eventlist\r\n"
709 "Accept: application/rlmi+xml, multipart/related, text/xml+msrtc.pidf%s\r\n"
710 "Supported: ms-piggyback-first-notify\r\n"
711 "%sSupported: ms-benotify\r\n"
712 "Proxy-Require: ms-benotify\r\n"
713 "Event: presence\r\n"
714 "Content-Type: %s\r\n"
723 sipe_subscribe_presence_buddy(sipe_private
, to
, request
, content
);
730 struct presence_batched_routed
{
735 static void sipe_subscribe_presence_batched_routed_free(gpointer payload
)
737 struct presence_batched_routed
*data
= payload
;
738 GSList
*buddies
= data
->buddies
;
740 g_free(buddies
->data
);
741 buddies
= buddies
->next
;
743 g_slist_free(data
->buddies
);
748 static void sipe_subscribe_presence_batched_routed(struct sipe_core_private
*sipe_private
,
751 struct presence_batched_routed
*data
= payload
;
752 GSList
*buddies
= data
->buddies
;
753 gchar
*resources_uri
= g_strdup("");
755 gchar
*tmp
= resources_uri
;
756 resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"/>\n", tmp
, (char *) buddies
->data
);
758 buddies
= buddies
->next
;
760 sipe_subscribe_presence_batched_to(sipe_private
, resources_uri
,
761 g_strdup(data
->host
));
764 void sipe_subscribe_presence_batched_schedule(struct sipe_core_private
*sipe_private
,
765 const gchar
*action_name
,
770 struct presence_batched_routed
*payload
= g_malloc(sizeof(struct presence_batched_routed
));
771 payload
->host
= g_strdup(who
);
772 payload
->buddies
= buddies
;
773 sipe_schedule_seconds(sipe_private
,
777 sipe_subscribe_presence_batched_routed
,
778 sipe_subscribe_presence_batched_routed_free
);
779 SIPE_DEBUG_INFO("Resubscription multiple contacts with batched support & route(%s) in %d", who
, timeout
);
782 static void sipe_subscribe_resource_uri_with_context(const gchar
*name
,
784 gchar
**resources_uri
)
786 struct sipe_buddy
*sbuddy
= (struct sipe_buddy
*)value
;
787 gchar
*context
= sbuddy
&& sbuddy
->just_added
? "><context/></resource>" : "/>";
788 gchar
*tmp
= *resources_uri
;
790 if (sbuddy
) sbuddy
->just_added
= FALSE
; /* should be enought to include context one time */
792 *resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"%s\n", tmp
, name
, context
);
796 static void sipe_subscribe_resource_uri(const char *name
,
797 SIPE_UNUSED_PARAMETER gpointer value
,
798 gchar
**resources_uri
)
800 gchar
*tmp
= *resources_uri
;
801 *resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"/>\n", tmp
, name
);
805 void sipe_subscribe_presence_batched(struct sipe_core_private
*sipe_private
)
807 gchar
*to
= sip_uri_self(sipe_private
);
808 gchar
*resources_uri
= g_strdup("");
809 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
810 g_hash_table_foreach(sipe_private
->buddies
, (GHFunc
) sipe_subscribe_resource_uri_with_context
, &resources_uri
);
812 g_hash_table_foreach(sipe_private
->buddies
, (GHFunc
) sipe_subscribe_resource_uri
, &resources_uri
);
815 sipe_subscribe_presence_batched_to(sipe_private
, resources_uri
, to
);
818 void sipe_subscribe_poolfqdn_resource_uri(const char *host
,
820 struct sipe_core_private
*sipe_private
)
822 struct presence_batched_routed
*payload
= g_malloc(sizeof(struct presence_batched_routed
));
823 SIPE_DEBUG_INFO("process_incoming_notify_rlmi_resub: pool(%s)", host
);
824 payload
->host
= g_strdup(host
);
825 payload
->buddies
= server
;
826 sipe_subscribe_presence_batched_routed(sipe_private
,
828 sipe_subscribe_presence_batched_routed_free(payload
);