digest: add support for OpenSSL 1.1.0
[siplcs.git] / src / core / sipe-subscriptions.c
blobd45fe4bbd7cdcbc87019f609c0c21a0f2c64bb95
1 /**
2 * @file sipe-subscriptions.c
4 * pidgin-sipe
6 * Copyright (C) 2010-2016 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-nls.h"
38 #include "sipe-notify.h"
39 #include "sipe-schedule.h"
40 #include "sipe-subscriptions.h"
41 #include "sipe-ucs.h"
42 #include "sipe-utils.h"
43 #include "sipe-xml.h"
45 /* RFC3265 subscription */
46 struct sip_subscription {
47 struct sip_dialog dialog;
48 gchar *event;
49 GSList *buddies; /* batched subscriptions */
52 static void sipe_subscription_free(struct sip_subscription *subscription)
55 if (!subscription) return;
57 g_free(subscription->event);
58 sipe_utils_slist_free_full(subscription->buddies, g_free);
60 /* NOTE: use cast to prevent BAD_FREE warning from Coverity */
61 sipe_dialog_free((struct sip_dialog *) subscription);
64 void sipe_subscriptions_init(struct sipe_core_private *sipe_private)
66 sipe_private->subscriptions = g_hash_table_new_full(g_str_hash,
67 g_str_equal,
68 g_free,
69 (GDestroyNotify)sipe_subscription_free);
72 static void sipe_unsubscribe_cb(SIPE_UNUSED_PARAMETER gpointer key,
73 gpointer value, gpointer user_data)
75 struct sip_subscription *subscription = value;
76 struct sip_dialog *dialog = &subscription->dialog;
77 struct sipe_core_private *sipe_private = user_data;
78 gchar *contact = get_contact(sipe_private);
79 gchar *hdr = g_strdup_printf(
80 "Event: %s\r\n"
81 "Expires: 0\r\n"
82 "Contact: %s\r\n", subscription->event, contact);
83 g_free(contact);
85 /* Rate limit to max. 25 requests per seconds */
86 g_usleep(1000000 / 25);
88 sip_transport_subscribe(sipe_private,
89 dialog->with,
90 hdr,
91 NULL,
92 dialog,
93 NULL);
95 g_free(hdr);
98 void sipe_subscriptions_unsubscribe(struct sipe_core_private *sipe_private)
100 /* unsubscribe all */
101 g_hash_table_foreach(sipe_private->subscriptions,
102 sipe_unsubscribe_cb,
103 sipe_private);
107 void sipe_subscriptions_destroy(struct sipe_core_private *sipe_private)
109 g_hash_table_destroy(sipe_private->subscriptions);
112 static void sipe_subscription_remove(struct sipe_core_private *sipe_private,
113 const gchar *key)
115 if (g_hash_table_lookup(sipe_private->subscriptions, key)) {
116 g_hash_table_remove(sipe_private->subscriptions, key);
117 SIPE_DEBUG_INFO("sipe_subscription_remove: %s", key);
122 * Generate subscription key
124 * @param event event name (must not by @c NULL)
125 * @param uri presence URI (ignored if @c event != "presence")
127 * @return key string. Must be g_free()'d after use.
129 static gchar *sipe_subscription_key(const gchar *event,
130 const gchar *uri)
132 if (!g_ascii_strcasecmp(event, "presence"))
133 /* Subscription is identified by <presence><uri> key */
134 return(sipe_utils_presence_key(uri));
135 else
136 /* Subscription is identified by <event> key */
137 return(g_strdup_printf("<%s>", event));
140 static struct sip_dialog *sipe_subscribe_dialog(struct sipe_core_private *sipe_private,
141 const gchar *key)
143 struct sip_dialog *dialog = g_hash_table_lookup(sipe_private->subscriptions,
144 key);
145 SIPE_DEBUG_INFO("sipe_subscribe_dialog: dialog for '%s' is %s", key, dialog ? "not NULL" : "NULL");
146 return(dialog);
149 static void sipe_subscription_expiration(struct sipe_core_private *sipe_private,
150 struct sipmsg *msg,
151 const gchar *event);
152 static gboolean process_subscribe_response(struct sipe_core_private *sipe_private,
153 struct sipmsg *msg,
154 struct transaction *trans)
156 const gchar *event = sipmsg_find_header(msg, "Event");
158 /* No Event header - error or 2005 Public IM Connectivity (PIC) */
159 if (!event) {
160 struct sipmsg *request_msg = trans->msg;
161 event = sipmsg_find_header(request_msg, "Event");
164 if (event) {
165 gchar *with = parse_from(sipmsg_find_header(msg, "To"));
166 const gchar *subscription_state = sipmsg_find_header(msg, "subscription-state");
167 gboolean terminated = subscription_state && strstr(subscription_state, "terminated");
168 gchar *key = sipe_subscription_key(event, with);
171 * @TODO: does the server send this only for one-off
172 * subscriptions, i.e. the ones which anyway
173 * have "Expires: 0"?
175 if (terminated)
176 SIPE_DEBUG_INFO("process_subscribe_response: subscription '%s' to '%s' was terminated",
177 event, with);
179 /* 400 Bad request */
180 if (msg->response == 400) {
182 SIPE_DEBUG_INFO("process_subscribe_response: subscription '%s' to '%s' failed",
183 event, with);
185 sipe_subscription_remove(sipe_private, key);
187 if (sipe_strcase_equal(event, "presence")) {
188 sipe_backend_notify_error(SIPE_CORE_PUBLIC,
189 _("Presence subscription failed!"),
190 _("One or more buddies will therefore permanently show as offline.\n\nPlease check that there are no corrupted SIP URIs in your contacts list."));
193 /* 481 Call Leg Does Not Exist */
194 } else if ((msg->response == 481) || terminated) {
195 sipe_subscription_remove(sipe_private, key);
197 /* 488 Not acceptable here */
198 } else if (msg->response == 488) {
200 SIPE_DEBUG_INFO("process_subscribe_response: subscription '%s' to '%s' was rejected",
201 event, with);
203 sipe_subscription_remove(sipe_private, key);
205 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007) &&
206 sipe_strcase_equal(event, "vnd-microsoft-roaming-contacts")) {
207 SIPE_DEBUG_INFO_NOFORMAT("no contact list available - assuming Lync 2013+ and Unified Contact Store (UCS)");
208 SIPE_CORE_PRIVATE_FLAG_SET(LYNC2013);
209 sipe_ucs_init(sipe_private, TRUE);
212 /* create/store subscription dialog if not yet */
213 } else if (msg->response == 200) {
214 struct sip_dialog *dialog = sipe_subscribe_dialog(sipe_private, key);
216 if (!dialog) {
217 struct sip_subscription *subscription = g_new0(struct sip_subscription, 1);
219 SIPE_DEBUG_INFO("process_subscribe_response: subscription dialog added for event '%s'",
220 key);
222 g_hash_table_insert(sipe_private->subscriptions,
223 key,
224 subscription);
225 key = NULL; /* table takes ownership of key */
227 subscription->dialog.callid = g_strdup(sipmsg_find_header(msg, "Call-ID"));
228 subscription->dialog.cseq = sipmsg_parse_cseq(msg);
229 subscription->dialog.with = g_strdup(with);
230 subscription->event = g_strdup(event);
232 dialog = &subscription->dialog;
235 sipe_dialog_parse(dialog, msg, TRUE);
237 sipe_subscription_expiration(sipe_private, msg, event);
239 g_free(key);
240 g_free(with);
243 if (sipmsg_find_header(msg, "ms-piggyback-cseq"))
244 process_incoming_notify(sipe_private, msg);
246 return(TRUE);
250 * common subscription code
252 static void sipe_subscribe(struct sipe_core_private *sipe_private,
253 const gchar *uri,
254 const gchar *event,
255 const gchar *accept,
256 const gchar *addheaders,
257 const gchar *body,
258 struct sip_dialog *dialog)
260 gchar *contact = get_contact(sipe_private);
261 gchar *hdr = g_strdup_printf(
262 "Event: %s\r\n"
263 "Accept: %s\r\n"
264 "Supported: com.microsoft.autoextend\r\n"
265 "Supported: ms-benotify\r\n"
266 "Proxy-Require: ms-benotify\r\n"
267 "Supported: ms-piggyback-first-notify\r\n"
268 "%s"
269 "Contact: %s\r\n",
270 event,
271 accept,
272 addheaders ? addheaders : "",
273 contact);
274 g_free(contact);
276 sip_transport_subscribe(sipe_private,
277 uri,
278 hdr,
279 body,
280 dialog,
281 process_subscribe_response);
282 g_free(hdr);
286 * common subscription code for self-subscriptions
288 static void sipe_subscribe_self(struct sipe_core_private *sipe_private,
289 const gchar *event,
290 const gchar *accept,
291 const gchar *addheaders,
292 const gchar *body)
294 gchar *self = sip_uri_self(sipe_private);
295 gchar *key = sipe_subscription_key(event, self);
296 struct sip_dialog *dialog = sipe_subscribe_dialog(sipe_private, key);
298 sipe_subscribe(sipe_private,
299 self,
300 event,
301 accept,
302 addheaders,
303 body,
304 dialog);
305 g_free(key);
306 g_free(self);
309 static void sipe_subscribe_presence_wpending(struct sipe_core_private *sipe_private,
310 SIPE_UNUSED_PARAMETER void *unused)
312 sipe_subscribe_self(sipe_private,
313 "presence.wpending",
314 "text/xml+msrtc.wpending",
315 NULL,
316 NULL);
320 * Subscribe roaming ACL
322 static void sipe_subscribe_roaming_acl(struct sipe_core_private *sipe_private,
323 SIPE_UNUSED_PARAMETER void *unused)
325 sipe_subscribe_self(sipe_private,
326 "vnd-microsoft-roaming-ACL",
327 "application/vnd-microsoft-roaming-acls+xml",
328 NULL,
329 NULL);
333 * Subscribe roaming contacts
335 static void sipe_subscribe_roaming_contacts(struct sipe_core_private *sipe_private,
336 SIPE_UNUSED_PARAMETER void *unused)
338 const gchar *addheaders = NULL;
340 /* indicate that we support Unified Contact Store (UCS) */
341 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007))
342 addheaders = "Supported: ms-ucs\r\n";
343 sipe_subscribe_self(sipe_private,
344 "vnd-microsoft-roaming-contacts",
345 "application/vnd-microsoft-roaming-contacts+xml",
346 addheaders,
347 NULL);
351 * OCS 2005 version
353 static void sipe_subscribe_roaming_provisioning(struct sipe_core_private *sipe_private,
354 SIPE_UNUSED_PARAMETER void *unused)
356 sipe_subscribe_self(sipe_private,
357 "vnd-microsoft-provisioning",
358 "application/vnd-microsoft-roaming-provisioning+xml",
359 "Expires: 0\r\n",
360 NULL);
364 * Subscription for provisioning information to help with initial
365 * configuration. This subscription is a one-time query (denoted by the
366 * Expires header, which asks for 0 seconds for the subscription lifetime).
367 * This subscription asks for server configuration, meeting policies, and
368 * policy settings that Communicator must enforce.
370 static void sipe_subscribe_roaming_provisioning_v2(struct sipe_core_private *sipe_private,
371 SIPE_UNUSED_PARAMETER void *unused)
373 sipe_subscribe_self(sipe_private,
374 "vnd-microsoft-provisioning-v2",
375 "application/vnd-microsoft-roaming-provisioning-v2+xml",
376 "Expires: 0\r\n"
377 "Content-Type: application/vnd-microsoft-roaming-provisioning-v2+xml\r\n",
378 "<provisioningGroupList xmlns=\"http://schemas.microsoft.com/2006/09/sip/provisioninggrouplist\">"
379 " <provisioningGroup name=\"ServerConfiguration\"/>"
380 " <provisioningGroup name=\"meetingPolicy\"/>"
381 " <provisioningGroup name=\"persistentChatConfiguration\"/>"
382 " <provisioningGroup name=\"ucPolicy\"/>"
383 "</provisioningGroupList>");
387 * To request for presence information about the user, access level settings
388 * that have already been configured by the user to control who has access to
389 * what information, and the list of contacts who currently have outstanding
390 * subscriptions.
392 * We wait for (BE)NOTIFY messages with some info change (categories,
393 * containers, subscribers)
395 static void sipe_subscribe_roaming_self(struct sipe_core_private *sipe_private,
396 SIPE_UNUSED_PARAMETER void *unused)
398 sipe_subscribe_self(sipe_private,
399 "vnd-microsoft-roaming-self",
400 "application/vnd-microsoft-roaming-self+xml",
401 "Content-Type: application/vnd-microsoft-roaming-self+xml\r\n",
402 "<roamingList xmlns=\"http://schemas.microsoft.com/2006/09/sip/roaming-self\">"
403 "<roaming type=\"categories\"/>"
404 "<roaming type=\"containers\"/>"
405 "<roaming type=\"subscribers\"/></roamingList>");
408 static void sipe_presence_timeout_mime_cb(gpointer user_data,
409 SIPE_UNUSED_PARAMETER const GSList *fields,
410 const gchar *body,
411 gsize length)
413 GSList **buddies = user_data;
414 sipe_xml *xml = sipe_xml_parse(body, length);
416 if (xml && !sipe_strequal(sipe_xml_name(xml), "list")) {
417 const gchar *uri = sipe_xml_attribute(xml, "uri");
418 const sipe_xml *xn_category;
421 * automaton: presence is never expected to change
423 * see: http://msdn.microsoft.com/en-us/library/ee354295(office.13).aspx
425 for (xn_category = sipe_xml_child(xml, "category");
426 xn_category;
427 xn_category = sipe_xml_twin(xn_category)) {
428 if (sipe_strequal(sipe_xml_attribute(xn_category, "name"),
429 "contactCard")) {
430 const sipe_xml *node = sipe_xml_child(xn_category, "contactCard/automaton");
431 if (node) {
432 char *boolean = sipe_xml_data(node);
433 if (sipe_strequal(boolean, "true")) {
434 SIPE_DEBUG_INFO("sipe_process_presence_timeout: %s is an automaton: - not subscribing to presence updates",
435 uri);
436 uri = NULL;
438 g_free(boolean);
440 break;
444 if (uri) {
445 *buddies = g_slist_append(*buddies, sip_uri(uri));
449 sipe_xml_free(xml);
452 static void sipe_subscribe_presence_batched_schedule(struct sipe_core_private *sipe_private,
453 const gchar *action_name,
454 const gchar *who,
455 GSList *buddies,
456 int timeout);
457 static void sipe_process_presence_timeout(struct sipe_core_private *sipe_private,
458 struct sipmsg *msg,
459 const gchar *who,
460 int timeout)
462 const char *ctype = sipmsg_find_header(msg, "Content-Type");
463 gchar *action_name = sipe_utils_presence_key(who);
465 SIPE_DEBUG_INFO("sipe_process_presence_timeout: Content-Type: %s", ctype ? ctype : "");
467 if (ctype &&
468 strstr(ctype, "multipart") &&
469 (strstr(ctype, "application/rlmi+xml") ||
470 strstr(ctype, "application/msrtc-event-categories+xml"))) {
471 GSList *buddies = NULL;
473 sipe_mime_parts_foreach(ctype, msg->body, sipe_presence_timeout_mime_cb, &buddies);
475 if (buddies)
476 sipe_subscribe_presence_batched_schedule(sipe_private,
477 action_name,
478 who,
479 buddies,
480 timeout);
482 } else {
483 sipe_schedule_seconds(sipe_private,
484 action_name,
485 g_strdup(who),
486 timeout,
487 sipe_subscribe_presence_single_cb,
488 g_free);
489 SIPE_DEBUG_INFO("Resubscription single contact with batched support(%s) in %d seconds", who, timeout);
491 g_free(action_name);
495 * @param expires not respected if set to negative value (E.g. -1)
497 void sipe_subscribe_conference(struct sipe_core_private *sipe_private,
498 const gchar *id,
499 gboolean expires)
501 sipe_subscribe(sipe_private,
503 "conference",
504 "application/conference-info+xml",
505 expires ? "Expires: 0\r\n" : NULL,
506 NULL,
507 NULL);
511 * code for presence subscription
513 static void sipe_subscribe_presence_buddy(struct sipe_core_private *sipe_private,
514 const gchar *uri,
515 const gchar *request,
516 const gchar *body)
518 gchar *key = sipe_utils_presence_key(uri);
520 sip_transport_subscribe(sipe_private,
521 uri,
522 request,
523 body,
524 sipe_subscribe_dialog(sipe_private, key),
525 process_subscribe_response);
527 g_free(key);
531 * if to == NULL: initial single subscription
532 * OCS2005: send to URI
533 * OCS2007: send to self URI
535 * if to != NULL:
536 * Single Category SUBSCRIBE [MS-PRES] ; To send when the server returns a 200 OK message with state="resubscribe" in response.
537 * The user sends a single SUBSCRIBE request to the subscribed contact.
538 * The To-URI and the URI listed in the resource list MUST be the same for a single category SUBSCRIBE request.
541 void sipe_subscribe_presence_single(struct sipe_core_private *sipe_private,
542 const gchar *uri,
543 const gchar *to)
545 gchar *self = NULL;
546 gchar *contact = get_contact(sipe_private);
547 gchar *request;
548 gchar *content = NULL;
549 const gchar *additional = "";
550 const gchar *content_type = "";
551 struct sipe_buddy *sbuddy = sipe_buddy_find_by_uri(sipe_private,
552 uri);
554 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
555 content_type = "Content-Type: application/msrtc-adrl-categorylist+xml\r\n";
556 content = g_strdup_printf("<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
557 "<action name=\"subscribe\" id=\"63792024\"><adhocList>\n"
558 "<resource uri=\"%s\"%s\n"
559 "</adhocList>\n"
560 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
561 "<category name=\"calendarData\"/>\n"
562 "<category name=\"contactCard\"/>\n"
563 "<category name=\"note\"/>\n"
564 "<category name=\"state\"/>\n"
565 "</categoryList>\n"
566 "</action>\n"
567 "</batchSub>",
568 sipe_private->username,
569 uri,
570 sbuddy && sbuddy->just_added ? "><context/></resource>" : "/>");
571 if (!to) {
572 additional = "Require: adhoclist, categoryList\r\n" \
573 "Supported: eventlist\r\n";
574 to = self = sip_uri_self(sipe_private);
577 } else {
578 additional = "Supported: com.microsoft.autoextend\r\n";
579 if (!to)
580 to = uri;
583 if (sbuddy)
584 sbuddy->just_added = FALSE;
586 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"
587 "Supported: ms-piggyback-first-notify\r\n"
588 "%s%sSupported: ms-benotify\r\n"
589 "Proxy-Require: ms-benotify\r\n"
590 "Event: presence\r\n"
591 "Contact: %s\r\n",
592 additional,
593 content_type,
594 contact);
595 g_free(contact);
597 sipe_subscribe_presence_buddy(sipe_private, to, request, content);
599 g_free(content);
600 g_free(self);
601 g_free(request);
604 void sipe_subscribe_presence_single_cb(struct sipe_core_private *sipe_private,
605 gpointer uri)
607 sipe_subscribe_presence_single(sipe_private, uri, NULL);
612 * Support for Batch Category SUBSCRIBE [MS-PRES] - msrtc-event-categories+xml OCS 2007
613 * Support for Batch Category SUBSCRIBE [MS-SIP] - adrl+xml LCS 2005
614 * The user sends an initial batched category SUBSCRIBE request against all contacts on his roaming list in only a request
615 * A batch category SUBSCRIBE request MUST have the same To-URI and From-URI.
616 * This header will be send only if adhoclist there is a "Supported: adhoclist" in REGISTER answer else will be send a Single Category SUBSCRIBE
618 static void sipe_subscribe_presence_batched_to(struct sipe_core_private *sipe_private,
619 gchar *resources_uri,
620 const gchar *to)
622 gchar *contact = get_contact(sipe_private);
623 gchar *request;
624 gchar *content;
625 const gchar *require = "";
626 const gchar *accept = "";
627 const gchar *autoextend = "";
628 const gchar *content_type;
630 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
631 require = ", categoryList";
632 accept = ", application/msrtc-event-categories+xml, application/xpidf+xml, application/pidf+xml";
633 content_type = "application/msrtc-adrl-categorylist+xml";
634 content = g_strdup_printf("<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
635 "<action name=\"subscribe\" id=\"63792024\">\n"
636 "<adhocList>\n%s</adhocList>\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"
642 "</categoryList>\n"
643 "</action>\n"
644 "</batchSub>",
645 sipe_private->username,
646 resources_uri);
647 } else {
648 autoextend = "Supported: com.microsoft.autoextend\r\n";
649 content_type = "application/adrl+xml";
650 content = g_strdup_printf("<adhoclist xmlns=\"urn:ietf:params:xml:ns:adrl\" uri=\"sip:%s\" name=\"sip:%s\">\n"
651 "<create xmlns=\"\">\n%s</create>\n"
652 "</adhoclist>\n",
653 sipe_private->username,
654 sipe_private->username,
655 resources_uri);
657 g_free(resources_uri);
659 request = g_strdup_printf("Require: adhoclist%s\r\n"
660 "Supported: eventlist\r\n"
661 "Accept: application/rlmi+xml, multipart/related, text/xml+msrtc.pidf%s\r\n"
662 "Supported: ms-piggyback-first-notify\r\n"
663 "%sSupported: ms-benotify\r\n"
664 "Proxy-Require: ms-benotify\r\n"
665 "Event: presence\r\n"
666 "Content-Type: %s\r\n"
667 "Contact: %s\r\n",
668 require,
669 accept,
670 autoextend,
671 content_type,
672 contact);
673 g_free(contact);
675 sipe_subscribe_presence_buddy(sipe_private, to, request, content);
677 g_free(content);
678 g_free(request);
681 struct presence_batched_routed {
682 gchar *host;
683 const GSList *buddies; /* points to subscription->buddies */
686 static void sipe_subscribe_presence_batched_routed_free(gpointer payload)
688 struct presence_batched_routed *data = payload;
689 g_free(data->host);
690 g_free(payload);
693 static void sipe_subscribe_presence_batched_routed(struct sipe_core_private *sipe_private,
694 gpointer payload)
696 struct presence_batched_routed *data = payload;
697 const GSList *buddies = data->buddies;
698 gchar *resources_uri = g_strdup("");
699 while (buddies) {
700 gchar *tmp = resources_uri;
701 resources_uri = g_strdup_printf("%s<resource uri=\"%s\"/>\n", tmp, (char *) buddies->data);
702 g_free(tmp);
703 buddies = buddies->next;
705 sipe_subscribe_presence_batched_to(sipe_private,
706 resources_uri,
707 data->host);
710 static void sipe_subscribe_presence_batched_schedule(struct sipe_core_private *sipe_private,
711 const gchar *action_name,
712 const gchar *who,
713 GSList *buddies,
714 int timeout)
716 struct sip_subscription *subscription = g_hash_table_lookup(sipe_private->subscriptions,
717 action_name);
718 struct presence_batched_routed *payload = g_malloc(sizeof(struct presence_batched_routed));
720 if (subscription->buddies) {
721 /* merge old and new list */
722 GSList *entry = buddies;
723 while (entry) {
724 subscription->buddies = sipe_utils_slist_insert_unique_sorted(subscription->buddies,
725 g_strdup(entry->data),
726 (GCompareFunc) g_ascii_strcasecmp,
727 g_free);
728 entry = entry->next;
730 sipe_utils_slist_free_full(buddies, g_free);
731 } else {
732 /* no list yet, simply take ownership of whole list */
733 subscription->buddies = buddies;
736 payload->host = g_strdup(who);
737 payload->buddies = subscription->buddies;
738 sipe_schedule_seconds(sipe_private,
739 action_name,
740 payload,
741 timeout,
742 sipe_subscribe_presence_batched_routed,
743 sipe_subscribe_presence_batched_routed_free);
744 SIPE_DEBUG_INFO("Resubscription multiple contacts with batched support & route(%s) in %d", who, timeout);
747 static void sipe_subscribe_resource_uri_with_context(const gchar *name,
748 gpointer value,
749 gchar **resources_uri)
751 struct sipe_buddy *sbuddy = (struct sipe_buddy *)value;
752 gchar *context = sbuddy && sbuddy->just_added ? "><context/></resource>" : "/>";
753 gchar *tmp = *resources_uri;
755 /* should be enough to include context one time */
756 if (sbuddy)
757 sbuddy->just_added = FALSE;
759 *resources_uri = g_strdup_printf("%s<resource uri=\"%s\"%s\n", tmp, name, context);
760 g_free(tmp);
763 static void sipe_subscribe_resource_uri(const char *name,
764 SIPE_UNUSED_PARAMETER gpointer value,
765 gchar **resources_uri)
767 gchar *tmp = *resources_uri;
768 *resources_uri = g_strdup_printf("%s<resource uri=\"%s\"/>\n", tmp, name);
769 g_free(tmp);
773 * A callback for g_hash_table_foreach
775 static void schedule_buddy_resubscription_cb(char *buddy_name,
776 SIPE_UNUSED_PARAMETER struct sipe_buddy *buddy,
777 struct sipe_core_private *sipe_private)
779 guint time_range = (sipe_buddy_count(sipe_private) * 1000) / 25; /* time interval for 25 requests per sec. In msec. */
782 * g_hash_table_size() can never return 0, otherwise this function
783 * wouldn't be called :-) But to keep Coverity happy...
785 if (time_range) {
786 gchar *action_name = sipe_utils_presence_key(buddy_name);
787 guint timeout = ((guint) rand()) / (RAND_MAX / time_range) + 1; /* random period within the range but never 0! */
789 sipe_schedule_mseconds(sipe_private,
790 action_name,
791 g_strdup(buddy_name),
792 timeout,
793 sipe_subscribe_presence_single_cb,
794 g_free);
795 g_free(action_name);
799 void sipe_subscribe_presence_initial(struct sipe_core_private *sipe_private)
802 * Subscribe to buddies, but only do it once.
803 * We'll resubsribe to them based on the Expire field values.
805 if (!SIPE_CORE_PRIVATE_FLAG_IS(SUBSCRIBED_BUDDIES)) {
807 /* Only try to subscribe if we have any buddies */
808 if (sipe_buddy_count(sipe_private) > 0) {
809 if (SIPE_CORE_PRIVATE_FLAG_IS(BATCHED_SUPPORT)) {
810 gchar *to = sip_uri_self(sipe_private);
811 gchar *resources_uri = g_strdup("");
812 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
813 sipe_buddy_foreach(sipe_private,
814 (GHFunc) sipe_subscribe_resource_uri_with_context,
815 &resources_uri);
816 } else {
817 sipe_buddy_foreach(sipe_private,
818 (GHFunc) sipe_subscribe_resource_uri,
819 &resources_uri);
821 sipe_subscribe_presence_batched_to(sipe_private, resources_uri, to);
822 g_free(to);
824 } else {
825 sipe_buddy_foreach(sipe_private,
826 (GHFunc) schedule_buddy_resubscription_cb,
827 sipe_private);
831 SIPE_CORE_PRIVATE_FLAG_SET(SUBSCRIBED_BUDDIES);
835 void sipe_subscribe_poolfqdn_resource_uri(const char *host,
836 GSList *server,
837 struct sipe_core_private *sipe_private)
839 struct presence_batched_routed *payload = g_malloc(sizeof(struct presence_batched_routed));
840 SIPE_DEBUG_INFO("process_incoming_notify_rlmi_resub: pool(%s)", host);
841 payload->host = g_strdup(host);
842 payload->buddies = server;
843 sipe_subscribe_presence_batched_routed(sipe_private,
844 payload);
845 sipe_subscribe_presence_batched_routed_free(payload);
846 sipe_utils_slist_free_full(server, g_free);
851 * subscription expiration handling
853 struct event_subscription_data {
854 const gchar *event;
855 sipe_schedule_action callback;
856 guint flags;
859 #define EVENT_OCS2005 0x00000001
860 #define EVENT_OCS2007 0x00000002
862 static const struct event_subscription_data events_table[] =
865 * For 2007+ it does not make sence to subscribe to:
867 * presence.wpending
868 * vnd-microsoft-roaming-ACL
869 * vnd-microsoft-provisioning (not v2)
871 * These are only needed as backward compatibility for older clients
873 * For 2005- we publish our initial statuses only after we received
874 * our existing UserInfo data in response to self subscription.
875 * Only in this case we won't override existing UserInfo data
876 * set earlier or by other client on our behalf.
878 * For 2007+ we publish our initial statuses and calendar data only
879 * after we received our existing publications in roaming_self.
880 * Only in this case we know versions of current publications made
881 * on our behalf.
883 { "presence.wpending", sipe_subscribe_presence_wpending,
884 EVENT_OCS2005 },
885 { "vnd-microsoft-roaming-ACL", sipe_subscribe_roaming_acl,
886 EVENT_OCS2005 },
887 { "vnd-microsoft-roaming-contacts", sipe_subscribe_roaming_contacts,
888 EVENT_OCS2005 | EVENT_OCS2007 },
889 { "vnd-microsoft-provisioning", sipe_subscribe_roaming_provisioning,
890 EVENT_OCS2005 },
891 { "vnd-microsoft-provisioning-v2", sipe_subscribe_roaming_provisioning_v2,
892 EVENT_OCS2007 },
893 { "vnd-microsoft-roaming-self", sipe_subscribe_roaming_self,
894 EVENT_OCS2007 },
895 { NULL, NULL, 0 }
898 static void sipe_subscription_expiration(struct sipe_core_private *sipe_private,
899 struct sipmsg *msg,
900 const gchar *event)
902 const gchar *expires_header = sipmsg_find_header(msg, "Expires");
903 guint timeout = expires_header ? strtol(expires_header, NULL, 10) : 0;
905 if (timeout) {
906 /* 2 min ahead of expiration */
907 if (timeout > 240) timeout -= 120;
909 if (sipe_strcase_equal(event, "presence")) {
910 gchar *who = parse_from(sipmsg_find_header(msg, "To"));
912 if (SIPE_CORE_PRIVATE_FLAG_IS(BATCHED_SUPPORT)) {
913 sipe_process_presence_timeout(sipe_private, msg, who, timeout);
914 } else {
915 gchar *action_name = sipe_utils_presence_key(who);
916 sipe_schedule_seconds(sipe_private,
917 action_name,
918 g_strdup(who),
919 timeout,
920 sipe_subscribe_presence_single_cb,
921 g_free);
922 g_free(action_name);
923 SIPE_DEBUG_INFO("Resubscription single contact '%s' in %d seconds", who, timeout);
925 g_free(who);
927 } else {
928 const struct event_subscription_data *esd;
930 for (esd = events_table; esd->event; esd++) {
931 if (sipe_strcase_equal(event, esd->event)) {
932 gchar *action_name = g_strdup_printf("<%s>", event);
933 sipe_schedule_seconds(sipe_private,
934 action_name,
935 NULL,
936 timeout,
937 esd->callback,
938 NULL);
939 g_free(action_name);
940 SIPE_DEBUG_INFO("Resubscription to event '%s' in %d seconds", event, timeout);
941 break;
949 * Initial event subscription
951 void sipe_subscription_self_events(struct sipe_core_private *sipe_private)
953 const guint mask = SIPE_CORE_PRIVATE_FLAG_IS(OCS2007) ? EVENT_OCS2007 : EVENT_OCS2005;
954 const struct event_subscription_data *esd;
956 /* subscribe to those events which are selected for
957 * this version and are allowed by the server */
958 for (esd = events_table; esd->event; esd++)
959 if ((esd->flags & mask) &&
960 (g_slist_find_custom(sipe_private->allowed_events,
961 esd->event,
962 (GCompareFunc) g_ascii_strcasecmp) != NULL))
963 (*esd->callback)(sipe_private, NULL);
967 Local Variables:
968 mode: c
969 c-file-style: "bsd"
970 indent-tabs-mode: t
971 tab-width: 8
972 End: