Fix #193: Pidgin Status changes stop working (III)
[siplcs.git] / src / core / sipe-subscriptions.c
blob19c2b884529abe1a5372804d4035f6d5cfd72305
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\"/><provisioningGroup name=\"meetingPolicy\"/>"
344 "<provisioningGroup name=\"ucPolicy\"/>"
345 "</provisioningGroupList>");
349 * To request for presence information about the user, access level settings
350 * that have already been configured by the user to control who has access to
351 * what information, and the list of contacts who currently have outstanding
352 * subscriptions.
354 * We wait for (BE)NOTIFY messages with some info change (categories,
355 * containers, subscribers)
357 static void sipe_subscribe_roaming_self(struct sipe_core_private *sipe_private,
358 SIPE_UNUSED_PARAMETER void *unused)
360 sipe_subscribe_self(sipe_private,
361 "vnd-microsoft-roaming-self",
362 "application/vnd-microsoft-roaming-self+xml",
363 "Content-Type: application/vnd-microsoft-roaming-self+xml\r\n",
364 "<roamingList xmlns=\"http://schemas.microsoft.com/2006/09/sip/roaming-self\">"
365 "<roaming type=\"categories\"/>"
366 "<roaming type=\"containers\"/>"
367 "<roaming type=\"subscribers\"/></roamingList>");
370 static void sipe_presence_timeout_mime_cb(gpointer user_data,
371 SIPE_UNUSED_PARAMETER const GSList *fields,
372 const gchar *body,
373 gsize length)
375 GSList **buddies = user_data;
376 sipe_xml *xml = sipe_xml_parse(body, length);
378 if (xml && !sipe_strequal(sipe_xml_name(xml), "list")) {
379 const gchar *uri = sipe_xml_attribute(xml, "uri");
380 const sipe_xml *xn_category;
383 * automaton: presence is never expected to change
385 * see: http://msdn.microsoft.com/en-us/library/ee354295(office.13).aspx
387 for (xn_category = sipe_xml_child(xml, "category");
388 xn_category;
389 xn_category = sipe_xml_twin(xn_category)) {
390 if (sipe_strequal(sipe_xml_attribute(xn_category, "name"),
391 "contactCard")) {
392 const sipe_xml *node = sipe_xml_child(xn_category, "contactCard/automaton");
393 if (node) {
394 char *boolean = sipe_xml_data(node);
395 if (sipe_strequal(boolean, "true")) {
396 SIPE_DEBUG_INFO("sipe_process_presence_timeout: %s is an automaton: - not subscribing to presence updates",
397 uri);
398 uri = NULL;
400 g_free(boolean);
402 break;
406 if (uri) {
407 *buddies = g_slist_append(*buddies, sip_uri(uri));
411 sipe_xml_free(xml);
414 static void sipe_subscribe_presence_batched_schedule(struct sipe_core_private *sipe_private,
415 const gchar *action_name,
416 const gchar *who,
417 GSList *buddies,
418 int timeout);
419 static void sipe_process_presence_timeout(struct sipe_core_private *sipe_private,
420 struct sipmsg *msg,
421 const gchar *who,
422 int timeout)
424 const char *ctype = sipmsg_find_header(msg, "Content-Type");
425 gchar *action_name = sipe_utils_presence_key(who);
427 SIPE_DEBUG_INFO("sipe_process_presence_timeout: Content-Type: %s", ctype ? ctype : "");
429 if (ctype &&
430 strstr(ctype, "multipart") &&
431 (strstr(ctype, "application/rlmi+xml") ||
432 strstr(ctype, "application/msrtc-event-categories+xml"))) {
433 GSList *buddies = NULL;
435 sipe_mime_parts_foreach(ctype, msg->body, sipe_presence_timeout_mime_cb, &buddies);
437 if (buddies)
438 sipe_subscribe_presence_batched_schedule(sipe_private,
439 action_name,
440 who,
441 buddies,
442 timeout);
444 } else {
445 sipe_schedule_seconds(sipe_private,
446 action_name,
447 g_strdup(who),
448 timeout,
449 sipe_subscribe_presence_single_cb,
450 g_free);
451 SIPE_DEBUG_INFO("Resubscription single contact with batched support(%s) in %d seconds", who, timeout);
453 g_free(action_name);
457 * @param expires not respected if set to negative value (E.g. -1)
459 void sipe_subscribe_conference(struct sipe_core_private *sipe_private,
460 const gchar *id,
461 gboolean expires)
463 sipe_subscribe(sipe_private,
465 "conference",
466 "application/conference-info+xml",
467 expires ? "Expires: 0\r\n" : NULL,
468 NULL,
469 NULL);
473 * code for presence subscription
475 static void sipe_subscribe_presence_buddy(struct sipe_core_private *sipe_private,
476 const gchar *uri,
477 const gchar *request,
478 const gchar *body)
480 gchar *key = sipe_utils_presence_key(uri);
482 sip_transport_subscribe(sipe_private,
483 uri,
484 request,
485 body,
486 sipe_subscribe_dialog(sipe_private, key),
487 process_subscribe_response);
489 g_free(key);
493 * if to == NULL: initial single subscription -> send to self URI
495 * if to != NULL:
496 * Single Category SUBSCRIBE [MS-PRES] ; To send when the server returns a 200 OK message with state="resubscribe" in response.
497 * The user sends a single SUBSCRIBE request to the subscribed contact.
498 * The To-URI and the URI listed in the resource list MUST be the same for a single category SUBSCRIBE request.
501 void sipe_subscribe_presence_single(struct sipe_core_private *sipe_private,
502 const gchar *uri,
503 const gchar *to)
505 gchar *self = sip_uri_self(sipe_private);
506 gchar *contact = get_contact(sipe_private);
507 gchar *request;
508 gchar *content = NULL;
509 const gchar *additional;
510 const gchar *content_type = "";
511 struct sipe_buddy *sbuddy = g_hash_table_lookup(sipe_private->buddies, uri);
513 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
514 additional = "Require: adhoclist, categoryList\r\n" \
515 "Supported: eventlist\r\n";
516 content_type = "Content-Type: application/msrtc-adrl-categorylist+xml\r\n";
517 content = g_strdup_printf("<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
518 "<action name=\"subscribe\" id=\"63792024\"><adhocList>\n"
519 "<resource uri=\"%s\"%s\n"
520 "</adhocList>\n"
521 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
522 "<category name=\"calendarData\"/>\n"
523 "<category name=\"contactCard\"/>\n"
524 "<category name=\"note\"/>\n"
525 "<category name=\"state\"/>\n"
526 "</categoryList>\n"
527 "</action>\n"
528 "</batchSub>",
529 sipe_private->username,
530 uri,
531 sbuddy && sbuddy->just_added ? "><context/></resource>" : "/>");
532 } else {
533 additional = "Supported: com.microsoft.autoextend\r\n";
536 if (sbuddy)
537 sbuddy->just_added = FALSE;
539 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"
540 "Supported: ms-piggyback-first-notify\r\n"
541 "%s%sSupported: ms-benotify\r\n"
542 "Proxy-Require: ms-benotify\r\n"
543 "Event: presence\r\n"
544 "Contact: %s\r\n",
545 additional,
546 content_type,
547 contact);
548 g_free(contact);
550 sipe_subscribe_presence_buddy(sipe_private, to ? to : self, request, content);
552 g_free(content);
553 g_free(self);
554 g_free(request);
557 void sipe_subscribe_presence_single_cb(struct sipe_core_private *sipe_private,
558 gpointer uri)
560 sipe_subscribe_presence_single(sipe_private, uri, NULL);
565 * Support for Batch Category SUBSCRIBE [MS-PRES] - msrtc-event-categories+xml OCS 2007
566 * Support for Batch Category SUBSCRIBE [MS-SIP] - adrl+xml LCS 2005
567 * The user sends an initial batched category SUBSCRIBE request against all contacts on his roaming list in only a request
568 * A batch category SUBSCRIBE request MUST have the same To-URI and From-URI.
569 * This header will be send only if adhoclist there is a "Supported: adhoclist" in REGISTER answer else will be send a Single Category SUBSCRIBE
571 static void sipe_subscribe_presence_batched_to(struct sipe_core_private *sipe_private,
572 gchar *resources_uri,
573 gchar *to)
575 gchar *contact = get_contact(sipe_private);
576 gchar *request;
577 gchar *content;
578 const gchar *require = "";
579 const gchar *accept = "";
580 const gchar *autoextend = "";
581 const gchar *content_type;
583 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
584 require = ", categoryList";
585 accept = ", application/msrtc-event-categories+xml, application/xpidf+xml, application/pidf+xml";
586 content_type = "application/msrtc-adrl-categorylist+xml";
587 content = g_strdup_printf("<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
588 "<action name=\"subscribe\" id=\"63792024\">\n"
589 "<adhocList>\n%s</adhocList>\n"
590 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
591 "<category name=\"calendarData\"/>\n"
592 "<category name=\"contactCard\"/>\n"
593 "<category name=\"note\"/>\n"
594 "<category name=\"state\"/>\n"
595 "</categoryList>\n"
596 "</action>\n"
597 "</batchSub>",
598 sipe_private->username,
599 resources_uri);
600 } else {
601 autoextend = "Supported: com.microsoft.autoextend\r\n";
602 content_type = "application/adrl+xml";
603 content = g_strdup_printf("<adhoclist xmlns=\"urn:ietf:params:xml:ns:adrl\" uri=\"sip:%s\" name=\"sip:%s\">\n"
604 "<create xmlns=\"\">\n%s</create>\n"
605 "</adhoclist>\n",
606 sipe_private->username,
607 sipe_private->username,
608 resources_uri);
610 g_free(resources_uri);
612 request = g_strdup_printf("Require: adhoclist%s\r\n"
613 "Supported: eventlist\r\n"
614 "Accept: application/rlmi+xml, multipart/related, text/xml+msrtc.pidf%s\r\n"
615 "Supported: ms-piggyback-first-notify\r\n"
616 "%sSupported: ms-benotify\r\n"
617 "Proxy-Require: ms-benotify\r\n"
618 "Event: presence\r\n"
619 "Content-Type: %s\r\n"
620 "Contact: %s\r\n",
621 require,
622 accept,
623 autoextend,
624 content_type,
625 contact);
626 g_free(contact);
628 sipe_subscribe_presence_buddy(sipe_private, to, request, content);
630 g_free(content);
631 g_free(to);
632 g_free(request);
635 struct presence_batched_routed {
636 gchar *host;
637 const GSList *buddies; /* points to subscription->buddies */
640 static void sipe_subscribe_presence_batched_routed_free(gpointer payload)
642 struct presence_batched_routed *data = payload;
643 g_free(data->host);
644 g_free(payload);
647 static void sipe_subscribe_presence_batched_routed(struct sipe_core_private *sipe_private,
648 gpointer payload)
650 struct presence_batched_routed *data = payload;
651 const GSList *buddies = data->buddies;
652 gchar *resources_uri = g_strdup("");
653 while (buddies) {
654 gchar *tmp = resources_uri;
655 resources_uri = g_strdup_printf("%s<resource uri=\"%s\"/>\n", tmp, (char *) buddies->data);
656 g_free(tmp);
657 buddies = buddies->next;
659 sipe_subscribe_presence_batched_to(sipe_private, resources_uri,
660 g_strdup(data->host));
663 static void sipe_subscribe_presence_batched_schedule(struct sipe_core_private *sipe_private,
664 const gchar *action_name,
665 const gchar *who,
666 GSList *buddies,
667 int timeout)
669 struct sip_subscription *subscription = g_hash_table_lookup(sipe_private->subscriptions,
670 action_name);
671 struct presence_batched_routed *payload = g_malloc(sizeof(struct presence_batched_routed));
673 if (subscription->buddies) {
674 /* merge old and new list */
675 GSList *entry = buddies;
676 while (entry) {
677 subscription->buddies = sipe_utils_slist_insert_unique_sorted(subscription->buddies,
678 g_strdup(entry->data),
679 (GCompareFunc) g_ascii_strcasecmp,
680 g_free);
681 entry = entry->next;
683 sipe_utils_slist_free_full(buddies, g_free);
684 } else {
685 /* no list yet, simply take ownership of whole list */
686 subscription->buddies = buddies;
689 payload->host = g_strdup(who);
690 payload->buddies = subscription->buddies;
691 sipe_schedule_seconds(sipe_private,
692 action_name,
693 payload,
694 timeout,
695 sipe_subscribe_presence_batched_routed,
696 sipe_subscribe_presence_batched_routed_free);
697 SIPE_DEBUG_INFO("Resubscription multiple contacts with batched support & route(%s) in %d", who, timeout);
700 static void sipe_subscribe_resource_uri_with_context(const gchar *name,
701 gpointer value,
702 gchar **resources_uri)
704 struct sipe_buddy *sbuddy = (struct sipe_buddy *)value;
705 gchar *context = sbuddy && sbuddy->just_added ? "><context/></resource>" : "/>";
706 gchar *tmp = *resources_uri;
708 /* should be enough to include context one time */
709 if (sbuddy)
710 sbuddy->just_added = FALSE;
712 *resources_uri = g_strdup_printf("%s<resource uri=\"%s\"%s\n", tmp, name, context);
713 g_free(tmp);
716 static void sipe_subscribe_resource_uri(const char *name,
717 SIPE_UNUSED_PARAMETER gpointer value,
718 gchar **resources_uri)
720 gchar *tmp = *resources_uri;
721 *resources_uri = g_strdup_printf("%s<resource uri=\"%s\"/>\n", tmp, name);
722 g_free(tmp);
725 void sipe_subscribe_presence_batched(struct sipe_core_private *sipe_private)
727 gchar *to = sip_uri_self(sipe_private);
728 gchar *resources_uri = g_strdup("");
729 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
730 g_hash_table_foreach(sipe_private->buddies,
731 (GHFunc) sipe_subscribe_resource_uri_with_context,
732 &resources_uri);
733 } else {
734 g_hash_table_foreach(sipe_private->buddies,
735 (GHFunc) sipe_subscribe_resource_uri,
736 &resources_uri);
738 sipe_subscribe_presence_batched_to(sipe_private, resources_uri, to);
741 void sipe_subscribe_poolfqdn_resource_uri(const char *host,
742 GSList *server,
743 struct sipe_core_private *sipe_private)
745 struct presence_batched_routed *payload = g_malloc(sizeof(struct presence_batched_routed));
746 SIPE_DEBUG_INFO("process_incoming_notify_rlmi_resub: pool(%s)", host);
747 payload->host = g_strdup(host);
748 payload->buddies = server;
749 sipe_subscribe_presence_batched_routed(sipe_private,
750 payload);
751 sipe_subscribe_presence_batched_routed_free(payload);
752 sipe_utils_slist_free_full(server, g_free);
757 * subscription expiration handling
759 struct event_subscription_data {
760 const gchar *event;
761 sipe_schedule_action callback;
762 guint flags;
765 #define EVENT_OCS2005 0x00000001
766 #define EVENT_OCS2007 0x00000002
768 static const struct event_subscription_data events_table[] =
771 * For 2007+ it does not make sence to subscribe to:
773 * presence.wpending
774 * vnd-microsoft-roaming-ACL
775 * vnd-microsoft-provisioning (not v2)
777 * These are only needed as backward compatibility for older clients
779 * For 2005- we publish our initial statuses only after we received
780 * our existing UserInfo data in response to self subscription.
781 * Only in this case we won't override existing UserInfo data
782 * set earlier or by other client on our behalf.
784 * For 2007+ we publish our initial statuses and calendar data only
785 * after we received our existing publications in roaming_self.
786 * Only in this case we know versions of current publications made
787 * on our behalf.
789 { "presence.wpending", sipe_subscribe_presence_wpending,
790 EVENT_OCS2005 },
791 { "vnd-microsoft-roaming-ACL", sipe_subscribe_roaming_acl,
792 EVENT_OCS2005 },
793 { "vnd-microsoft-roaming-contacts", sipe_subscribe_roaming_contacts,
794 EVENT_OCS2005 | EVENT_OCS2007 },
795 { "vnd-microsoft-provisioning", sipe_subscribe_roaming_provisioning,
796 EVENT_OCS2005 },
797 { "vnd-microsoft-provisioning-v2", sipe_subscribe_roaming_provisioning_v2,
798 EVENT_OCS2007 },
799 { "vnd-microsoft-roaming-self", sipe_subscribe_roaming_self,
800 EVENT_OCS2007 },
801 { NULL, NULL, 0 }
804 static void sipe_subscription_expiration(struct sipe_core_private *sipe_private,
805 struct sipmsg *msg,
806 const gchar *event)
808 const gchar *expires_header = sipmsg_find_header(msg, "Expires");
809 guint timeout = expires_header ? strtol(expires_header, NULL, 10) : 0;
811 if (timeout) {
812 /* 2 min ahead of expiration */
813 if (timeout > 240) timeout -= 120;
815 if (sipe_strcase_equal(event, "presence")) {
816 gchar *who = parse_from(sipmsg_find_header(msg, "To"));
818 if (SIPE_CORE_PRIVATE_FLAG_IS(BATCHED_SUPPORT)) {
819 sipe_process_presence_timeout(sipe_private, msg, who, timeout);
820 } else {
821 gchar *action_name = sipe_utils_presence_key(who);
822 sipe_schedule_seconds(sipe_private,
823 action_name,
824 g_strdup(who),
825 timeout,
826 sipe_subscribe_presence_single_cb,
827 g_free);
828 g_free(action_name);
829 SIPE_DEBUG_INFO("Resubscription single contact '%s' in %d seconds", who, timeout);
831 g_free(who);
833 } else {
834 const struct event_subscription_data *esd;
836 for (esd = events_table; esd->event; esd++) {
837 if (sipe_strcase_equal(event, esd->event)) {
838 gchar *action_name = g_strdup_printf("<%s>", event);
839 sipe_schedule_seconds(sipe_private,
840 action_name,
841 NULL,
842 timeout,
843 esd->callback,
844 NULL);
845 g_free(action_name);
846 SIPE_DEBUG_INFO("Resubscription to event '%s' in %d seconds", event, timeout);
847 break;
855 * Initial event subscription
857 void sipe_subscription_self_events(struct sipe_core_private *sipe_private)
859 const guint mask = SIPE_CORE_PRIVATE_FLAG_IS(OCS2007) ? EVENT_OCS2007 : EVENT_OCS2005;
860 const struct event_subscription_data *esd;
862 /* subscribe to those events which are selected for
863 * this version and are allowed by the server */
864 for (esd = events_table; esd->event; esd++)
865 if ((esd->flags & mask) &&
866 (g_slist_find_custom(sipe_private->allowed_events,
867 esd->event,
868 (GCompareFunc) g_ascii_strcasecmp) != NULL))
869 (*esd->callback)(sipe_private, NULL);
873 Local Variables:
874 mode: c
875 c-file-style: "bsd"
876 indent-tabs-mode: t
877 tab-width: 8
878 End: