subscriptions: implement event timeouts
[siplcs.git] / src / core / sipe-subscriptions.c
blob016b4ea5554f0dfcc46f255572acfd48960afd98
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;
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,
61 g_str_equal,
62 g_free,
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(
74 "Event: %s\r\n"
75 "Expires: 0\r\n"
76 "Contact: %s\r\n", subscription->event, contact);
77 g_free(contact);
79 /* Rate limit to max. 25 requests per seconds */
80 g_usleep(1000000 / 25);
82 sip_transport_subscribe(sipe_private,
83 dialog->with,
84 hdr,
85 NULL,
86 dialog,
87 NULL);
89 g_free(hdr);
92 void sipe_subscriptions_unsubscribe(struct sipe_core_private *sipe_private)
94 /* unsubscribe all */
95 g_hash_table_foreach(sipe_private->subscriptions,
96 sipe_unsubscribe_cb,
97 sipe_private);
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,
107 const gchar *key)
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,
124 const gchar *uri)
126 if (!g_ascii_strcasecmp(event, "presence"))
127 /* Subscription is identified by <presence><uri> key */
128 return(sipe_utils_presence_key(uri));
129 else
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,
135 const gchar *event)
137 return(g_slist_find_custom(sipe_private->allowed_events,
138 event,
139 (GCompareFunc) g_ascii_strcasecmp) != NULL);
142 static struct sip_dialog *sipe_subscribe_dialog(struct sipe_core_private *sipe_private,
143 const gchar *key)
145 struct sip_dialog *dialog = g_hash_table_lookup(sipe_private->subscriptions,
146 key);
147 SIPE_DEBUG_INFO("sipe_subscribe_dialog: dialog for '%s' is %s", key, dialog ? "not NULL" : "NULL");
148 return(dialog);
151 static void sipe_subscription_expiration(struct sipe_core_private *sipe_private,
152 struct sipmsg *msg,
153 const gchar *event);
154 static gboolean process_subscribe_response(struct sipe_core_private *sipe_private,
155 struct sipmsg *msg,
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 */
162 if (!event) {
163 struct sipmsg *request_msg = trans->msg;
164 event = sipmsg_find_header(request_msg, "Event");
167 if (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
175 * have "Expires: 0"?
177 if (terminated)
178 SIPE_DEBUG_INFO("process_subscribe_response: subscription '%s' to '%s' was terminated",
179 event, with);
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);
189 if (!dialog) {
190 struct sip_subscription *subscription = g_new0(struct sip_subscription, 1);
192 SIPE_DEBUG_INFO("process_subscribe_response: subscription dialog added for event '%s'",
193 key);
195 g_hash_table_insert(sipe_private->subscriptions,
196 key,
197 subscription);
198 key = NULL; /* table takes ownership of key */
200 subscription->dialog.callid = g_strdup(sipmsg_find_header(msg, "Call-ID"));
201 subscription->dialog.cseq = sipmsg_parse_cseq(msg);
202 subscription->dialog.with = g_strdup(with);
203 subscription->event = g_strdup(event);
205 dialog = &subscription->dialog;
208 sipe_dialog_parse(dialog, msg, TRUE);
210 sipe_subscription_expiration(sipe_private, msg, event);
212 g_free(key);
214 g_free(with);
216 if (sipmsg_find_header(msg, "ms-piggyback-cseq"))
217 process_incoming_notify(sipe_private, msg);
219 return(TRUE);
223 * common subscription code
225 static void sipe_subscribe(struct sipe_core_private *sipe_private,
226 const gchar *uri,
227 const gchar *event,
228 const gchar *accept,
229 const gchar *addheaders,
230 const gchar *body,
231 struct sip_dialog *dialog)
233 gchar *contact = get_contact(sipe_private);
234 gchar *hdr = g_strdup_printf(
235 "Event: %s\r\n"
236 "Accept: %s\r\n"
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"
241 "%s"
242 "Contact: %s\r\n",
243 event,
244 accept,
245 addheaders ? addheaders : "",
246 contact);
247 g_free(contact);
249 sip_transport_subscribe(sipe_private,
250 uri,
251 hdr,
252 body,
253 dialog,
254 process_subscribe_response);
255 g_free(hdr);
259 * common subscription code for self-subscriptions
261 static void sipe_subscribe_self(struct sipe_core_private *sipe_private,
262 const gchar *event,
263 const gchar *accept,
264 const gchar *addheaders,
265 const gchar *body)
267 gchar *self = sip_uri_self(sipe_private);
268 gchar *key = sipe_subscription_key(event, self);
269 struct sip_dialog *dialog = sipe_subscribe_dialog(sipe_private, key);
271 sipe_subscribe(sipe_private,
272 self,
273 event,
274 accept,
275 addheaders,
276 body,
277 dialog);
278 g_free(key);
279 g_free(self);
282 static void sipe_subscribe_presence_wpending(struct sipe_core_private *sipe_private,
283 SIPE_UNUSED_PARAMETER void *unused)
285 sipe_subscribe_self(sipe_private,
286 "presence.wpending",
287 "text/xml+msrtc.wpending",
288 NULL,
289 NULL);
293 * Subscribe roaming ACL
295 static void sipe_subscribe_roaming_acl(struct sipe_core_private *sipe_private,
296 SIPE_UNUSED_PARAMETER void *unused)
298 sipe_subscribe_self(sipe_private,
299 "vnd-microsoft-roaming-ACL",
300 "application/vnd-microsoft-roaming-acls+xml",
301 NULL,
302 NULL);
306 * Subscribe roaming contacts
308 static void sipe_subscribe_roaming_contacts(struct sipe_core_private *sipe_private,
309 SIPE_UNUSED_PARAMETER void *unused)
311 sipe_subscribe_self(sipe_private,
312 "vnd-microsoft-roaming-contacts",
313 "application/vnd-microsoft-roaming-contacts+xml",
314 NULL,
315 NULL);
319 * OCS 2005 version
321 static void sipe_subscribe_roaming_provisioning(struct sipe_core_private *sipe_private,
322 SIPE_UNUSED_PARAMETER void *unused)
324 sipe_subscribe_self(sipe_private,
325 "vnd-microsoft-provisioning",
326 "application/vnd-microsoft-roaming-provisioning+xml",
327 "Expires: 0\r\n",
328 NULL);
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,
339 SIPE_UNUSED_PARAMETER void *unused)
341 sipe_subscribe_self(sipe_private,
342 "vnd-microsoft-provisioning-v2",
343 "application/vnd-microsoft-roaming-provisioning-v2+xml",
344 "Expires: 0\r\n"
345 "Content-Type: application/vnd-microsoft-roaming-provisioning-v2+xml\r\n",
346 "<provisioningGroupList xmlns=\"http://schemas.microsoft.com/2006/09/sip/provisioninggrouplist\">"
347 "<provisioningGroup name=\"ServerConfiguration\"/><provisioningGroup name=\"meetingPolicy\"/>"
348 "<provisioningGroup name=\"ucPolicy\"/>"
349 "</provisioningGroupList>");
353 * To request for presence information about the user, access level settings
354 * that have already been configured by the user to control who has access to
355 * what information, and the list of contacts who currently have outstanding
356 * subscriptions.
358 * We wait for (BE)NOTIFY messages with some info change (categories,
359 * containers, subscribers)
361 static void sipe_subscribe_roaming_self(struct sipe_core_private *sipe_private,
362 SIPE_UNUSED_PARAMETER void *unused)
364 sipe_subscribe_self(sipe_private,
365 "vnd-microsoft-roaming-self",
366 "application/vnd-microsoft-roaming-self+xml",
367 "Content-Type: application/vnd-microsoft-roaming-self+xml\r\n",
368 "<roamingList xmlns=\"http://schemas.microsoft.com/2006/09/sip/roaming-self\">"
369 "<roaming type=\"categories\"/>"
370 "<roaming type=\"containers\"/>"
371 "<roaming type=\"subscribers\"/></roamingList>");
374 void sipe_subscription_self_events(struct sipe_core_private *sipe_private)
376 if (sipe_subscription_is_allowed(sipe_private,
377 "vnd-microsoft-roaming-contacts"))
378 sipe_subscribe_roaming_contacts(sipe_private, NULL);
381 * For 2007+ it does not make sence to subscribe to:
383 * vnd-microsoft-roaming-ACL
384 * vnd-microsoft-provisioning (not v2)
385 * presence.wpending
387 * These are only needed as backward compatibility for older clients
389 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
390 if (sipe_subscription_is_allowed(sipe_private,
391 "vnd-microsoft-roaming-self"))
392 sipe_subscribe_roaming_self(sipe_private, NULL);
394 if (sipe_subscription_is_allowed(sipe_private,
395 "vnd-microsoft-provisioning-v2"))
396 sipe_subscribe_roaming_provisioning_v2(sipe_private, NULL);
399 * For 2007+ we publish our initial statuses and calendar data only after
400 * received our existing publications in sipe_process_roaming_self().
401 * Only in this case we know versions of current publications made
402 * on our behalf.
405 } else {
406 /* For 2005- servers */
407 //sipe_options_request(sip, sipe_private->public.sip_domain);
409 if (sipe_subscription_is_allowed(sipe_private,
410 "vnd-microsoft-roaming-ACL"))
411 sipe_subscribe_roaming_acl(sipe_private, NULL);
413 if (sipe_subscription_is_allowed(sipe_private,
414 "vnd-microsoft-provisioning"))
415 sipe_subscribe_roaming_provisioning(sipe_private, NULL);
417 if (sipe_subscription_is_allowed(sipe_private,
418 "presence.wpending"))
419 sipe_subscribe_presence_wpending(sipe_private, NULL);
422 * For 2005- we publish our initial statuses only after
423 * received our existing UserInfo data in response to
424 * self subscription.
425 * Only in this case we won't override existing UserInfo data
426 * set earlier or by other client on our behalf.
431 static void sipe_presence_timeout_mime_cb(gpointer user_data,
432 SIPE_UNUSED_PARAMETER const GSList *fields,
433 const gchar *body,
434 gsize length)
436 GSList **buddies = user_data;
437 sipe_xml *xml = sipe_xml_parse(body, length);
439 if (xml && !sipe_strequal(sipe_xml_name(xml), "list")) {
440 const gchar *uri = sipe_xml_attribute(xml, "uri");
441 const sipe_xml *xn_category;
444 * automaton: presence is never expected to change
446 * see: http://msdn.microsoft.com/en-us/library/ee354295(office.13).aspx
448 for (xn_category = sipe_xml_child(xml, "category");
449 xn_category;
450 xn_category = sipe_xml_twin(xn_category)) {
451 if (sipe_strequal(sipe_xml_attribute(xn_category, "name"),
452 "contactCard")) {
453 const sipe_xml *node = sipe_xml_child(xn_category, "contactCard/automaton");
454 if (node) {
455 char *boolean = sipe_xml_data(node);
456 if (sipe_strequal(boolean, "true")) {
457 SIPE_DEBUG_INFO("sipe_process_presence_timeout: %s is an automaton: - not subscribing to presence updates",
458 uri);
459 uri = NULL;
461 g_free(boolean);
463 break;
467 if (uri) {
468 *buddies = g_slist_append(*buddies, sip_uri(uri));
472 sipe_xml_free(xml);
475 static void sipe_process_presence_timeout(struct sipe_core_private *sipe_private,
476 struct sipmsg *msg,
477 const gchar *who,
478 int timeout)
480 const char *ctype = sipmsg_find_header(msg, "Content-Type");
481 gchar *action_name = sipe_utils_presence_key(who);
483 SIPE_DEBUG_INFO("sipe_process_presence_timeout: Content-Type: %s", ctype ? ctype : "");
485 if (ctype &&
486 strstr(ctype, "multipart") &&
487 (strstr(ctype, "application/rlmi+xml") ||
488 strstr(ctype, "application/msrtc-event-categories+xml"))) {
489 GSList *buddies = NULL;
491 sipe_mime_parts_foreach(ctype, msg->body, sipe_presence_timeout_mime_cb, &buddies);
493 if (buddies)
494 sipe_subscribe_presence_batched_schedule(sipe_private,
495 action_name,
496 who,
497 buddies,
498 timeout);
500 } else {
501 sipe_schedule_seconds(sipe_private,
502 action_name,
503 g_strdup(who),
504 timeout,
505 sipe_subscribe_presence_single,
506 g_free);
507 SIPE_DEBUG_INFO("Resubscription single contact with batched support(%s) in %d seconds", who, timeout);
509 g_free(action_name);
513 * @param expires not respected if set to negative value (E.g. -1)
515 void sipe_subscribe_conference(struct sipe_core_private *sipe_private,
516 const gchar *id,
517 gboolean expires)
519 sipe_subscribe(sipe_private,
521 "conference",
522 "application/conference-info+xml",
523 expires ? "Expires: 0\r\n" : NULL,
524 NULL,
525 NULL);
529 * code for presence subscription
531 static void sipe_subscribe_presence_buddy(struct sipe_core_private *sipe_private,
532 const gchar *uri,
533 const gchar *request,
534 const gchar *body)
536 gchar *key = sipe_utils_presence_key(uri);
538 sip_transport_subscribe(sipe_private,
539 uri,
540 request,
541 body,
542 sipe_subscribe_dialog(sipe_private, key),
543 process_subscribe_response);
545 g_free(key);
549 * Single Category SUBSCRIBE [MS-PRES] ; To send when the server returns a 200 OK message with state="resubscribe" in response.
550 * The user sends a single SUBSCRIBE request to the subscribed contact.
551 * The To-URI and the URI listed in the resource list MUST be the same for a single category SUBSCRIBE request.
554 void sipe_subscribe_presence_single(struct sipe_core_private *sipe_private,
555 gpointer buddy_name)
557 gchar *to = sip_uri((gchar *)buddy_name);
558 gchar *tmp = get_contact(sipe_private);
559 gchar *request;
560 gchar *content = NULL;
561 gchar *autoextend = "";
562 gchar *content_type = "";
563 struct sipe_buddy *sbuddy = g_hash_table_lookup(sipe_private->buddies, to);
564 gchar *context = sbuddy && sbuddy->just_added ? "><context/></resource>" : "/>";
566 if (sbuddy) sbuddy->just_added = FALSE;
568 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
569 content_type = "Content-Type: application/msrtc-adrl-categorylist+xml\r\n";
570 } else {
571 autoextend = "Supported: com.microsoft.autoextend\r\n";
574 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"
575 "Supported: ms-piggyback-first-notify\r\n"
576 "%s%sSupported: ms-benotify\r\n"
577 "Proxy-Require: ms-benotify\r\n"
578 "Event: presence\r\n"
579 "Contact: %s\r\n",
580 autoextend,
581 content_type,
582 tmp);
584 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
585 content = g_strdup_printf("<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
586 "<action name=\"subscribe\" id=\"63792024\"><adhocList>\n"
587 "<resource uri=\"%s\"%s\n"
588 "</adhocList>\n"
589 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
590 "<category name=\"calendarData\"/>\n"
591 "<category name=\"contactCard\"/>\n"
592 "<category name=\"note\"/>\n"
593 "<category name=\"state\"/>\n"
594 "</categoryList>\n"
595 "</action>\n"
596 "</batchSub>",
597 sipe_private->username,
599 context);
602 g_free(tmp);
604 sipe_subscribe_presence_buddy(sipe_private, to, request, content);
606 g_free(content);
607 g_free(to);
608 g_free(request);
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 gchar *to)
622 gchar *contact = get_contact(sipe_private);
623 gchar *request;
624 gchar *content;
625 gchar *require = "";
626 gchar *accept = "";
627 gchar *autoextend = "";
628 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(to);
679 g_free(request);
682 struct presence_batched_routed {
683 gchar *host;
684 GSList *buddies;
687 static void sipe_subscribe_presence_batched_routed_free(gpointer payload)
689 struct presence_batched_routed *data = payload;
690 GSList *buddies = data->buddies;
691 while (buddies) {
692 g_free(buddies->data);
693 buddies = buddies->next;
695 g_slist_free(data->buddies);
696 g_free(data->host);
697 g_free(payload);
700 static void sipe_subscribe_presence_batched_routed(struct sipe_core_private *sipe_private,
701 gpointer payload)
703 struct presence_batched_routed *data = payload;
704 GSList *buddies = data->buddies;
705 gchar *resources_uri = g_strdup("");
706 while (buddies) {
707 gchar *tmp = resources_uri;
708 resources_uri = g_strdup_printf("%s<resource uri=\"%s\"/>\n", tmp, (char *) buddies->data);
709 g_free(tmp);
710 buddies = buddies->next;
712 sipe_subscribe_presence_batched_to(sipe_private, resources_uri,
713 g_strdup(data->host));
716 void sipe_subscribe_presence_batched_schedule(struct sipe_core_private *sipe_private,
717 const gchar *action_name,
718 const gchar *who,
719 GSList *buddies,
720 int timeout)
722 struct presence_batched_routed *payload = g_malloc(sizeof(struct presence_batched_routed));
723 payload->host = g_strdup(who);
724 payload->buddies = buddies;
725 sipe_schedule_seconds(sipe_private,
726 action_name,
727 payload,
728 timeout,
729 sipe_subscribe_presence_batched_routed,
730 sipe_subscribe_presence_batched_routed_free);
731 SIPE_DEBUG_INFO("Resubscription multiple contacts with batched support & route(%s) in %d", who, timeout);
734 static void sipe_subscribe_resource_uri_with_context(const gchar *name,
735 gpointer value,
736 gchar **resources_uri)
738 struct sipe_buddy *sbuddy = (struct sipe_buddy *)value;
739 gchar *context = sbuddy && sbuddy->just_added ? "><context/></resource>" : "/>";
740 gchar *tmp = *resources_uri;
742 if (sbuddy) sbuddy->just_added = FALSE; /* should be enought to include context one time */
744 *resources_uri = g_strdup_printf("%s<resource uri=\"%s\"%s\n", tmp, name, context);
745 g_free(tmp);
748 static void sipe_subscribe_resource_uri(const char *name,
749 SIPE_UNUSED_PARAMETER gpointer value,
750 gchar **resources_uri)
752 gchar *tmp = *resources_uri;
753 *resources_uri = g_strdup_printf("%s<resource uri=\"%s\"/>\n", tmp, name);
754 g_free(tmp);
757 void sipe_subscribe_presence_batched(struct sipe_core_private *sipe_private)
759 gchar *to = sip_uri_self(sipe_private);
760 gchar *resources_uri = g_strdup("");
761 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
762 g_hash_table_foreach(sipe_private->buddies, (GHFunc) sipe_subscribe_resource_uri_with_context , &resources_uri);
763 } else {
764 g_hash_table_foreach(sipe_private->buddies, (GHFunc) sipe_subscribe_resource_uri, &resources_uri);
767 sipe_subscribe_presence_batched_to(sipe_private, resources_uri, to);
770 void sipe_subscribe_poolfqdn_resource_uri(const char *host,
771 GSList *server,
772 struct sipe_core_private *sipe_private)
774 struct presence_batched_routed *payload = g_malloc(sizeof(struct presence_batched_routed));
775 SIPE_DEBUG_INFO("process_incoming_notify_rlmi_resub: pool(%s)", host);
776 payload->host = g_strdup(host);
777 payload->buddies = server;
778 sipe_subscribe_presence_batched_routed(sipe_private,
779 payload);
780 sipe_subscribe_presence_batched_routed_free(payload);
785 * subscription expiration handling
787 struct event_subscription_data {
788 const gchar *event;
789 sipe_schedule_action callback;
792 static const struct event_subscription_data events_table[] =
794 { "presence.wpending", sipe_subscribe_presence_wpending },
795 { "vnd-microsoft-roaming-ACL", sipe_subscribe_roaming_acl },
796 { "vnd-microsoft-roaming-contacts", sipe_subscribe_roaming_contacts },
797 { "vnd-microsoft-provisioning", sipe_subscribe_roaming_provisioning },
798 { "vnd-microsoft-provisioning-v2", sipe_subscribe_roaming_provisioning_v2 },
799 { "vnd-microsoft-roaming-self", sipe_subscribe_roaming_self },
800 { NULL, NULL }
803 static void sipe_subscription_expiration(struct sipe_core_private *sipe_private,
804 struct sipmsg *msg,
805 const gchar *event)
807 const gchar *expires_header = sipmsg_find_header(msg, "Expires");
808 guint timeout = expires_header ? strtol(expires_header, NULL, 10) : 0;
810 if (timeout) {
811 /* 2 min ahead of expiration */
812 if (timeout > 240) timeout -= 120;
814 if (sipe_strcase_equal(event, "presence")) {
815 gchar *who = parse_from(sipmsg_find_header(msg, "To"));
817 if (SIPE_CORE_PRIVATE_FLAG_IS(BATCHED_SUPPORT)) {
818 sipe_process_presence_timeout(sipe_private, msg, who, timeout);
819 } else {
820 gchar *action_name = sipe_utils_presence_key(who);
821 sipe_schedule_seconds(sipe_private,
822 action_name,
823 g_strdup(who),
824 timeout,
825 sipe_subscribe_presence_single,
826 g_free);
827 g_free(action_name);
828 SIPE_DEBUG_INFO("Resubscription single contact '%s' in %d seconds", who, timeout);
830 g_free(who);
832 } else {
833 const struct event_subscription_data *esd;
835 for (esd = events_table; esd->event; esd++) {
836 if (sipe_strcase_equal(event, esd->event)) {
837 gchar *action_name = g_strdup_printf("<%s>", event);
838 sipe_schedule_seconds(sipe_private,
839 action_name,
840 NULL,
841 timeout,
842 esd->callback,
843 NULL);
844 g_free(action_name);
845 SIPE_DEBUG_INFO("Resubscription to event '%s' in %d seconds", event, timeout);
846 break;
854 Local Variables:
855 mode: c
856 c-file-style: "bsd"
857 indent-tabs-mode: t
858 tab-width: 8
859 End: