6 * Copyright (C) 2010-11 SIPE Project <http://sipe.sourceforge.net/>
7 * Copyright (C) 2010 pier11 <pier11@operamail.com>
8 * Copyright (C) 2009 Anibal Avelar <debianmx@gmail.com>
9 * Copyright (C) 2009 pier11 <pier11@operamail.com>
10 * Copyright (C) 2008 Novell, Inc., Anibal Avelar <debianmx@gmail.com>
11 * Copyright (C) 2007 Anibal Avelar <debianmx@gmail.com>
12 * Copyright (C) 2005 Thomas Butter <butter@uni-mannheim.de>
15 * Thanks to Google's Summer of Code Program and the helpful mentors
18 * Session-based SIP MESSAGE documentation:
19 * http://tools.ietf.org/html/draft-ietf-simple-im-session-00
21 * This program is free software; you can redistribute it and/or modify
22 * it under the terms of the GNU General Public License as published by
23 * the Free Software Foundation; either version 2 of the License, or
24 * (at your option) any later version.
26 * This program is distributed in the hope that it will be useful,
27 * but WITHOUT ANY WARRANTY; without even the implied warranty of
28 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29 * GNU General Public License for more details.
31 * You should have received a copy of the GNU General Public License
32 * along with this program; if not, write to the Free Software
33 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
51 #include "sipe-common.h"
55 #include "connection.h"
56 #include "conversation.h"
62 #include "savedstatuses.h"
64 #include "core-depurple.h" /* Temporary for the core de-purple transition */
66 #include "http-conn.h"
69 #include "sip-transport.h"
70 #include "sipe-backend.h"
71 #include "sipe-buddy.h"
73 #include "sipe-chat.h"
74 #include "sipe-conf.h"
75 #include "sipe-core.h"
76 #include "sipe-core-private.h"
77 #include "sipe-group.h"
78 #include "sipe-dialog.h"
80 #include "sipe-domino.h"
81 #include "sipe-groupchat.h"
83 #include "sipe-mime.h"
85 #include "sipe-schedule.h"
86 #include "sipe-session.h"
87 #include "sipe-subscriptions.h"
89 #include "sipe-media.h"
91 #include "sipe-utils.h"
96 #define SIPE_IDLE_SET_DELAY 1 /* 1 sec */
98 #define UPDATE_CALENDAR_DELAY 1*60 /* 1 min */
99 #define UPDATE_CALENDAR_INTERVAL 30*60 /* 30 min */
101 /* Status identifiers (see also: sipe_status_types()) */
102 #define SIPE_STATUS_ID_UNKNOWN purple_primitive_get_id_from_type(PURPLE_STATUS_UNSET) /* Unset (primitive) */
103 #define SIPE_STATUS_ID_OFFLINE purple_primitive_get_id_from_type(PURPLE_STATUS_OFFLINE) /* Offline (primitive) */
104 #define SIPE_STATUS_ID_AVAILABLE purple_primitive_get_id_from_type(PURPLE_STATUS_AVAILABLE) /* Online */
105 /* PURPLE_STATUS_UNAVAILABLE: */
106 #define SIPE_STATUS_ID_BUSY "busy" /* Busy */
107 #define SIPE_STATUS_ID_BUSYIDLE "busyidle" /* BusyIdle */
108 #define SIPE_STATUS_ID_DND "do-not-disturb" /* Do Not Disturb */
109 #define SIPE_STATUS_ID_IN_MEETING "in-a-meeting" /* In a meeting */
110 #define SIPE_STATUS_ID_IN_CONF "in-a-conference" /* In a conference */
111 #define SIPE_STATUS_ID_ON_PHONE "on-the-phone" /* On the phone */
112 #define SIPE_STATUS_ID_INVISIBLE purple_primitive_get_id_from_type(PURPLE_STATUS_INVISIBLE) /* Appear Offline */
113 /* PURPLE_STATUS_AWAY: */
114 #define SIPE_STATUS_ID_IDLE "idle" /* Idle/Inactive */
115 #define SIPE_STATUS_ID_BRB "be-right-back" /* Be Right Back */
116 #define SIPE_STATUS_ID_AWAY purple_primitive_get_id_from_type(PURPLE_STATUS_AWAY) /* Away (primitive) */
117 /** Reuters status (user settable) */
118 #define SIPE_STATUS_ID_LUNCH "out-to-lunch" /* Out To Lunch */
119 /* ??? PURPLE_STATUS_EXTENDED_AWAY */
120 /* ??? PURPLE_STATUS_MOBILE */
121 /* ??? PURPLE_STATUS_TUNE */
123 /* Status attributes (see also sipe_status_types() */
124 #define SIPE_STATUS_ATTR_ID_MESSAGE "message"
126 static struct sipe_activity_map_struct
131 const char *status_id
;
133 } const sipe_activity_map
[] =
135 /* This has nothing to do with Availability numbers, like 3500 (online).
136 * Just a mapping of Communicator Activities to Purple statuses to be able display them in Pidgin.
138 { SIPE_ACTIVITY_UNSET
, "unset", NULL
, NULL
},
139 { SIPE_ACTIVITY_ONLINE
, "online", NULL
, NULL
},
140 { SIPE_ACTIVITY_INACTIVE
, SIPE_STATUS_ID_IDLE
, N_("Inactive") , NULL
},
141 { SIPE_ACTIVITY_BUSY
, SIPE_STATUS_ID_BUSY
, N_("Busy") , SIPE_STATUS_ID_BUSY
},
142 { SIPE_ACTIVITY_BUSYIDLE
, SIPE_STATUS_ID_BUSYIDLE
, N_("Busy-Idle") , NULL
},
143 { SIPE_ACTIVITY_DND
, SIPE_STATUS_ID_DND
, NULL
, SIPE_STATUS_ID_DND
},
144 { SIPE_ACTIVITY_BRB
, SIPE_STATUS_ID_BRB
, N_("Be right back") , SIPE_STATUS_ID_BRB
},
145 { SIPE_ACTIVITY_AWAY
, "away", NULL
, NULL
},
146 { SIPE_ACTIVITY_LUNCH
, SIPE_STATUS_ID_LUNCH
, N_("Out to lunch") , NULL
},
147 { SIPE_ACTIVITY_OFFLINE
, "offline", NULL
, NULL
},
148 { SIPE_ACTIVITY_ON_PHONE
, SIPE_STATUS_ID_ON_PHONE
, N_("In a call") , NULL
},
149 { SIPE_ACTIVITY_IN_CONF
, SIPE_STATUS_ID_IN_CONF
, N_("In a conference") , NULL
},
150 { SIPE_ACTIVITY_IN_MEETING
, SIPE_STATUS_ID_IN_MEETING
, N_("In a meeting") , NULL
},
151 { SIPE_ACTIVITY_OOF
, "out-of-office", N_("Out of office") , NULL
},
152 { SIPE_ACTIVITY_URGENT_ONLY
, "urgent-interruptions-only", N_("Urgent interruptions only") , NULL
}
154 /** @param x is sipe_activity */
155 #define SIPE_ACTIVITY_I18N(x) gettext(sipe_activity_map[x].desc)
158 sipe_get_activity_by_token(const char *token
)
162 for (i
= 0; i
< SIPE_ACTIVITY_NUM_TYPES
; i
++)
164 if (sipe_strequal(token
, sipe_activity_map
[i
].token
))
165 return sipe_activity_map
[i
].type
;
168 return sipe_activity_map
[0].type
;
172 sipe_get_activity_desc_by_token(const char *token
)
174 if (!token
) return NULL
;
176 return SIPE_ACTIVITY_I18N(sipe_get_activity_by_token(token
));
179 static void send_presence_status(struct sipe_core_private
*sipe_private
,
183 * @param from0 from URI (with 'sip:' prefix). Will be filled with self-URI if NULL passed.
186 send_soap_request_with_cb(struct sipe_core_private
*sipe_private
,
189 TransCallback callback
,
190 struct transaction_payload
*payload
)
192 gchar
*from
= from0
? g_strdup(from0
) : sip_uri_self(sipe_private
);
193 gchar
*contact
= get_contact(sipe_private
);
194 gchar
*hdr
= g_strdup_printf("Contact: %s\r\n"
195 "Content-Type: application/SOAP+xml\r\n",contact
);
197 struct transaction
*trans
= sip_transport_service(sipe_private
,
202 trans
->payload
= payload
;
210 send_soap_request(struct sipe_core_private
*sipe_private
,
213 send_soap_request_with_cb(sipe_private
, NULL
, body
, NULL
, NULL
);
217 * Returns pointer to URI without sip: prefix if any
219 * @param sip_uri SIP URI possibly with sip: prefix. Example: sip:first.last@hq.company.com
220 * @return pointer to URL without sip: prefix. Coresponding example: first.last@hq.company.com
222 * Doesn't allocate memory
225 sipe_get_no_sip_uri(const char *sip_uri
)
227 const char *prefix
= "sip:";
228 if (!sip_uri
) return NULL
;
230 if (g_str_has_prefix(sip_uri
, prefix
)) {
231 return (sip_uri
+strlen(prefix
));
238 sipe_contact_set_acl (struct sipe_core_private
*sipe_private
,
242 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
243 gchar
* body
= g_strdup_printf(SIPE_SOAP_ALLOW_DENY
, who
, rights
, sip
->acl_delta
++);
244 send_soap_request(sipe_private
, body
);
249 sipe_change_access_level(struct sipe_core_private
*sipe_private
,
250 const int container_id
,
255 sipe_core_contact_allow_deny (struct sipe_core_public
*sipe_public
,
256 const gchar
* who
, gboolean allow
)
258 struct sipe_core_private
*sipe_private
= SIPE_CORE_PRIVATE
;
261 SIPE_DEBUG_INFO("Authorizing contact %s", who
);
263 SIPE_DEBUG_INFO("Blocking contact %s", who
);
266 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
267 sipe_change_access_level(sipe_private
, (allow
? -1 : 32000), "user", sipe_get_no_sip_uri(who
));
269 sipe_contact_set_acl(sipe_private
, who
, allow
? "AA" : "BD");
274 void sipe_auth_user_cb(void * data
)
276 struct sipe_auth_job
* job
= (struct sipe_auth_job
*) data
;
279 sipe_core_contact_allow_deny((struct sipe_core_public
*)job
->sipe_private
, job
->who
, TRUE
);
284 void sipe_deny_user_cb(void * data
)
286 struct sipe_auth_job
* job
= (struct sipe_auth_job
*) data
;
289 sipe_core_contact_allow_deny((struct sipe_core_public
*)job
->sipe_private
, job
->who
, FALSE
);
293 /** @applicable: 2005-
296 sipe_process_presence_wpending (struct sipe_core_private
*sipe_private
,
300 const sipe_xml
*watcher
;
301 // Ensure it's either not a response (eg it's a BENOTIFY) or that it's a 200 OK response
302 if (msg
->response
!= 0 && msg
->response
!= 200) return;
304 if (msg
->bodylen
== 0 || msg
->body
== NULL
|| sipe_strequal(sipmsg_find_header(msg
, "Event"), "msrtc.wpending")) return;
306 watchers
= sipe_xml_parse(msg
->body
, msg
->bodylen
);
307 if (!watchers
) return;
309 for (watcher
= sipe_xml_child(watchers
, "watcher"); watcher
; watcher
= sipe_xml_twin(watcher
)) {
310 gchar
* remote_user
= g_strdup(sipe_xml_attribute(watcher
, "uri"));
311 gchar
* alias
= g_strdup(sipe_xml_attribute(watcher
, "displayName"));
312 gboolean on_list
= g_hash_table_lookup(sipe_private
->buddies
, remote_user
) != NULL
;
314 // TODO pull out optional displayName to pass as alias
316 struct sipe_auth_job
* job
= g_new0(struct sipe_auth_job
, 1);
317 job
->who
= remote_user
;
318 job
->sipe_private
= sipe_private
;
319 sipe_backend_buddy_request_authorization(SIPE_CORE_PUBLIC
,
330 sipe_xml_free(watchers
);
335 * Returns string like "2 4 7 8" - group ids buddy belong to.
338 sipe_get_buddy_groups_string (struct sipe_buddy
*buddy
) {
341 //creating array from GList, converting int to gchar*
342 gchar
**ids_arr
= g_new(gchar
*, g_slist_length(buddy
->groups
) + 1);
343 GSList
*entry
= buddy
->groups
;
345 if (!ids_arr
) return NULL
;
348 struct sipe_group
* group
= entry
->data
;
349 ids_arr
[i
] = g_strdup_printf("%d", group
->id
);
354 res
= g_strjoinv(" ", ids_arr
);
360 * Sends buddy update to server
363 sipe_core_group_set_user(struct sipe_core_public
*sipe_public
, const gchar
* who
)
365 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA
;
366 struct sipe_buddy
*buddy
= g_hash_table_lookup(SIPE_CORE_PRIVATE
->buddies
, who
);
367 sipe_backend_buddy backend_buddy
= sipe_backend_buddy_find(sipe_public
, who
, NULL
);
369 if (buddy
&& backend_buddy
) {
370 gchar
*alias
= sipe_backend_buddy_get_alias(sipe_public
, backend_buddy
);
371 gchar
*groups
= sipe_get_buddy_groups_string(buddy
);
374 SIPE_DEBUG_INFO("Saving buddy %s with alias %s and groups %s", who
, alias
, groups
);
376 body
= g_markup_printf_escaped(SIPE_SOAP_SET_CONTACT
,
377 alias
, groups
, "true", buddy
->name
, sip
->contacts_delta
++
379 send_soap_request(SIPE_CORE_PRIVATE
, body
);
388 sipe_sched_calendar_status_update(struct sipe_core_private
*sipe_private
,
389 time_t calculate_from
);
392 sipe_get_availability_by_status(const char* sipe_status_id
, char** activity_token
);
395 sipe_get_status_by_availability(int avail
,
399 sipe_set_purple_account_status_and_note(const PurpleAccount
*account
,
400 const char *status_id
,
402 time_t do_not_publish
[]);
405 sipe_apply_calendar_status(struct sipe_core_private
*sipe_private
,
406 struct sipe_buddy
*sbuddy
,
407 const char *status_id
)
409 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
410 time_t cal_avail_since
;
411 int cal_status
= sipe_cal_get_status(sbuddy
, time(NULL
), &cal_avail_since
);
417 if (cal_status
< SIPE_CAL_NO_DATA
) {
418 SIPE_DEBUG_INFO("sipe_apply_calendar_status: cal_status : %d for %s", cal_status
, sbuddy
->name
);
419 SIPE_DEBUG_INFO("sipe_apply_calendar_status: cal_avail_since : %s", asctime(localtime(&cal_avail_since
)));
422 /* scheduled Cal update call */
424 status_id
= sbuddy
->last_non_cal_status_id
;
425 g_free(sbuddy
->activity
);
426 sbuddy
->activity
= g_strdup(sbuddy
->last_non_cal_activity
);
430 SIPE_DEBUG_INFO("sipe_apply_calendar_status: status_id is NULL for %s, exiting.",
431 sbuddy
->name
? sbuddy
->name
: "" );
435 /* adjust to calendar status */
436 if (cal_status
!= SIPE_CAL_NO_DATA
) {
437 SIPE_DEBUG_INFO("sipe_apply_calendar_status: user_avail_since: %s", asctime(localtime(&sbuddy
->user_avail_since
)));
439 if (cal_status
== SIPE_CAL_BUSY
440 && cal_avail_since
> sbuddy
->user_avail_since
441 && 6500 >= sipe_get_availability_by_status(status_id
, NULL
))
443 status_id
= SIPE_STATUS_ID_BUSY
;
444 g_free(sbuddy
->activity
);
445 sbuddy
->activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_IN_MEETING
));
447 avail
= sipe_get_availability_by_status(status_id
, NULL
);
449 SIPE_DEBUG_INFO("sipe_apply_calendar_status: activity_since : %s", asctime(localtime(&sbuddy
->activity_since
)));
450 if (cal_avail_since
> sbuddy
->activity_since
) {
451 if (cal_status
== SIPE_CAL_OOF
452 && avail
>= 15000) /* 12000 in 2007 */
454 g_free(sbuddy
->activity
);
455 sbuddy
->activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_OOF
));
460 /* then set status_id actually */
461 SIPE_DEBUG_INFO("sipe_apply_calendar_status: to %s for %s", status_id
, sbuddy
->name
? sbuddy
->name
: "" );
462 sipe_backend_buddy_set_status(SIPE_CORE_PUBLIC
, sbuddy
->name
, status_id
);
464 /* set our account state to the one in roaming (including calendar info) */
465 self_uri
= sip_uri_self(sipe_private
);
466 if (sip
->initial_state_published
&& sipe_strcase_equal(sbuddy
->name
, self_uri
)) {
467 if (sipe_strequal(status_id
, SIPE_STATUS_ID_OFFLINE
)) {
468 status_id
= g_strdup(SIPE_STATUS_ID_INVISIBLE
); /* not not let offline status switch us off */
471 SIPE_DEBUG_INFO("sipe_apply_calendar_status: switch to '%s' for the account", sip
->status
);
472 sipe_set_purple_account_status_and_note(sip
->account
, status_id
, sip
->note
, sip
->do_not_publish
);
478 sipe_core_buddy_got_status(struct sipe_core_public
*sipe_public
,
480 const gchar
*status_id
)
482 struct sipe_core_private
*sipe_private
= SIPE_CORE_PRIVATE
;
483 struct sipe_buddy
*sbuddy
= g_hash_table_lookup(sipe_private
->buddies
, uri
);
487 /* Check if on 2005 system contact's calendar,
488 * then set/preserve it.
490 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
491 sipe_apply_calendar_status(sipe_private
, sbuddy
, status_id
);
493 sipe_backend_buddy_set_status(sipe_public
, uri
, status_id
);
498 update_calendar_status_cb(SIPE_UNUSED_PARAMETER
char *name
,
499 struct sipe_buddy
*sbuddy
,
500 struct sipe_core_private
*sipe_private
)
502 sipe_apply_calendar_status(sipe_private
, sbuddy
, NULL
);
506 * Updates contact's status
507 * based on their calendar information.
509 * Applicability: 2005 systems
512 update_calendar_status(struct sipe_core_private
*sipe_private
,
513 SIPE_UNUSED_PARAMETER
void *unused
)
515 SIPE_DEBUG_INFO_NOFORMAT("update_calendar_status() started.");
516 g_hash_table_foreach(sipe_private
->buddies
, (GHFunc
)update_calendar_status_cb
, sipe_private
);
518 /* repeat scheduling */
519 sipe_sched_calendar_status_update(sipe_private
, time(NULL
) + 3*60 /* 3 min */);
523 * Schedules process of contacts' status update
524 * based on their calendar information.
525 * Should be scheduled to the beginning of every
526 * 15 min interval, like:
527 * 13:00, 13:15, 13:30, 13:45, etc.
529 * Applicability: 2005 systems
532 sipe_sched_calendar_status_update(struct sipe_core_private
*sipe_private
,
533 time_t calculate_from
)
535 int interval
= 15*60;
536 /** start of the beginning of closest 15 min interval. */
537 time_t next_start
= ((time_t)((int)((int)calculate_from
)/interval
+ 1)*interval
);
539 SIPE_DEBUG_INFO("sipe_sched_calendar_status_update: calculate_from time: %s",
540 asctime(localtime(&calculate_from
)));
541 SIPE_DEBUG_INFO("sipe_sched_calendar_status_update: next start time : %s",
542 asctime(localtime(&next_start
)));
544 sipe_schedule_seconds(sipe_private
,
545 "<+2005-cal-status>",
547 next_start
- time(NULL
),
548 update_calendar_status
,
553 * Schedules process of self status publish
554 * based on own calendar information.
555 * Should be scheduled to the beginning of every
556 * 15 min interval, like:
557 * 13:00, 13:15, 13:30, 13:45, etc.
559 * Applicability: 2007+ systems
562 sipe_sched_calendar_status_self_publish(struct sipe_core_private
*sipe_private
,
563 time_t calculate_from
)
566 /** start of the beginning of closest 5 min interval. */
567 time_t next_start
= ((time_t)((int)((int)calculate_from
)/interval
+ 1)*interval
);
569 SIPE_DEBUG_INFO("sipe_sched_calendar_status_self_publish: calculate_from time: %s",
570 asctime(localtime(&calculate_from
)));
571 SIPE_DEBUG_INFO("sipe_sched_calendar_status_self_publish: next start time : %s",
572 asctime(localtime(&next_start
)));
574 sipe_schedule_seconds(sipe_private
,
575 "<+2007-cal-status>",
577 next_start
- time(NULL
),
578 publish_calendar_status_self
,
582 static void sipe_subscribe_resource_uri(const char *name
,
583 SIPE_UNUSED_PARAMETER gpointer value
,
584 gchar
**resources_uri
)
586 gchar
*tmp
= *resources_uri
;
587 *resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"/>\n", tmp
, name
);
591 static void sipe_subscribe_resource_uri_with_context(const char *name
, gpointer value
, gchar
**resources_uri
)
593 struct sipe_buddy
*sbuddy
= (struct sipe_buddy
*)value
;
594 gchar
*context
= sbuddy
&& sbuddy
->just_added
? "><context/></resource>" : "/>";
595 gchar
*tmp
= *resources_uri
;
597 if (sbuddy
) sbuddy
->just_added
= FALSE
; /* should be enought to include context one time */
599 *resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"%s\n", tmp
, name
, context
);
604 * Support for Batch Category SUBSCRIBE [MS-PRES] - msrtc-event-categories+xml OCS 2007
605 * Support for Batch Category SUBSCRIBE [MS-SIP] - adrl+xml LCS 2005
606 * The user sends an initial batched category SUBSCRIBE request against all contacts on his roaming list in only a request
607 * A batch category SUBSCRIBE request MUST have the same To-URI and From-URI.
608 * This header will be send only if adhoclist there is a "Supported: adhoclist" in REGISTER answer else will be send a Single Category SUBSCRIBE
611 static void sipe_subscribe_presence_batched_to(struct sipe_core_private
*sipe_private
,
612 gchar
*resources_uri
,
615 gchar
*contact
= get_contact(sipe_private
);
620 gchar
*autoextend
= "";
623 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
624 require
= ", categoryList";
625 accept
= ", application/msrtc-event-categories+xml, application/xpidf+xml, application/pidf+xml";
626 content_type
= "application/msrtc-adrl-categorylist+xml";
627 content
= g_strdup_printf(
628 "<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
629 "<action name=\"subscribe\" id=\"63792024\">\n"
630 "<adhocList>\n%s</adhocList>\n"
631 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
632 "<category name=\"calendarData\"/>\n"
633 "<category name=\"contactCard\"/>\n"
634 "<category name=\"note\"/>\n"
635 "<category name=\"state\"/>\n"
638 "</batchSub>", sipe_private
->username
, resources_uri
);
640 autoextend
= "Supported: com.microsoft.autoextend\r\n";
641 content_type
= "application/adrl+xml";
642 content
= g_strdup_printf(
643 "<adhoclist xmlns=\"urn:ietf:params:xml:ns:adrl\" uri=\"sip:%s\" name=\"sip:%s\">\n"
644 "<create xmlns=\"\">\n%s</create>\n"
645 "</adhoclist>\n", sipe_private
->username
, sipe_private
->username
, resources_uri
);
647 g_free(resources_uri
);
649 request
= g_strdup_printf(
650 "Require: adhoclist%s\r\n"
651 "Supported: eventlist\r\n"
652 "Accept: application/rlmi+xml, multipart/related, text/xml+msrtc.pidf%s\r\n"
653 "Supported: ms-piggyback-first-notify\r\n"
654 "%sSupported: ms-benotify\r\n"
655 "Proxy-Require: ms-benotify\r\n"
656 "Event: presence\r\n"
657 "Content-Type: %s\r\n"
658 "Contact: %s\r\n", require
, accept
, autoextend
, content_type
, contact
);
661 sipe_subscribe_presence_buddy(sipe_private
, to
, request
, content
);
668 static void sipe_subscribe_presence_batched(struct sipe_core_private
*sipe_private
,
669 SIPE_UNUSED_PARAMETER
void *unused
)
671 gchar
*to
= sip_uri_self(sipe_private
);
672 gchar
*resources_uri
= g_strdup("");
673 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
674 g_hash_table_foreach(sipe_private
->buddies
, (GHFunc
) sipe_subscribe_resource_uri_with_context
, &resources_uri
);
676 g_hash_table_foreach(sipe_private
->buddies
, (GHFunc
) sipe_subscribe_resource_uri
, &resources_uri
);
679 sipe_subscribe_presence_batched_to(sipe_private
, resources_uri
, to
);
682 struct presence_batched_routed
{
687 static void sipe_subscribe_presence_batched_routed_free(void *payload
)
689 struct presence_batched_routed
*data
= payload
;
690 GSList
*buddies
= data
->buddies
;
692 g_free(buddies
->data
);
693 buddies
= buddies
->next
;
695 g_slist_free(data
->buddies
);
700 static void sipe_subscribe_presence_batched_routed(struct sipe_core_private
*sipe_private
,
703 struct presence_batched_routed
*data
= payload
;
704 GSList
*buddies
= data
->buddies
;
705 gchar
*resources_uri
= g_strdup("");
707 gchar
*tmp
= resources_uri
;
708 resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"/>\n", tmp
, (char *) buddies
->data
);
710 buddies
= buddies
->next
;
712 sipe_subscribe_presence_batched_to(sipe_private
, resources_uri
,
713 g_strdup(data
->host
));
717 * Single Category SUBSCRIBE [MS-PRES] ; To send when the server returns a 200 OK message with state="resubscribe" in response.
718 * The user sends a single SUBSCRIBE request to the subscribed contact.
719 * The To-URI and the URI listed in the resource list MUST be the same for a single category SUBSCRIBE request.
723 static void sipe_subscribe_presence_single(struct sipe_core_private
*sipe_private
,
726 gchar
*to
= sip_uri((char *)buddy_name
);
727 gchar
*tmp
= get_contact(sipe_private
);
729 gchar
*content
= NULL
;
730 gchar
*autoextend
= "";
731 gchar
*content_type
= "";
732 struct sipe_buddy
*sbuddy
= g_hash_table_lookup(sipe_private
->buddies
, to
);
733 gchar
*context
= sbuddy
&& sbuddy
->just_added
? "><context/></resource>" : "/>";
735 if (sbuddy
) sbuddy
->just_added
= FALSE
;
737 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
738 content_type
= "Content-Type: application/msrtc-adrl-categorylist+xml\r\n";
740 autoextend
= "Supported: com.microsoft.autoextend\r\n";
743 request
= g_strdup_printf(
744 "Accept: application/msrtc-event-categories+xml, text/xml+msrtc.pidf, application/xpidf+xml, application/pidf+xml, application/rlmi+xml, multipart/related\r\n"
745 "Supported: ms-piggyback-first-notify\r\n"
746 "%s%sSupported: ms-benotify\r\n"
747 "Proxy-Require: ms-benotify\r\n"
748 "Event: presence\r\n"
749 "Contact: %s\r\n", autoextend
, content_type
, tmp
);
751 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
752 content
= g_strdup_printf(
753 "<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
754 "<action name=\"subscribe\" id=\"63792024\"><adhocList>\n"
755 "<resource uri=\"%s\"%s\n"
757 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
758 "<category name=\"calendarData\"/>\n"
759 "<category name=\"contactCard\"/>\n"
760 "<category name=\"note\"/>\n"
761 "<category name=\"state\"/>\n"
764 "</batchSub>", sipe_private
->username
, to
, context
);
769 sipe_subscribe_presence_buddy(sipe_private
, to
, request
, content
);
776 void sipe_set_status(PurpleAccount
*account
, PurpleStatus
*status
)
778 SIPE_DEBUG_INFO("sipe_set_status: status=%s", purple_status_get_id(status
));
780 if (!purple_status_is_active(status
))
784 struct sipe_core_private
*sipe_private
= PURPLE_ACCOUNT_TO_SIPE_CORE_PRIVATE
;
785 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
790 time_t now
= time(NULL
);
791 const char *status_id
= purple_status_get_id(status
);
792 const char *note
= purple_status_get_attr_string(status
, SIPE_STATUS_ATTR_ID_MESSAGE
);
793 sipe_activity activity
= sipe_get_activity_by_token(status_id
);
794 gboolean do_not_publish
= ((now
- sip
->do_not_publish
[activity
]) <= 2);
796 /* when other point of presence clears note, but we are keeping
799 if (do_not_publish
&& !note
&& sip
->cal
&& sip
->cal
->oof_note
) {
800 SIPE_DEBUG_INFO_NOFORMAT("sipe_set_status: enabling publication as OOF note keepers.");
801 do_not_publish
= FALSE
;
804 SIPE_DEBUG_INFO("sipe_set_status: was: sip->do_not_publish[%s]=%d [?] now(time)=%d",
805 status_id
, (int)sip
->do_not_publish
[activity
], (int)now
);
807 sip
->do_not_publish
[activity
] = 0;
808 SIPE_DEBUG_INFO("sipe_set_status: set: sip->do_not_publish[%s]=%d [0]",
809 status_id
, (int)sip
->do_not_publish
[activity
]);
813 SIPE_DEBUG_INFO_NOFORMAT("sipe_set_status: publication was switched off, exiting.");
818 sip
->status
= g_strdup(status_id
);
820 /* hack to escape apostrof before comparison */
821 tmp
= note
? sipe_utils_str_replace(note
, "'", "'") : NULL
;
823 /* this will preserve OOF flag as well */
824 if (!sipe_strequal(tmp
, sip
->note
)) {
825 sip
->is_oof_note
= FALSE
;
827 sip
->note
= g_strdup(note
);
828 sip
->note_since
= time(NULL
);
832 /* schedule 2 sec to capture idle flag */
833 action_name
= g_strdup_printf("<%s>", "+set-status");
834 sipe_schedule_seconds(sipe_private
,
838 send_presence_status
,
846 sipe_set_idle(PurpleConnection
* gc
,
849 SIPE_DEBUG_INFO("sipe_set_idle: interval=%d", interval
);
852 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
853 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
856 sip
->idle_switch
= time(NULL
);
857 SIPE_DEBUG_INFO("sipe_set_idle: sip->idle_switch : %s", asctime(localtime(&(sip
->idle_switch
))));
863 sipe_group_buddy(PurpleConnection
*gc
,
865 const char *old_group_name
,
866 const char *new_group_name
)
868 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
869 struct sipe_buddy
* buddy
= g_hash_table_lookup(sipe_private
->buddies
, who
);
870 struct sipe_group
* old_group
= NULL
;
871 struct sipe_group
* new_group
;
873 SIPE_DEBUG_INFO("sipe_group_buddy[CB]: who:%s old_group_name:%s new_group_name:%s",
874 who
? who
: "", old_group_name
? old_group_name
: "", new_group_name
? new_group_name
: "");
876 if(!buddy
) { // buddy not in roaming list
880 if (old_group_name
) {
881 old_group
= sipe_group_find_by_name(sipe_private
, old_group_name
);
883 new_group
= sipe_group_find_by_name(sipe_private
, new_group_name
);
886 buddy
->groups
= g_slist_remove(buddy
->groups
, old_group
);
887 SIPE_DEBUG_INFO("buddy %s removed from old group %s", who
, old_group_name
);
891 sipe_group_create(sipe_private
, new_group_name
, who
);
893 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, new_group
, (GCompareFunc
)sipe_group_compare
);
894 sipe_core_group_set_user(SIPE_CORE_PUBLIC
, who
);
898 void sipe_add_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
900 SIPE_DEBUG_INFO("sipe_add_buddy[CB]: buddy:%s group:%s", buddy
? buddy
->name
: "", group
? group
->name
: "");
902 /* libpurple can call us with undefined buddy or group */
903 if (buddy
&& group
) {
904 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
906 /* Buddy name must be lower case as we use purple_normalize_nocase() to compare */
907 gchar
*buddy_name
= g_ascii_strdown(buddy
->name
, -1);
908 purple_blist_rename_buddy(buddy
, buddy_name
);
911 /* Prepend sip: if needed */
912 if (!g_str_has_prefix(buddy
->name
, "sip:")) {
913 gchar
*buf
= sip_uri_from_name(buddy
->name
);
914 purple_blist_rename_buddy(buddy
, buf
);
918 if (!g_hash_table_lookup(sipe_private
->buddies
, buddy
->name
)) {
919 struct sipe_buddy
*b
= g_new0(struct sipe_buddy
, 1);
920 SIPE_DEBUG_INFO("sipe_add_buddy: adding %s", buddy
->name
);
921 b
->name
= g_strdup(buddy
->name
);
922 b
->just_added
= TRUE
;
923 g_hash_table_insert(sipe_private
->buddies
, b
->name
, b
);
924 /* @TODO should go to callback */
925 sipe_subscribe_presence_single(sipe_private
,
928 SIPE_DEBUG_INFO("sipe_add_buddy: buddy %s already in internal list", buddy
->name
);
931 sipe_group_buddy(gc
, buddy
->name
, NULL
, group
->name
);
935 static void sipe_free_buddy(struct sipe_buddy
*buddy
)
939 * We are calling g_hash_table_foreach_steal(). That means that no
940 * key/value deallocation functions are called. Therefore the glib
941 * hash code does not touch the key (buddy->name) or value (buddy)
942 * of the to-be-deleted hash node at all. It follows that we
944 * - MUST free the memory for the key ourselves and
945 * - ARE allowed to do it in this function
947 * Conclusion: glib must be broken on the Windows platform if sipe
948 * crashes with SIGTRAP when closing. You'll have to live
949 * with the memory leak until this is fixed.
953 g_free(buddy
->activity
);
954 g_free(buddy
->meeting_subject
);
955 g_free(buddy
->meeting_location
);
958 g_free(buddy
->cal_start_time
);
959 g_free(buddy
->cal_free_busy_base64
);
960 g_free(buddy
->cal_free_busy
);
961 g_free(buddy
->last_non_cal_activity
);
963 sipe_cal_free_working_hours(buddy
->cal_working_hours
);
965 g_free(buddy
->device_name
);
966 g_slist_free(buddy
->groups
);
971 * Unassociates buddy from group first.
972 * Then see if no groups left, removes buddy completely.
973 * Otherwise updates buddy groups on server.
975 void sipe_remove_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
977 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
978 struct sipe_buddy
*b
;
979 struct sipe_group
*g
= NULL
;
981 SIPE_DEBUG_INFO("sipe_remove_buddy[CB]: buddy:%s group:%s", buddy
? buddy
->name
: "", group
? group
->name
: "");
984 b
= g_hash_table_lookup(sipe_private
->buddies
, buddy
->name
);
988 g
= sipe_group_find_by_name(sipe_private
, group
->name
);
992 b
->groups
= g_slist_remove(b
->groups
, g
);
993 SIPE_DEBUG_INFO("buddy %s removed from group %s", buddy
->name
, g
->name
);
996 if (g_slist_length(b
->groups
) < 1) {
997 gchar
*action_name
= sipe_utils_presence_key(buddy
->name
);
998 sipe_schedule_cancel(sipe_private
, action_name
);
1001 g_hash_table_remove(sipe_private
->buddies
, buddy
->name
);
1004 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
1005 gchar
* body
= g_strdup_printf(SIPE_SOAP_DEL_CONTACT
, b
->name
, sip
->contacts_delta
++);
1006 send_soap_request(sipe_private
, body
);
1012 //updates groups on server
1013 sipe_core_group_set_user(SIPE_CORE_PUBLIC
, b
->name
);
1019 * A callback for g_hash_table_foreach
1022 sipe_buddy_subscribe_cb(char *buddy_name
,
1023 SIPE_UNUSED_PARAMETER
struct sipe_buddy
*buddy
,
1024 struct sipe_core_private
*sipe_private
)
1026 gchar
*action_name
= sipe_utils_presence_key(buddy_name
);
1027 /* g_hash_table_size() can never return 0, otherwise this function wouldn't be called :-) */
1028 guint time_range
= (g_hash_table_size(sipe_private
->buddies
) * 1000) / 25; /* time interval for 25 requests per sec. In msec. */
1029 guint timeout
= ((guint
) rand()) / (RAND_MAX
/ time_range
) + 1; /* random period within the range but never 0! */
1031 sipe_schedule_mseconds(sipe_private
,
1033 g_strdup(buddy_name
),
1035 sipe_subscribe_presence_single
,
1037 g_free(action_name
);
1041 * Removes entries from local buddy list
1042 * that does not correspond ones in the roaming contact list.
1044 static void sipe_cleanup_local_blist(struct sipe_core_private
*sipe_private
) {
1045 GSList
*buddies
= sipe_backend_buddy_find_all(SIPE_CORE_PUBLIC
, NULL
, NULL
);
1046 GSList
*entry
= buddies
;
1047 struct sipe_buddy
*buddy
;
1048 sipe_backend_buddy b
;
1052 SIPE_DEBUG_INFO("sipe_cleanup_local_blist: overall %d backend buddies (including clones)", g_slist_length(buddies
));
1053 SIPE_DEBUG_INFO("sipe_cleanup_local_blist: %d sipe buddies (unique)", g_hash_table_size(sipe_private
->buddies
));
1056 gname
= sipe_backend_buddy_get_group_name(SIPE_CORE_PUBLIC
, b
);
1057 bname
= sipe_backend_buddy_get_name(SIPE_CORE_PUBLIC
, b
);
1058 buddy
= g_hash_table_lookup(sipe_private
->buddies
, bname
);
1061 gboolean in_sipe_groups
= FALSE
;
1062 GSList
*entry2
= buddy
->groups
;
1064 struct sipe_group
*group
= entry2
->data
;
1065 if (sipe_strequal(group
->name
, gname
)) {
1066 in_sipe_groups
= TRUE
;
1069 entry2
= entry2
->next
;
1071 if(!in_sipe_groups
) {
1072 SIPE_DEBUG_INFO("*** REMOVING %s from blist group: %s as not having this group in roaming list", bname
, gname
);
1073 sipe_backend_buddy_remove(SIPE_CORE_PUBLIC
, b
);
1076 SIPE_DEBUG_INFO("*** REMOVING %s from blist group: %s as this buddy not in roaming list", bname
, gname
);
1077 sipe_backend_buddy_remove(SIPE_CORE_PUBLIC
, b
);
1080 entry
= entry
->next
;
1082 g_slist_free(buddies
);
1086 sipe_find_access_level(struct sipe_core_private
*sipe_private
,
1089 gboolean
*is_group_access
);
1092 sipe_refresh_blocked_status_cb(char *buddy_name
,
1093 SIPE_UNUSED_PARAMETER
struct sipe_buddy
*buddy
,
1094 struct sipe_core_private
*sipe_private
)
1096 int container_id
= sipe_find_access_level(sipe_private
, "user", buddy_name
, NULL
);
1097 gboolean blocked
= (container_id
== 32000);
1098 gboolean blocked_in_blist
= sipe_backend_buddy_is_blocked(SIPE_CORE_PUBLIC
, buddy_name
);
1100 /* SIPE_DEBUG_INFO("sipe_refresh_blocked_status_cb: buddy_name=%s, blocked=%s, blocked_in_blist=%s",
1101 buddy_name, blocked ? "T" : "F", blocked_in_blist ? "T" : "F"); */
1103 if (blocked
!= blocked_in_blist
) {
1104 sipe_backend_buddy_set_blocked_status(SIPE_CORE_PUBLIC
, buddy_name
, blocked
);
1109 sipe_refresh_blocked_status(struct sipe_core_private
*sipe_private
)
1111 g_hash_table_foreach(sipe_private
->buddies
,
1112 (GHFunc
) sipe_refresh_blocked_status_cb
,
1116 static gboolean
sipe_process_roaming_contacts(struct sipe_core_private
*sipe_private
,
1119 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
1120 int len
= msg
->bodylen
;
1122 const gchar
*tmp
= sipmsg_find_header(msg
, "Event");
1123 const sipe_xml
*item
;
1125 const gchar
*contacts_delta
;
1126 const sipe_xml
*group_node
;
1127 if (!g_str_has_prefix(tmp
, "vnd-microsoft-roaming-contacts")) {
1131 /* Convert the contact from XML to backend Buddies */
1132 isc
= sipe_xml_parse(msg
->body
, len
);
1137 contacts_delta
= sipe_xml_attribute(isc
, "deltaNum");
1138 if (contacts_delta
) {
1139 sip
->contacts_delta
= (int)g_ascii_strtod(contacts_delta
, NULL
);
1142 if (sipe_strequal(sipe_xml_name(isc
), "contactList")) {
1145 for (group_node
= sipe_xml_child(isc
, "group"); group_node
; group_node
= sipe_xml_twin(group_node
)) {
1146 struct sipe_group
* group
= g_new0(struct sipe_group
, 1);
1147 const char *name
= sipe_xml_attribute(group_node
, "name");
1149 if (g_str_has_prefix(name
, "~")) {
1150 name
= _("Other Contacts");
1152 group
->name
= g_strdup(name
);
1153 group
->id
= (int)g_ascii_strtod(sipe_xml_attribute(group_node
, "id"), NULL
);
1155 sipe_group_add(sipe_private
, group
);
1158 // Make sure we have at least one group
1159 if (g_slist_length(sip
->groups
) == 0) {
1160 sipe_group_create(sipe_private
, _("Other Contacts"), NULL
);
1163 /* Parse contacts */
1164 for (item
= sipe_xml_child(isc
, "contact"); item
; item
= sipe_xml_twin(item
)) {
1165 const gchar
*uri
= sipe_xml_attribute(item
, "uri");
1166 const gchar
*name
= sipe_xml_attribute(item
, "name");
1168 struct sipe_buddy
*buddy
= NULL
;
1170 gchar
**item_groups
;
1173 /* Buddy name must be lower case as we use purple_normalize_nocase() to compare */
1174 tmp
= sip_uri_from_name(uri
);
1175 buddy_name
= g_ascii_strdown(tmp
, -1);
1178 /* assign to group Other Contacts if nothing else received */
1179 tmp
= g_strdup(sipe_xml_attribute(item
, "groups"));
1181 struct sipe_group
*group
= sipe_group_find_by_name(sipe_private
, _("Other Contacts"));
1183 tmp
= group
? g_strdup_printf("%d", group
->id
) : g_strdup("1");
1185 item_groups
= g_strsplit(tmp
, " ", 0);
1188 while (item_groups
[i
]) {
1189 struct sipe_group
*group
= sipe_group_find_by_id(sipe_private
, g_ascii_strtod(item_groups
[i
], NULL
));
1191 // If couldn't find the right group for this contact, just put them in the first group we have
1192 if (group
== NULL
&& g_slist_length(sip
->groups
) > 0) {
1193 group
= sip
->groups
->data
;
1196 if (group
!= NULL
) {
1198 sipe_backend_buddy b
= sipe_backend_buddy_find(SIPE_CORE_PUBLIC
, buddy_name
, group
->name
);
1200 b
= sipe_backend_buddy_add(SIPE_CORE_PUBLIC
, buddy_name
, uri
, group
->name
);
1201 SIPE_DEBUG_INFO("Created new buddy %s with alias %s", buddy_name
, uri
);
1204 b_alias
= sipe_backend_buddy_get_alias(SIPE_CORE_PUBLIC
, b
);
1205 if (sipe_strcase_equal(uri
, b_alias
)) {
1206 if (name
!= NULL
&& strlen(name
) != 0) {
1207 sipe_backend_buddy_set_alias(SIPE_CORE_PUBLIC
, b
, name
);
1209 SIPE_DEBUG_INFO("Replaced buddy %s alias with %s", buddy_name
, name
);
1215 buddy
= g_new0(struct sipe_buddy
, 1);
1216 buddy
->name
= sipe_backend_buddy_get_name(SIPE_CORE_PUBLIC
, b
);
1217 g_hash_table_insert(sipe_private
->buddies
, buddy
->name
, buddy
);
1219 SIPE_DEBUG_INFO("Added SIPE buddy %s", buddy
->name
);
1222 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, group
, (GCompareFunc
)sipe_group_compare
);
1224 SIPE_DEBUG_INFO("Added buddy %s to group %s", buddy
->name
, group
->name
);
1226 SIPE_DEBUG_INFO("No group found for contact %s! Unable to add to buddy list",
1231 } // while, contact groups
1232 g_strfreev(item_groups
);
1237 sipe_cleanup_local_blist(sipe_private
);
1239 /* Add self-contact if not there yet. 2005 systems. */
1240 /* This will resemble subscription to roaming_self in 2007 systems */
1241 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
1242 gchar
*self_uri
= sip_uri_self(sipe_private
);
1243 struct sipe_buddy
*buddy
= g_hash_table_lookup(sipe_private
->buddies
, self_uri
);
1246 buddy
= g_new0(struct sipe_buddy
, 1);
1247 buddy
->name
= g_strdup(self_uri
);
1248 g_hash_table_insert(sipe_private
->buddies
, buddy
->name
, buddy
);
1255 /* subscribe to buddies */
1256 if (!sip
->subscribed_buddies
) { //do it once, then count Expire field to schedule resubscribe.
1257 if (sip
->batched_support
) {
1258 sipe_subscribe_presence_batched(sipe_private
, NULL
);
1260 g_hash_table_foreach(sipe_private
->buddies
,
1261 (GHFunc
)sipe_buddy_subscribe_cb
,
1264 sip
->subscribed_buddies
= TRUE
;
1266 /* for 2005 systems schedule contacts' status update
1267 * based on their calendar information
1269 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
1270 sipe_sched_calendar_status_update(sipe_private
, time(NULL
));
1277 * Fires on deregistration event initiated by server.
1278 * [MS-SIPREGE] SIP extension.
1283 // Content-Type: text/registration-event
1284 // subscription-state: terminated;expires=0
1285 // ms-diagnostics-public: 4141;reason="User disabled"
1287 // deregistered;event=rejected
1289 static void sipe_process_registration_notify(struct sipe_core_private
*sipe_private
,
1292 const gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
1293 gchar
*event
= NULL
;
1294 gchar
*reason
= NULL
;
1297 SIPE_DEBUG_INFO_NOFORMAT("sipe_process_registration_notify: deregistration received.");
1299 if (!g_ascii_strncasecmp(contenttype
, "text/registration-event", 23)) {
1300 event
= sipmsg_find_part_of_header(msg
->body
, "event=", NULL
, NULL
);
1301 //@TODO have proper parameter extraction _by_name_ func, case insesitive.
1302 event
= event
? event
: sipmsg_find_part_of_header(msg
->body
, "event=", ";", NULL
);
1304 SIPE_DEBUG_INFO_NOFORMAT("sipe_process_registration_notify: unknown content type, exiting.");
1308 reason
= sipmsg_get_ms_diagnostics_reason(msg
);
1309 reason
= reason
? reason
: sipmsg_get_ms_diagnostics_public_reason(msg
);
1310 if (!reason
) { // for LCS2005
1311 if (event
&& sipe_strcase_equal(event
, "unregistered")) {
1312 //reason = g_strdup(_("User logged out")); // [MS-OCER]
1313 reason
= g_strdup(_("you are already signed in at another location"));
1314 } else if (event
&& sipe_strcase_equal(event
, "rejected")) {
1315 reason
= g_strdup(_("user disabled")); // [MS-OCER]
1316 } else if (event
&& sipe_strcase_equal(event
, "deactivated")) {
1317 reason
= g_strdup(_("user moved")); // [MS-OCER]
1321 warning
= g_strdup_printf(_("You have been rejected by the server: %s"), reason
? reason
: _("no reason given"));
1324 sipe_backend_connection_error(SIPE_CORE_PUBLIC
,
1325 SIPE_CONNECTION_ERROR_INVALID_USERNAME
,
1331 static void sipe_process_provisioning_v2(struct sipe_core_private
*sipe_private
,
1334 sipe_xml
*xn_provision_group_list
;
1335 const sipe_xml
*node
;
1337 xn_provision_group_list
= sipe_xml_parse(msg
->body
, msg
->bodylen
);
1339 /* provisionGroup */
1340 for (node
= sipe_xml_child(xn_provision_group_list
, "provisionGroup"); node
; node
= sipe_xml_twin(node
)) {
1341 if (sipe_strequal("ServerConfiguration", sipe_xml_attribute(node
, "name"))) {
1342 g_free(sipe_private
->focus_factory_uri
);
1343 sipe_private
->focus_factory_uri
= sipe_xml_data(sipe_xml_child(node
, "focusFactoryUri"));
1344 SIPE_DEBUG_INFO("sipe_process_provisioning_v2: sipe_private->focus_factory_uri=%s",
1345 sipe_private
->focus_factory_uri
? sipe_private
->focus_factory_uri
: "");
1348 g_free(sipe_private
->mras_uri
);
1349 sipe_private
->mras_uri
= g_strstrip(sipe_xml_data(sipe_xml_child(node
, "mrasUri")));
1350 SIPE_DEBUG_INFO("sipe_process_provisioning_v2: sipe_private->mras_uri=%s",
1351 sipe_private
->mras_uri
? sipe_private
->mras_uri
: "");
1353 if (sipe_private
->mras_uri
)
1354 sipe_media_get_av_edge_credentials(sipe_private
);
1359 sipe_xml_free(xn_provision_group_list
);
1362 /** for 2005 system */
1364 sipe_process_provisioning(struct sipe_core_private
*sipe_private
,
1367 sipe_xml
*xn_provision
;
1368 const sipe_xml
*node
;
1370 xn_provision
= sipe_xml_parse(msg
->body
, msg
->bodylen
);
1371 if ((node
= sipe_xml_child(xn_provision
, "user"))) {
1372 SIPE_DEBUG_INFO("sipe_process_provisioning: uri=%s", sipe_xml_attribute(node
, "uri"));
1373 if ((node
= sipe_xml_child(node
, "line"))) {
1374 const gchar
*line_uri
= sipe_xml_attribute(node
, "uri");
1375 const gchar
*server
= sipe_xml_attribute(node
, "server");
1376 SIPE_DEBUG_INFO("sipe_process_provisioning: line_uri=%s server=%s", line_uri
, server
);
1377 sip_csta_open(sipe_private
, line_uri
, server
);
1380 sipe_xml_free(xn_provision
);
1383 static void sipe_process_roaming_acl(struct sipe_core_private
*sipe_private
,
1386 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
1387 const gchar
*contacts_delta
;
1390 xml
= sipe_xml_parse(msg
->body
, msg
->bodylen
);
1396 contacts_delta
= sipe_xml_attribute(xml
, "deltaNum");
1399 sip
->acl_delta
= (int)g_ascii_strtod(contacts_delta
, NULL
);
1405 /** MS-PRES container */
1406 struct sipe_container
{
1411 /** MS-PRES container member */
1412 struct sipe_container_member
{
1413 /** user, domain, sameEnterprise, federated, publicCloud; everyone */
1419 free_container_member(struct sipe_container_member
*member
)
1421 if (!member
) return;
1423 g_free(member
->type
);
1424 g_free(member
->value
);
1429 free_container(struct sipe_container
*container
)
1433 if (!container
) return;
1435 entry
= container
->members
;
1437 void *data
= entry
->data
;
1438 entry
= g_slist_remove(entry
, data
);
1439 free_container_member((struct sipe_container_member
*)data
);
1445 sipe_send_container_members_prepare(const guint container_id
,
1446 const guint container_version
,
1447 const gchar
*action
,
1450 char **container_xmls
)
1452 gchar
*value_str
= value
? g_strdup_printf(" value=\"%s\"", value
) : g_strdup("");
1455 if (!container_xmls
) return;
1457 body
= g_strdup_printf(
1458 "<container id=\"%d\" version=\"%d\"><member action=\"%s\" type=\"%s\"%s/></container>",
1466 if ((*container_xmls
) == NULL
) {
1467 *container_xmls
= body
;
1469 char *tmp
= *container_xmls
;
1471 *container_xmls
= g_strconcat(*container_xmls
, body
, NULL
);
1478 sipe_send_set_container_members(struct sipe_core_private
*sipe_private
,
1479 char *container_xmls
)
1486 if (!container_xmls
) return;
1488 self
= sip_uri_self(sipe_private
);
1489 body
= g_strdup_printf(
1490 "<setContainerMembers xmlns=\"http://schemas.microsoft.com/2006/09/sip/container-management\">"
1492 "</setContainerMembers>",
1495 contact
= get_contact(sipe_private
);
1496 hdr
= g_strdup_printf("Contact: %s\r\n"
1497 "Content-Type: application/msrtc-setcontainermembers+xml\r\n", contact
);
1500 sip_transport_service(sipe_private
,
1512 * Finds locally stored MS-PRES container member
1514 static struct sipe_container_member
*
1515 sipe_find_container_member(struct sipe_container
*container
,
1519 struct sipe_container_member
*member
;
1522 if (container
== NULL
|| type
== NULL
) {
1526 entry
= container
->members
;
1528 member
= entry
->data
;
1529 if (sipe_strcase_equal(member
->type
, type
) &&
1530 sipe_strcase_equal(member
->value
, value
))
1534 entry
= entry
->next
;
1540 * Finds locally stored MS-PRES container by id
1542 static struct sipe_container
*
1543 sipe_find_container(struct sipe_core_private
*sipe_private
,
1546 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
1547 struct sipe_container
*container
;
1554 entry
= sip
->containers
;
1556 container
= entry
->data
;
1557 if (id
== container
->id
) {
1560 entry
= entry
->next
;
1566 sipe_get_access_domains(struct sipe_core_private
*sipe_private
)
1568 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
1569 struct sipe_container
*container
;
1570 struct sipe_container_member
*member
;
1575 if (!sip
) return NULL
;
1577 entry
= sip
->containers
;
1579 container
= entry
->data
;
1581 entry2
= container
->members
;
1583 member
= entry2
->data
;
1584 if (sipe_strcase_equal(member
->type
, "domain"))
1586 res
= slist_insert_unique_sorted(res
, g_strdup(member
->value
), (GCompareFunc
)g_ascii_strcasecmp
);
1588 entry2
= entry2
->next
;
1590 entry
= entry
->next
;
1596 * Returns pointer to domain part in provided Email URL
1598 * @param email an email URL. Example: first.last@hq.company.com
1599 * @return pointer to domain part of email URL. Coresponding example: hq.company.com
1601 * Doesn't allocate memory
1604 sipe_get_domain(const char *email
)
1608 if (!email
) return NULL
;
1610 tmp
= strstr(email
, "@");
1612 if (tmp
&& ((tmp
+1) < (email
+ strlen(email
)))) {
1620 /* @TODO: replace with binary search for faster access? */
1621 /** source: http://support.microsoft.com/kb/897567 */
1622 static const char * const public_domains
[] = {
1623 "aol.com", "icq.com", "love.com", "mac.com", "br.live.com",
1624 "hotmail.co.il", "hotmail.co.jp", "hotmail.co.th", "hotmail.co.uk",
1625 "hotmail.com", "hotmail.com.ar", "hotmail.com.tr", "hotmail.es",
1626 "hotmail.de", "hotmail.fr", "hotmail.it", "live.at", "live.be",
1627 "live.ca", "live.cl", "live.cn", "live.co.in", "live.co.kr",
1628 "live.co.uk", "live.co.za", "live.com", "live.com.ar", "live.com.au",
1629 "live.com.co", "live.com.mx", "live.com.my", "live.com.pe",
1630 "live.com.ph", "live.com.pk", "live.com.pt", "live.com.sg",
1631 "live.com.ve", "live.de", "live.dk", "live.fr", "live.hk", "live.ie",
1632 "live.in", "live.it", "live.jp", "live.nl", "live.no", "live.ph",
1633 "live.ru", "live.se", "livemail.com.br", "livemail.tw",
1634 "messengeruser.com", "msn.com", "passport.com", "sympatico.ca",
1635 "tw.live.com", "webtv.net", "windowslive.com", "windowslive.es",
1640 sipe_is_public_domain(const char *domain
)
1643 while (public_domains
[i
]) {
1644 if (sipe_strcase_equal(public_domains
[i
], domain
)) {
1661 sipe_get_access_level_name(int container_id
)
1663 switch(container_id
) {
1664 case 32000: return _("Blocked");
1665 case 400: return _("Personal");
1666 case 300: return _("Team");
1667 case 200: return _("Company");
1668 case 100: return _("Public");
1670 return _("Unknown");
1673 static const guint containers
[] = {32000, 400, 300, 200, 100};
1674 #define CONTAINERS_LEN (sizeof(containers) / sizeof(guint))
1678 sipe_find_member_access_level(struct sipe_core_private
*sipe_private
,
1683 const gchar
*value_mod
= value
;
1685 if (!type
) return -1;
1687 if (sipe_strequal("user", type
)) {
1688 value_mod
= sipe_get_no_sip_uri(value
);
1691 for (i
= 0; i
< CONTAINERS_LEN
; i
++) {
1692 struct sipe_container_member
*member
;
1693 struct sipe_container
*container
= sipe_find_container(sipe_private
, containers
[i
]);
1694 if (!container
) continue;
1696 member
= sipe_find_container_member(container
, type
, value_mod
);
1697 if (member
) return containers
[i
];
1703 /** Member type: user, domain, sameEnterprise, federated, publicCloud; everyone */
1705 sipe_find_access_level(struct sipe_core_private
*sipe_private
,
1708 gboolean
*is_group_access
)
1710 int container_id
= -1;
1712 if (sipe_strequal("user", type
)) {
1714 const char *no_sip_uri
= sipe_get_no_sip_uri(value
);
1716 container_id
= sipe_find_member_access_level(sipe_private
, "user", no_sip_uri
);
1717 if (container_id
>= 0) {
1718 if (is_group_access
) *is_group_access
= FALSE
;
1719 return container_id
;
1722 domain
= sipe_get_domain(no_sip_uri
);
1723 container_id
= sipe_find_member_access_level(sipe_private
, "domain", domain
);
1724 if (container_id
>= 0) {
1725 if (is_group_access
) *is_group_access
= TRUE
;
1726 return container_id
;
1729 container_id
= sipe_find_member_access_level(sipe_private
, "sameEnterprise", NULL
);
1730 if ((container_id
>= 0) && sipe_strcase_equal(sipe_private
->public.sip_domain
, domain
)) {
1731 if (is_group_access
) *is_group_access
= TRUE
;
1732 return container_id
;
1735 container_id
= sipe_find_member_access_level(sipe_private
, "publicCloud", NULL
);
1736 if ((container_id
>= 0) && sipe_is_public_domain(domain
)) {
1737 if (is_group_access
) *is_group_access
= TRUE
;
1738 return container_id
;
1741 container_id
= sipe_find_member_access_level(sipe_private
, "everyone", NULL
);
1742 if ((container_id
>= 0)) {
1743 if (is_group_access
) *is_group_access
= TRUE
;
1744 return container_id
;
1747 container_id
= sipe_find_member_access_level(sipe_private
, type
, value
);
1748 if (is_group_access
) *is_group_access
= FALSE
;
1751 return container_id
;
1755 * @param container_id a new access level. If -1 then current access level
1756 * is just removed (I.e. the member is removed from all containers).
1757 * @param type a type of member. E.g. "user", "sameEnterprise", etc.
1758 * @param value a value for member. E.g. SIP URI for "user" member type.
1761 sipe_change_access_level(struct sipe_core_private
*sipe_private
,
1762 const int container_id
,
1767 int current_container_id
= -1;
1768 char *container_xmls
= NULL
;
1770 /* for each container: find/delete */
1771 for (i
= 0; i
< CONTAINERS_LEN
; i
++) {
1772 struct sipe_container_member
*member
;
1773 struct sipe_container
*container
= sipe_find_container(sipe_private
, containers
[i
]);
1775 if (!container
) continue;
1777 member
= sipe_find_container_member(container
, type
, value
);
1779 current_container_id
= containers
[i
];
1780 /* delete/publish current access level */
1781 if (container_id
< 0 || container_id
!= current_container_id
) {
1782 sipe_send_container_members_prepare(current_container_id
, container
->version
, "remove", type
, value
, &container_xmls
);
1783 /* remove member from our cache, to be able to recalculate AL below */
1784 container
->members
= g_slist_remove(container
->members
, member
);
1785 current_container_id
= -1;
1790 /* recalculate AL below */
1791 current_container_id
= sipe_find_access_level(sipe_private
, type
, value
, NULL
);
1793 /* assign/publish new access level */
1794 if (container_id
!= current_container_id
&& container_id
>= 0) {
1795 struct sipe_container
*container
= sipe_find_container(sipe_private
, container_id
);
1796 guint version
= container
? container
->version
: 0;
1798 sipe_send_container_members_prepare(container_id
, version
, "add", type
, value
, &container_xmls
);
1801 if (container_xmls
) {
1802 sipe_send_set_container_members(sipe_private
, container_xmls
);
1804 g_free(container_xmls
);
1808 free_publication(struct sipe_publication
*publication
)
1810 g_free(publication
->category
);
1811 g_free(publication
->cal_event_hash
);
1812 g_free(publication
->note
);
1814 g_free(publication
->working_hours_xml_str
);
1815 g_free(publication
->fb_start_str
);
1816 g_free(publication
->free_busy_base64
);
1818 g_free(publication
);
1821 /* key is <category><instance><container> */
1823 sipe_is_our_publication(struct sipe_core_private
*sipe_private
,
1826 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
1829 /* filling keys for our publications if not yet cached */
1830 if (!sip
->our_publication_keys
) {
1831 guint device_instance
= sipe_get_pub_instance(sipe_private
, SIPE_PUB_DEVICE
);
1832 guint machine_instance
= sipe_get_pub_instance(sipe_private
, SIPE_PUB_STATE_MACHINE
);
1833 guint user_instance
= sipe_get_pub_instance(sipe_private
, SIPE_PUB_STATE_USER
);
1834 guint calendar_instance
= sipe_get_pub_instance(sipe_private
, SIPE_PUB_STATE_CALENDAR
);
1835 guint cal_oof_instance
= sipe_get_pub_instance(sipe_private
, SIPE_PUB_STATE_CALENDAR_OOF
);
1836 guint cal_data_instance
= sipe_get_pub_instance(sipe_private
, SIPE_PUB_CALENDAR_DATA
);
1837 guint note_oof_instance
= sipe_get_pub_instance(sipe_private
, SIPE_PUB_NOTE_OOF
);
1839 SIPE_DEBUG_INFO_NOFORMAT("* Our Publication Instances *");
1840 SIPE_DEBUG_INFO("\tDevice : %u\t0x%08X", device_instance
, device_instance
);
1841 SIPE_DEBUG_INFO("\tMachine State : %u\t0x%08X", machine_instance
, machine_instance
);
1842 SIPE_DEBUG_INFO("\tUser Stare : %u\t0x%08X", user_instance
, user_instance
);
1843 SIPE_DEBUG_INFO("\tCalendar State : %u\t0x%08X", calendar_instance
, calendar_instance
);
1844 SIPE_DEBUG_INFO("\tCalendar OOF State : %u\t0x%08X", cal_oof_instance
, cal_oof_instance
);
1845 SIPE_DEBUG_INFO("\tCalendar FreeBusy : %u\t0x%08X", cal_data_instance
, cal_data_instance
);
1846 SIPE_DEBUG_INFO("\tOOF Note : %u\t0x%08X", note_oof_instance
, note_oof_instance
);
1847 SIPE_DEBUG_INFO("\tNote : %u", 0);
1848 SIPE_DEBUG_INFO("\tCalendar WorkingHours: %u", 0);
1851 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
1852 g_strdup_printf("<%s><%u><%u>", "device", device_instance
, 2));
1854 /* state:machineState */
1855 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
1856 g_strdup_printf("<%s><%u><%u>", "state", machine_instance
, 2));
1857 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
1858 g_strdup_printf("<%s><%u><%u>", "state", machine_instance
, 3));
1860 /* state:userState */
1861 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
1862 g_strdup_printf("<%s><%u><%u>", "state", user_instance
, 2));
1863 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
1864 g_strdup_printf("<%s><%u><%u>", "state", user_instance
, 3));
1866 /* state:calendarState */
1867 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
1868 g_strdup_printf("<%s><%u><%u>", "state", calendar_instance
, 2));
1869 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
1870 g_strdup_printf("<%s><%u><%u>", "state", calendar_instance
, 3));
1872 /* state:calendarState OOF */
1873 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
1874 g_strdup_printf("<%s><%u><%u>", "state", cal_oof_instance
, 2));
1875 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
1876 g_strdup_printf("<%s><%u><%u>", "state", cal_oof_instance
, 3));
1879 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
1880 g_strdup_printf("<%s><%u><%u>", "note", 0, 200));
1881 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
1882 g_strdup_printf("<%s><%u><%u>", "note", 0, 300));
1883 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
1884 g_strdup_printf("<%s><%u><%u>", "note", 0, 400));
1887 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
1888 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance
, 200));
1889 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
1890 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance
, 300));
1891 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
1892 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance
, 400));
1894 /* calendarData:WorkingHours */
1895 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
1896 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 1));
1897 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
1898 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 100));
1899 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
1900 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 200));
1901 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
1902 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 300));
1903 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
1904 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 400));
1905 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
1906 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 32000));
1908 /* calendarData:FreeBusy */
1909 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
1910 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 1));
1911 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
1912 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 100));
1913 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
1914 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 200));
1915 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
1916 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 300));
1917 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
1918 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 400));
1919 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
1920 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 32000));
1922 //SIPE_DEBUG_INFO("sipe_is_our_publication: sip->our_publication_keys length=%d",
1923 // sip->our_publication_keys ? (int) g_slist_length(sip->our_publication_keys) : -1);
1926 //SIPE_DEBUG_INFO("sipe_is_our_publication: key=%s", key);
1928 entry
= sip
->our_publication_keys
;
1930 //SIPE_DEBUG_INFO(" sipe_is_our_publication: entry->data=%s", entry->data);
1931 if (sipe_strequal(entry
->data
, key
)) {
1934 entry
= entry
->next
;
1939 /** Property names to store in blist.xml */
1940 #define ALIAS_PROP "alias"
1941 #define EMAIL_PROP "email"
1942 #define PHONE_PROP "phone"
1943 #define PHONE_DISPLAY_PROP "phone-display"
1944 #define PHONE_MOBILE_PROP "phone-mobile"
1945 #define PHONE_MOBILE_DISPLAY_PROP "phone-mobile-display"
1946 #define PHONE_HOME_PROP "phone-home"
1947 #define PHONE_HOME_DISPLAY_PROP "phone-home-display"
1948 #define PHONE_OTHER_PROP "phone-other"
1949 #define PHONE_OTHER_DISPLAY_PROP "phone-other-display"
1950 #define PHONE_CUSTOM1_PROP "phone-custom1"
1951 #define PHONE_CUSTOM1_DISPLAY_PROP "phone-custom1-display"
1952 #define SITE_PROP "site"
1953 #define COMPANY_PROP "company"
1954 #define DEPARTMENT_PROP "department"
1955 #define TITLE_PROP "title"
1956 #define OFFICE_PROP "office"
1957 /** implies work address */
1958 #define ADDRESS_STREET_PROP "address-street"
1959 #define ADDRESS_CITY_PROP "address-city"
1960 #define ADDRESS_STATE_PROP "address-state"
1961 #define ADDRESS_ZIPCODE_PROP "address-zipcode"
1962 #define ADDRESS_COUNTRYCODE_PROP "address-country-code"
1965 * Tries to figure out user first and last name
1966 * based on Display Name and email properties.
1968 * Allocates memory - must be g_free()'d
1970 * Examples to parse:
1972 * First Last - Company Name
1975 * Last, First (C)(STP) (Company)
1976 * first.last@company.com (preprocessed as "first last")
1977 * first.last.company.com@reuters.net (preprocessed as "first last company com")
1979 * Unusable examples:
1980 * user@company.com (preprocessed as "user")
1981 * first.m.last@company.com (preprocessed as "first m last")
1982 * user.company.com@reuters.net (preprocessed as "user company com")
1985 sipe_get_first_last_names(struct sipe_core_private
*sipe_private
,
1990 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
1991 sipe_backend_buddy p_buddy
;
1994 const char *first
, *last
;
1997 gboolean has_comma
= FALSE
;
1999 if (!sip
|| !uri
) return;
2001 p_buddy
= sipe_backend_buddy_find(SIPE_CORE_PUBLIC
, uri
, NULL
);
2003 if (!p_buddy
) return;
2005 display_name
= sipe_backend_buddy_get_alias(SIPE_CORE_PUBLIC
, p_buddy
);
2006 email
= sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC
, p_buddy
, SIPE_BUDDY_INFO_EMAIL
);
2008 if (!display_name
&& !email
) return;
2010 /* if no display name, make "first last anything_else" out of email */
2011 if (email
&& !display_name
) {
2012 display_name
= g_strndup(email
, strstr(email
, "@") - email
);
2013 display_name
= sipe_utils_str_replace((tmp
= display_name
), ".", " ");
2018 has_comma
= (strstr(display_name
, ",") != NULL
);
2019 display_name
= sipe_utils_str_replace((tmp
= display_name
), ", ", " ");
2021 display_name
= sipe_utils_str_replace((tmp
= display_name
), ",", " ");
2025 parts
= g_strsplit(display_name
, " ", 0);
2027 if (!parts
[0] || !parts
[1]) {
2029 g_free(display_name
);
2043 *first_name
= g_strstrip(g_strdup(first
));
2047 *last_name
= g_strstrip(g_strdup(last
));
2051 g_free(display_name
);
2056 * Update user information
2058 * @param uri buddy SIP URI with 'sip:' prefix whose info we want to change.
2059 * @param property_name
2060 * @param property_value may be modified to strip white space
2063 sipe_update_user_info(struct sipe_core_private
*sipe_private
,
2065 sipe_buddy_info_fields propkey
,
2066 char *property_value
)
2068 GSList
*buddies
, *entry
;
2071 property_value
= g_strstrip(property_value
);
2073 entry
= buddies
= sipe_backend_buddy_find_all(SIPE_CORE_PUBLIC
, uri
, NULL
); /* all buddies in different groups */
2076 gchar
*server_alias
;
2078 sipe_backend_buddy p_buddy
= entry
->data
;
2080 /* for Display Name */
2081 if (propkey
== SIPE_BUDDY_INFO_DISPLAY_NAME
) {
2082 alias
= sipe_backend_buddy_get_alias(SIPE_CORE_PUBLIC
, p_buddy
);
2083 if (property_value
&& sipe_is_bad_alias(uri
, alias
)) {
2084 SIPE_DEBUG_INFO("Replacing alias for %s with %s", uri
, property_value
);
2085 sipe_backend_buddy_set_alias(SIPE_CORE_PUBLIC
, p_buddy
, property_value
);
2089 server_alias
= sipe_backend_buddy_get_server_alias(SIPE_CORE_PUBLIC
, p_buddy
);
2090 if (!is_empty(property_value
) &&
2091 (!sipe_strequal(property_value
, server_alias
) || is_empty(server_alias
)) )
2093 SIPE_DEBUG_INFO("Replacing service alias for %s with %s", uri
, property_value
);
2094 sipe_backend_buddy_set_server_alias(SIPE_CORE_PUBLIC
, p_buddy
, property_value
);
2096 g_free(server_alias
);
2098 /* for other properties */
2100 if (!is_empty(property_value
)) {
2101 prop_str
= sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC
, p_buddy
, propkey
);
2102 if (!prop_str
|| !sipe_strcase_equal(prop_str
, property_value
)) {
2103 sipe_backend_buddy_set_string(SIPE_CORE_PUBLIC
, p_buddy
, propkey
, property_value
);
2109 entry
= entry
->next
;
2111 g_slist_free(buddies
);
2116 * Suitable for both 2005 and 2007 systems.
2118 * @param uri buddy SIP URI with 'sip:' prefix whose info we want to change.
2120 * @param phone may be modified to strip white space
2121 * @param phone_display_string may be modified to strip white space
2124 sipe_update_user_phone(struct sipe_core_private
*sipe_private
,
2126 const gchar
*phone_type
,
2128 gchar
*phone_display_string
)
2130 sipe_buddy_info_fields phone_node
= SIPE_BUDDY_INFO_WORK_PHONE
; /* work phone by default */
2131 sipe_buddy_info_fields phone_display_node
= SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY
; /* work phone by default */
2133 if(!phone
|| strlen(phone
) == 0) return;
2135 if ((sipe_strequal(phone_type
, "mobile") || sipe_strequal(phone_type
, "cell"))) {
2136 phone_node
= SIPE_BUDDY_INFO_MOBILE_PHONE
;
2137 phone_display_node
= SIPE_BUDDY_INFO_MOBILE_PHONE_DISPLAY
;
2138 } else if (sipe_strequal(phone_type
, "home")) {
2139 phone_node
= SIPE_BUDDY_INFO_HOME_PHONE
;
2140 phone_display_node
= SIPE_BUDDY_INFO_HOME_PHONE_DISPLAY
;
2141 } else if (sipe_strequal(phone_type
, "other")) {
2142 phone_node
= SIPE_BUDDY_INFO_OTHER_PHONE
;
2143 phone_display_node
= SIPE_BUDDY_INFO_OTHER_PHONE_DISPLAY
;
2144 } else if (sipe_strequal(phone_type
, "custom1")) {
2145 phone_node
= SIPE_BUDDY_INFO_CUSTOM1_PHONE
;
2146 phone_display_node
= SIPE_BUDDY_INFO_CUSTOM1_PHONE_DISPLAY
;
2149 sipe_update_user_info(sipe_private
, uri
, phone_node
, phone
);
2150 if (phone_display_string
) {
2151 sipe_update_user_info(sipe_private
, uri
, phone_display_node
, phone_display_string
);
2156 sipe_core_update_calendar(struct sipe_core_public
*sipe_public
)
2158 SIPE_DEBUG_INFO_NOFORMAT("sipe_core_update_calendar: started.");
2161 * If failed, the branch will be disabled for subsequent calls.
2162 * Can't rely that user turned the functionality on in account settings.
2164 sipe_ews_update_calendar(SIPE_CORE_PRIVATE
);
2165 sipe_domino_update_calendar(SIPE_CORE_PRIVATE
);
2167 /* schedule repeat */
2168 sipe_schedule_seconds(SIPE_CORE_PRIVATE
,
2169 "<+update-calendar>",
2171 UPDATE_CALENDAR_INTERVAL
,
2172 (sipe_schedule_action
)sipe_core_update_calendar
,
2175 SIPE_DEBUG_INFO_NOFORMAT("sipe_core_update_calendar: finished.");
2179 * This method motivates Purple's Host (e.g. Pidgin) to update its UI
2180 * by using standard Purple's means of signals and saved statuses.
2182 * Thus all UI elements get updated: Status Button with Note, docklet.
2183 * This is ablolutely important as both our status and note can come
2184 * inbound (roaming) or be updated programmatically (e.g. based on our
2188 sipe_set_purple_account_status_and_note(const PurpleAccount
*account
,
2189 const char *status_id
,
2190 const char *message
,
2191 time_t do_not_publish
[])
2193 PurpleStatus
*status
= purple_account_get_active_status(account
);
2194 gboolean changed
= TRUE
;
2196 if (g_str_equal(status_id
, purple_status_get_id(status
)) &&
2197 sipe_strequal(message
, purple_status_get_attr_string(status
, SIPE_STATUS_ATTR_ID_MESSAGE
)))
2202 if (purple_savedstatus_is_idleaway()) {
2207 PurpleSavedStatus
*saved_status
;
2208 const PurpleStatusType
*acct_status_type
=
2209 purple_status_type_find_with_id(account
->status_types
, status_id
);
2210 PurpleStatusPrimitive primitive
= purple_status_type_get_primitive(acct_status_type
);
2211 sipe_activity activity
= sipe_get_activity_by_token(status_id
);
2213 saved_status
= purple_savedstatus_find_transient_by_type_and_message(primitive
, message
);
2215 purple_savedstatus_set_substatus(saved_status
, account
, acct_status_type
, message
);
2218 /* If this type+message is unique then create a new transient saved status
2219 * Ref: gtkstatusbox.c
2221 if (!saved_status
) {
2223 GList
*active_accts
= purple_accounts_get_all_active();
2225 saved_status
= purple_savedstatus_new(NULL
, primitive
);
2226 purple_savedstatus_set_message(saved_status
, message
);
2228 for (tmp
= active_accts
; tmp
!= NULL
; tmp
= tmp
->next
) {
2229 purple_savedstatus_set_substatus(saved_status
,
2230 (PurpleAccount
*)tmp
->data
, acct_status_type
, message
);
2232 g_list_free(active_accts
);
2235 do_not_publish
[activity
] = time(NULL
);
2236 SIPE_DEBUG_INFO("sipe_set_purple_account_status_and_note: do_not_publish[%s]=%d [now]",
2237 status_id
, (int)do_not_publish
[activity
]);
2239 /* Set the status for each account */
2240 purple_savedstatus_activate(saved_status
);
2244 struct hash_table_delete_payload
{
2245 GHashTable
*hash_table
;
2250 sipe_remove_category_container_publications_cb(const char *name
,
2251 struct sipe_publication
*publication
,
2252 struct hash_table_delete_payload
*payload
)
2254 if (publication
->container
== payload
->container
) {
2255 g_hash_table_remove(payload
->hash_table
, name
);
2259 sipe_remove_category_container_publications(GHashTable
*our_publications
,
2260 const char *category
,
2263 struct hash_table_delete_payload payload
;
2264 payload
.hash_table
= g_hash_table_lookup(our_publications
, category
);
2266 if (!payload
.hash_table
) return;
2268 payload
.container
= container
;
2269 g_hash_table_foreach(payload
.hash_table
, (GHFunc
)sipe_remove_category_container_publications_cb
, &payload
);
2273 send_publish_category_initial(struct sipe_core_private
*sipe_private
);
2276 * When we receive some self (BE) NOTIFY with a new subscriber
2277 * we sends a setSubscribers request to him [SIP-PRES] 4.8
2280 static void sipe_process_roaming_self(struct sipe_core_private
*sipe_private
,
2283 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
2287 const sipe_xml
*node
;
2288 const sipe_xml
*node2
;
2289 char *display_name
= NULL
;
2291 GSList
*category_names
= NULL
;
2292 int aggreg_avail
= 0;
2293 gboolean do_update_status
= FALSE
;
2294 gboolean has_note_cleaned
= FALSE
;
2295 GHashTable
*devices
;
2297 SIPE_DEBUG_INFO_NOFORMAT("sipe_process_roaming_self");
2299 xml
= sipe_xml_parse(msg
->body
, msg
->bodylen
);
2302 contact
= get_contact(sipe_private
);
2303 to
= sip_uri_self(sipe_private
);
2306 /* set list of categories participating in this XML */
2307 for (node
= sipe_xml_child(xml
, "categories/category"); node
; node
= sipe_xml_twin(node
)) {
2308 const gchar
*name
= sipe_xml_attribute(node
, "name");
2309 category_names
= slist_insert_unique_sorted(category_names
, (gchar
*)name
, (GCompareFunc
)strcmp
);
2311 SIPE_DEBUG_INFO("sipe_process_roaming_self: category_names length=%d",
2312 category_names
? (int) g_slist_length(category_names
) : -1);
2313 /* drop category information */
2314 if (category_names
) {
2315 GSList
*entry
= category_names
;
2317 GHashTable
*cat_publications
;
2318 const gchar
*category
= entry
->data
;
2319 entry
= entry
->next
;
2320 SIPE_DEBUG_INFO("sipe_process_roaming_self: dropping category: %s", category
);
2321 cat_publications
= g_hash_table_lookup(sip
->our_publications
, category
);
2322 if (cat_publications
) {
2323 g_hash_table_remove(sip
->our_publications
, category
);
2324 SIPE_DEBUG_INFO("sipe_process_roaming_self: dropped category: %s", category
);
2328 g_slist_free(category_names
);
2330 /* filling our categories reflected in roaming data */
2331 devices
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
2333 for (node
= sipe_xml_child(xml
, "categories/category"); node
; node
= sipe_xml_twin(node
)) {
2335 const gchar
*name
= sipe_xml_attribute(node
, "name");
2336 guint container
= sipe_xml_int_attribute(node
, "container", -1);
2337 guint instance
= sipe_xml_int_attribute(node
, "instance", -1);
2338 guint version
= sipe_xml_int_attribute(node
, "version", 0);
2339 time_t publish_time
= (tmp
= sipe_xml_attribute(node
, "publishTime")) ?
2340 sipe_utils_str_to_time(tmp
) : 0;
2342 GHashTable
*cat_publications
= g_hash_table_lookup(sip
->our_publications
, name
);
2344 /* Ex. clear note: <category name="note"/> */
2345 if (container
== (guint
)-1) {
2348 do_update_status
= TRUE
;
2352 /* Ex. clear note: <category name="note" container="200"/> */
2353 if (instance
== (guint
)-1) {
2354 if (container
== 200) {
2357 do_update_status
= TRUE
;
2359 SIPE_DEBUG_INFO("sipe_process_roaming_self: removing publications for: %s/%u", name
, container
);
2360 sipe_remove_category_container_publications(
2361 sip
->our_publications
, name
, container
);
2365 /* key is <category><instance><container> */
2366 key
= g_strdup_printf("<%s><%u><%u>", name
, instance
, container
);
2367 SIPE_DEBUG_INFO("sipe_process_roaming_self: key=%s version=%d", key
, version
);
2369 /* capture all userState publication for later clean up if required */
2370 if (sipe_strequal(name
, "state") && (container
== 2 || container
== 3)) {
2371 const sipe_xml
*xn_state
= sipe_xml_child(node
, "state");
2373 if (xn_state
&& sipe_strequal(sipe_xml_attribute(xn_state
, "type"), "userState")) {
2374 struct sipe_publication
*publication
= g_new0(struct sipe_publication
, 1);
2375 publication
->category
= g_strdup(name
);
2376 publication
->instance
= instance
;
2377 publication
->container
= container
;
2378 publication
->version
= version
;
2380 if (!sip
->user_state_publications
) {
2381 sip
->user_state_publications
= g_hash_table_new_full(
2382 g_str_hash
, g_str_equal
,
2383 g_free
, (GDestroyNotify
)free_publication
);
2385 g_hash_table_insert(sip
->user_state_publications
, g_strdup(key
), publication
);
2386 SIPE_DEBUG_INFO("sipe_process_roaming_self: added to user_state_publications key=%s version=%d",
2391 /* count each client instance only once */
2392 if (sipe_strequal(name
, "device"))
2393 g_hash_table_replace(devices
, g_strdup_printf("%u", instance
), NULL
);
2395 if (sipe_is_our_publication(sipe_private
, key
)) {
2396 struct sipe_publication
*publication
= g_new0(struct sipe_publication
, 1);
2398 publication
->category
= g_strdup(name
);
2399 publication
->instance
= instance
;
2400 publication
->container
= container
;
2401 publication
->version
= version
;
2403 /* filling publication->availability */
2404 if (sipe_strequal(name
, "state")) {
2405 const sipe_xml
*xn_state
= sipe_xml_child(node
, "state");
2406 const sipe_xml
*xn_avail
= sipe_xml_child(xn_state
, "availability");
2409 gchar
*avail_str
= sipe_xml_data(xn_avail
);
2411 publication
->availability
= atoi(avail_str
);
2415 /* for calendarState */
2416 if (xn_state
&& sipe_strequal(sipe_xml_attribute(xn_state
, "type"), "calendarState")) {
2417 const sipe_xml
*xn_activity
= sipe_xml_child(xn_state
, "activity");
2418 struct sipe_cal_event
*event
= g_new0(struct sipe_cal_event
, 1);
2420 event
->start_time
= sipe_utils_str_to_time(sipe_xml_attribute(xn_state
, "startTime"));
2422 if (sipe_strequal(sipe_xml_attribute(xn_activity
, "token"),
2423 sipe_activity_map
[SIPE_ACTIVITY_IN_MEETING
].token
))
2425 event
->is_meeting
= TRUE
;
2428 event
->subject
= sipe_xml_data(sipe_xml_child(xn_state
, "meetingSubject"));
2429 event
->location
= sipe_xml_data(sipe_xml_child(xn_state
, "meetingLocation"));
2431 publication
->cal_event_hash
= sipe_cal_event_hash(event
);
2432 SIPE_DEBUG_INFO("sipe_process_roaming_self: hash=%s",
2433 publication
->cal_event_hash
);
2434 sipe_cal_event_free(event
);
2437 /* filling publication->note */
2438 if (sipe_strequal(name
, "note")) {
2439 const sipe_xml
*xn_body
= sipe_xml_child(node
, "note/body");
2441 if (!has_note_cleaned
) {
2442 has_note_cleaned
= TRUE
;
2446 sip
->note_since
= publish_time
;
2448 do_update_status
= TRUE
;
2451 g_free(publication
->note
);
2452 publication
->note
= NULL
;
2456 publication
->note
= g_markup_escape_text((tmp
= sipe_xml_data(xn_body
)), -1);
2458 if (publish_time
>= sip
->note_since
) {
2460 sip
->note
= g_strdup(publication
->note
);
2461 sip
->note_since
= publish_time
;
2462 sip
->is_oof_note
= sipe_strequal(sipe_xml_attribute(xn_body
, "type"), "OOF");
2464 do_update_status
= TRUE
;
2469 /* filling publication->fb_start_str, free_busy_base64, working_hours_xml_str */
2470 if (sipe_strequal(name
, "calendarData") && (publication
->container
== 300)) {
2471 const sipe_xml
*xn_free_busy
= sipe_xml_child(node
, "calendarData/freeBusy");
2472 const sipe_xml
*xn_working_hours
= sipe_xml_child(node
, "calendarData/WorkingHours");
2474 publication
->fb_start_str
= g_strdup(sipe_xml_attribute(xn_free_busy
, "startTime"));
2475 publication
->free_busy_base64
= sipe_xml_data(xn_free_busy
);
2477 if (xn_working_hours
) {
2478 publication
->working_hours_xml_str
= sipe_xml_stringify(xn_working_hours
);
2482 if (!cat_publications
) {
2483 cat_publications
= g_hash_table_new_full(
2484 g_str_hash
, g_str_equal
,
2485 g_free
, (GDestroyNotify
)free_publication
);
2486 g_hash_table_insert(sip
->our_publications
, g_strdup(name
), cat_publications
);
2487 SIPE_DEBUG_INFO("sipe_process_roaming_self: added GHashTable cat=%s", name
);
2489 g_hash_table_insert(cat_publications
, g_strdup(key
), publication
);
2490 SIPE_DEBUG_INFO("sipe_process_roaming_self: added key=%s version=%d", key
, version
);
2494 /* aggregateState (not an our publication) from 2-nd container */
2495 if (sipe_strequal(name
, "state") && container
== 2) {
2496 const sipe_xml
*xn_state
= sipe_xml_child(node
, "state");
2498 if (xn_state
&& sipe_strequal(sipe_xml_attribute(xn_state
, "type"), "aggregateState")) {
2499 const sipe_xml
*xn_avail
= sipe_xml_child(xn_state
, "availability");
2502 gchar
*avail_str
= sipe_xml_data(xn_avail
);
2504 aggreg_avail
= atoi(avail_str
);
2509 do_update_status
= TRUE
;
2513 /* userProperties published by server from AD */
2514 if (!sip
->csta
&& sipe_strequal(name
, "userProperties")) {
2515 const sipe_xml
*line
;
2516 /* line, for Remote Call Control (RCC) */
2517 for (line
= sipe_xml_child(node
, "userProperties/lines/line"); line
; line
= sipe_xml_twin(line
)) {
2518 const gchar
*line_server
= sipe_xml_attribute(line
, "lineServer");
2519 const gchar
*line_type
= sipe_xml_attribute(line
, "lineType");
2522 if (!line_server
|| !(sipe_strequal(line_type
, "Rcc") || sipe_strequal(line_type
, "Dual"))) continue;
2524 line_uri
= sipe_xml_data(line
);
2526 SIPE_DEBUG_INFO("sipe_process_roaming_self: line_uri=%s server=%s", line_uri
, line_server
);
2527 sip_csta_open(sipe_private
, line_uri
, line_server
);
2535 SIPE_DEBUG_INFO("sipe_process_roaming_self: sip->our_publications size=%d",
2536 sip
->our_publications
? (int) g_hash_table_size(sip
->our_publications
) : -1);
2538 /* active clients for user account */
2539 if (g_hash_table_size(devices
) > 1) {
2540 SIPE_CORE_PRIVATE_FLAG_SET(MPOP
);
2541 SIPE_DEBUG_INFO("sipe_process_roaming_self: multiple clients detected (%d)",
2542 g_hash_table_size(devices
));
2544 SIPE_CORE_PRIVATE_FLAG_UNSET(MPOP
);
2545 SIPE_DEBUG_INFO_NOFORMAT("sipe_process_roaming_self: single client detected");
2547 g_hash_table_destroy(devices
);
2550 for (node
= sipe_xml_child(xml
, "containers/container"); node
; node
= sipe_xml_twin(node
)) {
2551 guint id
= sipe_xml_int_attribute(node
, "id", 0);
2552 struct sipe_container
*container
= sipe_find_container(sipe_private
, id
);
2555 sip
->containers
= g_slist_remove(sip
->containers
, container
);
2556 SIPE_DEBUG_INFO("sipe_process_roaming_self: removed existing container id=%d v%d", container
->id
, container
->version
);
2557 free_container(container
);
2559 container
= g_new0(struct sipe_container
, 1);
2561 container
->version
= sipe_xml_int_attribute(node
, "version", 0);
2562 sip
->containers
= g_slist_append(sip
->containers
, container
);
2563 SIPE_DEBUG_INFO("sipe_process_roaming_self: added container id=%d v%d", container
->id
, container
->version
);
2565 for (node2
= sipe_xml_child(node
, "member"); node2
; node2
= sipe_xml_twin(node2
)) {
2566 struct sipe_container_member
*member
= g_new0(struct sipe_container_member
, 1);
2567 member
->type
= g_strdup(sipe_xml_attribute(node2
, "type"));
2568 member
->value
= g_strdup(sipe_xml_attribute(node2
, "value"));
2569 container
->members
= g_slist_append(container
->members
, member
);
2570 SIPE_DEBUG_INFO("sipe_process_roaming_self: added container member type=%s value=%s",
2571 member
->type
, member
->value
? member
->value
: "");
2575 SIPE_DEBUG_INFO("sipe_process_roaming_self: sip->access_level_set=%s", sip
->access_level_set
? "TRUE" : "FALSE");
2576 if (!sip
->access_level_set
&& sipe_xml_child(xml
, "containers")) {
2577 char *container_xmls
= NULL
;
2578 int sameEnterpriseAL
= sipe_find_access_level(sipe_private
, "sameEnterprise", NULL
, NULL
);
2579 int federatedAL
= sipe_find_access_level(sipe_private
, "federated", NULL
, NULL
);
2581 SIPE_DEBUG_INFO("sipe_process_roaming_self: sameEnterpriseAL=%d", sameEnterpriseAL
);
2582 SIPE_DEBUG_INFO("sipe_process_roaming_self: federatedAL=%d", federatedAL
);
2583 /* initial set-up to let counterparties see your status */
2584 if (sameEnterpriseAL
< 0) {
2585 struct sipe_container
*container
= sipe_find_container(sipe_private
, 200);
2586 guint version
= container
? container
->version
: 0;
2587 sipe_send_container_members_prepare(200, version
, "add", "sameEnterprise", NULL
, &container_xmls
);
2589 if (federatedAL
< 0) {
2590 struct sipe_container
*container
= sipe_find_container(sipe_private
, 100);
2591 guint version
= container
? container
->version
: 0;
2592 sipe_send_container_members_prepare(100, version
, "add", "federated", NULL
, &container_xmls
);
2594 sip
->access_level_set
= TRUE
;
2596 if (container_xmls
) {
2597 sipe_send_set_container_members(sipe_private
, container_xmls
);
2599 g_free(container_xmls
);
2602 /* Refresh contacts' blocked status */
2603 sipe_refresh_blocked_status(sipe_private
);
2606 for (node
= sipe_xml_child(xml
, "subscribers/subscriber"); node
; node
= sipe_xml_twin(node
)) {
2608 const char *acknowledged
;
2612 user
= sipe_xml_attribute(node
, "user"); /* without 'sip:' prefix */
2613 if (!user
) continue;
2614 SIPE_DEBUG_INFO("sipe_process_roaming_self: user %s", user
);
2615 display_name
= g_strdup(sipe_xml_attribute(node
, "displayName"));
2616 uri
= sip_uri_from_name(user
);
2618 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_DISPLAY_NAME
, display_name
);
2620 acknowledged
= sipe_xml_attribute(node
, "acknowledged");
2621 if(sipe_strcase_equal(acknowledged
,"false")){
2622 SIPE_DEBUG_INFO("sipe_process_roaming_self: user added you %s", user
);
2623 if (!sipe_backend_buddy_find(SIPE_CORE_PUBLIC
, uri
, NULL
)) {
2624 sipe_backend_buddy_request_add(SIPE_CORE_PUBLIC
, uri
, display_name
);
2627 hdr
= g_strdup_printf(
2629 "Content-Type: application/msrtc-presence-setsubscriber+xml\r\n", contact
);
2631 body
= g_strdup_printf(
2632 "<setSubscribers xmlns=\"http://schemas.microsoft.com/2006/09/sip/presence-subscribers\">"
2633 "<subscriber user=\"%s\" acknowledged=\"true\"/>"
2634 "</setSubscribers>", user
);
2636 sip_transport_service(sipe_private
,
2644 g_free(display_name
);
2651 /* Publish initial state if not yet.
2652 * Assuming this happens on initial responce to subscription to roaming-self
2653 * so we've already updated our roaming data in full.
2656 if (!sip
->initial_state_published
) {
2657 send_publish_category_initial(sipe_private
);
2658 sipe_groupchat_init(sipe_private
);
2659 sip
->initial_state_published
= TRUE
;
2661 sipe_schedule_seconds(sipe_private
,
2662 "<+update-calendar>",
2664 UPDATE_CALENDAR_DELAY
,
2665 (sipe_schedule_action
)sipe_core_update_calendar
,
2667 do_update_status
= FALSE
;
2668 } else if (aggreg_avail
) {
2670 g_free(sip
->status
);
2671 if (aggreg_avail
&& aggreg_avail
< 18000) { /* not offline */
2672 sip
->status
= g_strdup(sipe_get_status_by_availability(aggreg_avail
, NULL
));
2674 sip
->status
= g_strdup(SIPE_STATUS_ID_INVISIBLE
); /* not not let offline status switch us off */
2678 if (do_update_status
) {
2679 SIPE_DEBUG_INFO("sipe_process_roaming_self: switch to '%s' for the account", sip
->status
);
2680 sipe_set_purple_account_status_and_note(sip
->account
, sip
->status
, sip
->note
, sip
->do_not_publish
);
2686 /* IM Session (INVITE and MESSAGE methods) */
2689 process_options_response(SIPE_UNUSED_PARAMETER
struct sipe_core_private
*sipe_private
,
2691 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
2693 gboolean ret
= TRUE
;
2695 if (msg
->response
!= 200) {
2696 SIPE_DEBUG_INFO("process_options_response: OPTIONS response is %d", msg
->response
);
2700 SIPE_DEBUG_INFO("process_options_response: body:\n%s", msg
->body
? msg
->body
: "");
2706 * Asks UA/proxy about its capabilities.
2708 static void sipe_options_request(struct sipe_core_private
*sipe_private
,
2711 gchar
*to
= sip_uri(who
);
2712 gchar
*contact
= get_contact(sipe_private
);
2713 gchar
*request
= g_strdup_printf(
2714 "Accept: application/sdp\r\n"
2715 "Contact: %s\r\n", contact
);
2718 sip_transport_request(sipe_private
,
2725 process_options_response
);
2732 sipe_convo_closed(PurpleConnection
* gc
, const char *who
)
2734 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
2736 SIPE_DEBUG_INFO("conversation with %s closed", who
);
2737 sipe_session_close(sipe_private
,
2738 sipe_session_find_im(sipe_private
, who
));
2742 * Returns 2005-style activity and Availability.
2744 * @param status Sipe statis id.
2747 sipe_get_act_avail_by_status_2005(const char *status
,
2751 int avail
= 300; /* online */
2752 int act
= 400; /* Available */
2754 if (sipe_strequal(status
, SIPE_STATUS_ID_AWAY
)) {
2756 //} else if (sipe_strequal(status, SIPE_STATUS_ID_LUNCH)) {
2758 } else if (sipe_strequal(status
, SIPE_STATUS_ID_BRB
)) {
2760 } else if (sipe_strequal(status
, SIPE_STATUS_ID_AVAILABLE
)) {
2762 //} else if (sipe_strequal(status, SIPE_STATUS_ID_ON_PHONE)) {
2764 } else if (sipe_strequal(status
, SIPE_STATUS_ID_BUSY
) ||
2765 sipe_strequal(status
, SIPE_STATUS_ID_DND
)) {
2767 } else if (sipe_strequal(status
, SIPE_STATUS_ID_INVISIBLE
) ||
2768 sipe_strequal(status
, SIPE_STATUS_ID_OFFLINE
)) {
2769 avail
= 0; /* offline */
2772 act
= 400; /* Available */
2775 if (activity
) *activity
= act
;
2776 if (availability
) *availability
= avail
;
2782 * @param activity 2005 aggregated activity. Ex.: 600
2783 * @param availablity 2005 aggregated availablity. Ex.: 300
2786 sipe_get_status_by_act_avail_2005(const int activity
,
2787 const int availablity
,
2788 char **activity_desc
)
2790 const char *status_id
= NULL
;
2791 const char *act
= NULL
;
2793 if (activity
< 150) {
2794 status_id
= SIPE_STATUS_ID_AWAY
;
2795 } else if (activity
< 200) {
2796 //status_id = SIPE_STATUS_ID_LUNCH;
2797 status_id
= SIPE_STATUS_ID_AWAY
;
2798 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_LUNCH
);
2799 } else if (activity
< 300) {
2800 //status_id = SIPE_STATUS_ID_IDLE;
2801 status_id
= SIPE_STATUS_ID_AWAY
;
2802 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_INACTIVE
);
2803 } else if (activity
< 400) {
2804 status_id
= SIPE_STATUS_ID_BRB
;
2805 } else if (activity
< 500) {
2806 status_id
= SIPE_STATUS_ID_AVAILABLE
;
2807 } else if (activity
< 600) {
2808 //status_id = SIPE_STATUS_ID_ON_PHONE;
2809 status_id
= SIPE_STATUS_ID_BUSY
;
2810 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_ON_PHONE
);
2811 } else if (activity
< 700) {
2812 status_id
= SIPE_STATUS_ID_BUSY
;
2813 } else if (activity
< 800) {
2814 status_id
= SIPE_STATUS_ID_AWAY
;
2816 status_id
= SIPE_STATUS_ID_AVAILABLE
;
2819 if (availablity
< 100)
2820 status_id
= SIPE_STATUS_ID_OFFLINE
;
2822 if (activity_desc
&& act
) {
2823 g_free(*activity_desc
);
2824 *activity_desc
= g_strdup(act
);
2831 * [MS-PRES] Table 3: Conversion of legacyInterop elements and attributes to MSRTC elements and attributes.
2834 sipe_get_status_by_availability(int avail
,
2835 char** activity_desc
)
2838 const char *act
= NULL
;
2841 status
= SIPE_STATUS_ID_OFFLINE
;
2842 } else if (avail
< 4500) {
2843 status
= SIPE_STATUS_ID_AVAILABLE
;
2844 } else if (avail
< 6000) {
2845 //status = SIPE_STATUS_ID_IDLE;
2846 status
= SIPE_STATUS_ID_AVAILABLE
;
2847 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_INACTIVE
);
2848 } else if (avail
< 7500) {
2849 status
= SIPE_STATUS_ID_BUSY
;
2850 } else if (avail
< 9000) {
2851 //status = SIPE_STATUS_ID_BUSYIDLE;
2852 status
= SIPE_STATUS_ID_BUSY
;
2853 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_BUSYIDLE
);
2854 } else if (avail
< 12000) {
2855 status
= SIPE_STATUS_ID_DND
;
2856 } else if (avail
< 15000) {
2857 status
= SIPE_STATUS_ID_BRB
;
2858 } else if (avail
< 18000) {
2859 status
= SIPE_STATUS_ID_AWAY
;
2861 status
= SIPE_STATUS_ID_OFFLINE
;
2864 if (activity_desc
&& act
) {
2865 g_free(*activity_desc
);
2866 *activity_desc
= g_strdup(act
);
2873 * Returns 2007-style availability value
2875 * @param sipe_status_id (in)
2876 * @param activity_token (out) Must be g_free()'d after use if consumed.
2879 sipe_get_availability_by_status(const char* sipe_status_id
, char** activity_token
)
2882 sipe_activity activity
= SIPE_ACTIVITY_UNSET
;
2884 if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_AWAY
)) {
2885 availability
= 15500;
2886 if (!activity_token
|| !(*activity_token
)) {
2887 activity
= SIPE_ACTIVITY_AWAY
;
2889 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_BRB
)) {
2890 availability
= 12500;
2891 activity
= SIPE_ACTIVITY_BRB
;
2892 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_DND
)) {
2893 availability
= 9500;
2894 activity
= SIPE_ACTIVITY_DND
;
2895 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_BUSY
)) {
2896 availability
= 6500;
2897 if (!activity_token
|| !(*activity_token
)) {
2898 activity
= SIPE_ACTIVITY_BUSY
;
2900 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_AVAILABLE
)) {
2901 availability
= 3500;
2902 activity
= SIPE_ACTIVITY_ONLINE
;
2903 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_UNKNOWN
)) {
2906 // Offline or invisible
2907 availability
= 18500;
2908 activity
= SIPE_ACTIVITY_OFFLINE
;
2911 if (activity_token
) {
2912 *activity_token
= g_strdup(sipe_activity_map
[activity
].token
);
2914 return availability
;
2917 static void process_incoming_notify_rlmi(struct sipe_core_private
*sipe_private
,
2918 const gchar
*data
, unsigned len
)
2920 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
2922 sipe_xml
*xn_categories
;
2923 const sipe_xml
*xn_category
;
2924 const char *status
= NULL
;
2925 gboolean do_update_status
= FALSE
;
2926 gboolean has_note_cleaned
= FALSE
;
2927 gboolean has_free_busy_cleaned
= FALSE
;
2929 xn_categories
= sipe_xml_parse(data
, len
);
2930 uri
= sipe_xml_attribute(xn_categories
, "uri"); /* with 'sip:' prefix */
2932 for (xn_category
= sipe_xml_child(xn_categories
, "category");
2934 xn_category
= sipe_xml_twin(xn_category
) )
2936 const sipe_xml
*xn_node
;
2938 const char *attrVar
= sipe_xml_attribute(xn_category
, "name");
2939 time_t publish_time
= (tmp
= sipe_xml_attribute(xn_category
, "publishTime")) ?
2940 sipe_utils_str_to_time(tmp
) : 0;
2943 if (sipe_strequal(attrVar
, "contactCard"))
2945 const sipe_xml
*card
= sipe_xml_child(xn_category
, "contactCard");
2948 const sipe_xml
*node
;
2949 /* identity - Display Name and email */
2950 node
= sipe_xml_child(card
, "identity");
2952 char* display_name
= sipe_xml_data(
2953 sipe_xml_child(node
, "name/displayName"));
2954 char* email
= sipe_xml_data(
2955 sipe_xml_child(node
, "email"));
2957 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_DISPLAY_NAME
, display_name
);
2958 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_EMAIL
, email
);
2960 g_free(display_name
);
2964 node
= sipe_xml_child(card
, "company");
2966 char* company
= sipe_xml_data(node
);
2967 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_COMPANY
, company
);
2971 node
= sipe_xml_child(card
, "department");
2973 char* department
= sipe_xml_data(node
);
2974 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_DEPARTMENT
, department
);
2978 node
= sipe_xml_child(card
, "title");
2980 char* title
= sipe_xml_data(node
);
2981 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_JOB_TITLE
, title
);
2985 node
= sipe_xml_child(card
, "office");
2987 char* office
= sipe_xml_data(node
);
2988 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_OFFICE
, office
);
2992 node
= sipe_xml_child(card
, "url");
2994 char* site
= sipe_xml_data(node
);
2995 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_SITE
, site
);
2999 for (node
= sipe_xml_child(card
, "phone");
3001 node
= sipe_xml_twin(node
))
3003 const char *phone_type
= sipe_xml_attribute(node
, "type");
3004 char* phone
= sipe_xml_data(sipe_xml_child(node
, "uri"));
3005 char* phone_display_string
= sipe_xml_data(sipe_xml_child(node
, "displayString"));
3007 sipe_update_user_phone(sipe_private
, uri
, phone_type
, phone
, phone_display_string
);
3010 g_free(phone_display_string
);
3013 for (node
= sipe_xml_child(card
, "address");
3015 node
= sipe_xml_twin(node
))
3017 if (sipe_strequal(sipe_xml_attribute(node
, "type"), "work")) {
3018 char* street
= sipe_xml_data(sipe_xml_child(node
, "street"));
3019 char* city
= sipe_xml_data(sipe_xml_child(node
, "city"));
3020 char* state
= sipe_xml_data(sipe_xml_child(node
, "state"));
3021 char* zipcode
= sipe_xml_data(sipe_xml_child(node
, "zipcode"));
3022 char* country_code
= sipe_xml_data(sipe_xml_child(node
, "countryCode"));
3024 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_STREET
, street
);
3025 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_CITY
, city
);
3026 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_STATE
, state
);
3027 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_ZIPCODE
, zipcode
);
3028 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_COUNTRY
, country_code
);
3034 g_free(country_code
);
3042 else if (sipe_strequal(attrVar
, "note"))
3045 struct sipe_buddy
*sbuddy
= g_hash_table_lookup(sipe_private
->buddies
, uri
);
3047 if (!has_note_cleaned
) {
3048 has_note_cleaned
= TRUE
;
3050 g_free(sbuddy
->note
);
3051 sbuddy
->note
= NULL
;
3052 sbuddy
->is_oof_note
= FALSE
;
3053 sbuddy
->note_since
= publish_time
;
3055 do_update_status
= TRUE
;
3057 if (sbuddy
&& (publish_time
>= sbuddy
->note_since
)) {
3058 /* clean up in case no 'note' element is supplied
3059 * which indicate note removal in client
3061 g_free(sbuddy
->note
);
3062 sbuddy
->note
= NULL
;
3063 sbuddy
->is_oof_note
= FALSE
;
3064 sbuddy
->note_since
= publish_time
;
3066 xn_node
= sipe_xml_child(xn_category
, "note/body");
3069 sbuddy
->note
= g_markup_escape_text((tmp
= sipe_xml_data(xn_node
)), -1);
3071 sbuddy
->is_oof_note
= sipe_strequal(sipe_xml_attribute(xn_node
, "type"), "OOF");
3072 sbuddy
->note_since
= publish_time
;
3074 SIPE_DEBUG_INFO("process_incoming_notify_rlmi: uri(%s), note(%s)",
3075 uri
, sbuddy
->note
? sbuddy
->note
: "");
3077 /* to trigger UI refresh in case no status info is supplied in this update */
3078 do_update_status
= TRUE
;
3083 else if(sipe_strequal(attrVar
, "state"))
3087 const sipe_xml
*xn_availability
;
3088 const sipe_xml
*xn_activity
;
3089 const sipe_xml
*xn_meeting_subject
;
3090 const sipe_xml
*xn_meeting_location
;
3091 struct sipe_buddy
*sbuddy
= uri
? g_hash_table_lookup(sipe_private
->buddies
, uri
) : NULL
;
3093 xn_node
= sipe_xml_child(xn_category
, "state");
3094 if (!xn_node
) continue;
3095 xn_availability
= sipe_xml_child(xn_node
, "availability");
3096 if (!xn_availability
) continue;
3097 xn_activity
= sipe_xml_child(xn_node
, "activity");
3098 xn_meeting_subject
= sipe_xml_child(xn_node
, "meetingSubject");
3099 xn_meeting_location
= sipe_xml_child(xn_node
, "meetingLocation");
3101 tmp
= sipe_xml_data(xn_availability
);
3102 availability
= atoi(tmp
);
3105 /* activity, meeting_subject, meeting_location */
3110 g_free(sbuddy
->activity
);
3111 sbuddy
->activity
= NULL
;
3113 const char *token
= sipe_xml_attribute(xn_activity
, "token");
3114 const sipe_xml
*xn_custom
= sipe_xml_child(xn_activity
, "custom");
3117 if (!is_empty(token
)) {
3118 sbuddy
->activity
= g_strdup(sipe_get_activity_desc_by_token(token
));
3120 /* from custom element */
3122 char *custom
= sipe_xml_data(xn_custom
);
3124 if (!is_empty(custom
)) {
3125 sbuddy
->activity
= custom
;
3131 /* meeting_subject */
3132 g_free(sbuddy
->meeting_subject
);
3133 sbuddy
->meeting_subject
= NULL
;
3134 if (xn_meeting_subject
) {
3135 char *meeting_subject
= sipe_xml_data(xn_meeting_subject
);
3137 if (!is_empty(meeting_subject
)) {
3138 sbuddy
->meeting_subject
= meeting_subject
;
3139 meeting_subject
= NULL
;
3141 g_free(meeting_subject
);
3143 /* meeting_location */
3144 g_free(sbuddy
->meeting_location
);
3145 sbuddy
->meeting_location
= NULL
;
3146 if (xn_meeting_location
) {
3147 char *meeting_location
= sipe_xml_data(xn_meeting_location
);
3149 if (!is_empty(meeting_location
)) {
3150 sbuddy
->meeting_location
= meeting_location
;
3151 meeting_location
= NULL
;
3153 g_free(meeting_location
);
3156 status
= sipe_get_status_by_availability(availability
, &tmp
);
3157 if (sbuddy
->activity
&& tmp
) {
3158 char *tmp2
= sbuddy
->activity
;
3160 sbuddy
->activity
= g_strdup_printf("%s, %s", sbuddy
->activity
, tmp
);
3164 sbuddy
->activity
= tmp
;
3168 do_update_status
= TRUE
;
3171 else if(sipe_strequal(attrVar
, "calendarData"))
3173 struct sipe_buddy
*sbuddy
= uri
? g_hash_table_lookup(sipe_private
->buddies
, uri
) : NULL
;
3174 const sipe_xml
*xn_free_busy
= sipe_xml_child(xn_category
, "calendarData/freeBusy");
3175 const sipe_xml
*xn_working_hours
= sipe_xml_child(xn_category
, "calendarData/WorkingHours");
3177 if (sbuddy
&& xn_free_busy
) {
3178 if (!has_free_busy_cleaned
) {
3179 has_free_busy_cleaned
= TRUE
;
3181 g_free(sbuddy
->cal_start_time
);
3182 sbuddy
->cal_start_time
= NULL
;
3184 g_free(sbuddy
->cal_free_busy_base64
);
3185 sbuddy
->cal_free_busy_base64
= NULL
;
3187 g_free(sbuddy
->cal_free_busy
);
3188 sbuddy
->cal_free_busy
= NULL
;
3190 sbuddy
->cal_free_busy_published
= publish_time
;
3193 if (publish_time
>= sbuddy
->cal_free_busy_published
) {
3194 g_free(sbuddy
->cal_start_time
);
3195 sbuddy
->cal_start_time
= g_strdup(sipe_xml_attribute(xn_free_busy
, "startTime"));
3197 sbuddy
->cal_granularity
= sipe_strcase_equal(sipe_xml_attribute(xn_free_busy
, "granularity"), "PT15M") ?
3200 g_free(sbuddy
->cal_free_busy_base64
);
3201 sbuddy
->cal_free_busy_base64
= sipe_xml_data(xn_free_busy
);
3203 g_free(sbuddy
->cal_free_busy
);
3204 sbuddy
->cal_free_busy
= NULL
;
3206 sbuddy
->cal_free_busy_published
= publish_time
;
3208 SIPE_DEBUG_INFO("process_incoming_notify_rlmi: startTime=%s granularity=%d cal_free_busy_base64=\n%s", sbuddy
->cal_start_time
, sbuddy
->cal_granularity
, sbuddy
->cal_free_busy_base64
);
3212 if (sbuddy
&& xn_working_hours
) {
3213 sipe_cal_parse_working_hours(xn_working_hours
, sbuddy
);
3218 if (do_update_status
) {
3219 if (!status
) { /* no status category in this update, using contact's current status */
3220 PurpleBuddy
*pbuddy
= purple_find_buddy((PurpleAccount
*)sip
->account
, uri
);
3221 const PurplePresence
*presence
= purple_buddy_get_presence(pbuddy
);
3222 const PurpleStatus
*pstatus
= purple_presence_get_active_status(presence
);
3223 status
= purple_status_get_id(pstatus
);
3226 SIPE_DEBUG_INFO("process_incoming_notify_rlmi: %s", status
);
3227 sipe_core_buddy_got_status(SIPE_CORE_PUBLIC
, uri
, status
);
3230 sipe_xml_free(xn_categories
);
3233 static void sipe_subscribe_poolfqdn_resource_uri(const char *host
,
3235 struct sipe_core_private
*sipe_private
)
3237 struct presence_batched_routed
*payload
= g_malloc(sizeof(struct presence_batched_routed
));
3238 SIPE_DEBUG_INFO("process_incoming_notify_rlmi_resub: pool(%s)", host
);
3239 payload
->host
= g_strdup(host
);
3240 payload
->buddies
= server
;
3241 sipe_subscribe_presence_batched_routed(sipe_private
,
3243 sipe_subscribe_presence_batched_routed_free(payload
);
3246 static void process_incoming_notify_rlmi_resub(struct sipe_core_private
*sipe_private
,
3247 const gchar
*data
, unsigned len
)
3250 const sipe_xml
*xn_resource
;
3251 GHashTable
*servers
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
3256 xn_list
= sipe_xml_parse(data
, len
);
3258 for (xn_resource
= sipe_xml_child(xn_list
, "resource");
3260 xn_resource
= sipe_xml_twin(xn_resource
) )
3262 const char *uri
, *state
;
3263 const sipe_xml
*xn_instance
;
3265 xn_instance
= sipe_xml_child(xn_resource
, "instance");
3266 if (!xn_instance
) continue;
3268 uri
= sipe_xml_attribute(xn_resource
, "uri");
3269 state
= sipe_xml_attribute(xn_instance
, "state");
3270 SIPE_DEBUG_INFO("process_incoming_notify_rlmi_resub: uri(%s),state(%s)", uri
, state
);
3272 if (strstr(state
, "resubscribe")) {
3273 const char *poolFqdn
= sipe_xml_attribute(xn_instance
, "poolFqdn");
3275 if (poolFqdn
) { //[MS-PRES] Section 3.4.5.1.3 Processing Details
3276 gchar
*user
= g_strdup(uri
);
3277 host
= g_strdup(poolFqdn
);
3278 server
= g_hash_table_lookup(servers
, host
);
3279 server
= g_slist_append(server
, user
);
3280 g_hash_table_insert(servers
, host
, server
);
3282 sipe_subscribe_presence_single(sipe_private
,
3288 /* Send out any deferred poolFqdn subscriptions */
3289 g_hash_table_foreach(servers
, (GHFunc
) sipe_subscribe_poolfqdn_resource_uri
, sipe_private
);
3290 g_hash_table_destroy(servers
);
3292 sipe_xml_free(xn_list
);
3295 static void process_incoming_notify_pidf(struct sipe_core_private
*sipe_private
,
3296 const gchar
*data
, unsigned len
)
3300 gchar
*activity
= NULL
;
3302 const sipe_xml
*basicstatus
= NULL
, *tuple
, *status
;
3303 gboolean isonline
= FALSE
;
3304 const sipe_xml
*display_name_node
;
3306 pidf
= sipe_xml_parse(data
, len
);
3308 SIPE_DEBUG_INFO("process_incoming_notify_pidf: no parseable pidf:%s", data
);
3312 if ((tuple
= sipe_xml_child(pidf
, "tuple")))
3314 if ((status
= sipe_xml_child(tuple
, "status"))) {
3315 basicstatus
= sipe_xml_child(status
, "basic");
3320 SIPE_DEBUG_INFO_NOFORMAT("process_incoming_notify_pidf: no basic found");
3321 sipe_xml_free(pidf
);
3325 getbasic
= sipe_xml_data(basicstatus
);
3327 SIPE_DEBUG_INFO_NOFORMAT("process_incoming_notify_pidf: no basic data found");
3328 sipe_xml_free(pidf
);
3332 SIPE_DEBUG_INFO("process_incoming_notify_pidf: basic-status(%s)", getbasic
);
3333 if (strstr(getbasic
, "open")) {
3338 uri
= sip_uri(sipe_xml_attribute(pidf
, "entity")); /* with 'sip:' prefix */ /* AOL comes without the prefix */
3340 display_name_node
= sipe_xml_child(pidf
, "display-name");
3341 if (display_name_node
) {
3342 char * display_name
= sipe_xml_data(display_name_node
);
3344 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_DISPLAY_NAME
, display_name
);
3345 g_free(display_name
);
3348 if ((tuple
= sipe_xml_child(pidf
, "tuple"))) {
3349 if ((status
= sipe_xml_child(tuple
, "status"))) {
3350 if ((basicstatus
= sipe_xml_child(status
, "activities"))) {
3351 if ((basicstatus
= sipe_xml_child(basicstatus
, "activity"))) {
3352 activity
= sipe_xml_data(basicstatus
);
3353 SIPE_DEBUG_INFO("process_incoming_notify_pidf: activity(%s)", activity
);
3360 const gchar
* status_id
= NULL
;
3362 if (sipe_strequal(activity
, sipe_activity_map
[SIPE_ACTIVITY_BUSY
].token
)) {
3363 status_id
= SIPE_STATUS_ID_BUSY
;
3364 } else if (sipe_strequal(activity
, sipe_activity_map
[SIPE_ACTIVITY_AWAY
].token
)) {
3365 status_id
= SIPE_STATUS_ID_AWAY
;
3370 status_id
= SIPE_STATUS_ID_AVAILABLE
;
3373 SIPE_DEBUG_INFO("process_incoming_notify_pidf: status_id(%s)", status_id
);
3374 sipe_core_buddy_got_status(SIPE_CORE_PUBLIC
, uri
, status_id
);
3376 sipe_core_buddy_got_status(SIPE_CORE_PUBLIC
, uri
, SIPE_STATUS_ID_OFFLINE
);
3381 sipe_xml_free(pidf
);
3386 sipe_user_info_has_updated(struct sipe_core_private
*sipe_private
,
3387 const sipe_xml
*xn_userinfo
)
3389 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
3390 const sipe_xml
*xn_states
;
3392 g_free(sip
->user_states
);
3393 sip
->user_states
= NULL
;
3394 if ((xn_states
= sipe_xml_child(xn_userinfo
, "states")) != NULL
) {
3395 gchar
*orig
= sip
->user_states
= sipe_xml_stringify(xn_states
);
3397 /* this is a hack-around to remove added newline after inner element,
3398 * state in this case, where it shouldn't be.
3399 * After several use of sipe_xml_stringify, amount of added newlines
3400 * grows significantly.
3403 gchar c
, *stripped
= orig
;
3404 while ((c
= *orig
++)) {
3405 if ((c
!= '\n') /* && (c != '\r') */) {
3413 /* Publish initial state if not yet.
3414 * Assuming this happens on initial responce to self subscription
3415 * so we've already updated our UserInfo.
3417 if (!sip
->initial_state_published
) {
3418 send_presence_soap(sipe_private
, FALSE
);
3420 sipe_schedule_seconds(sipe_private
,
3421 "<+update-calendar>",
3423 UPDATE_CALENDAR_DELAY
,
3424 (sipe_schedule_action
) sipe_core_update_calendar
,
3429 static void process_incoming_notify_msrtc(struct sipe_core_private
*sipe_private
,
3430 const gchar
*data
, unsigned len
)
3432 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
3433 char *activity
= NULL
;
3435 const char *status_id
= NULL
;
3438 char *self_uri
= sip_uri_self(sipe_private
);
3441 const char *device_name
= NULL
;
3442 const char *cal_start_time
= NULL
;
3443 const char *cal_granularity
= NULL
;
3444 char *cal_free_busy_base64
= NULL
;
3445 struct sipe_buddy
*sbuddy
;
3446 const sipe_xml
*node
;
3447 sipe_xml
*xn_presentity
;
3448 const sipe_xml
*xn_availability
;
3449 const sipe_xml
*xn_activity
;
3450 const sipe_xml
*xn_display_name
;
3451 const sipe_xml
*xn_email
;
3452 const sipe_xml
*xn_phone_number
;
3453 const sipe_xml
*xn_userinfo
;
3454 const sipe_xml
*xn_note
;
3455 const sipe_xml
*xn_oof
;
3456 const sipe_xml
*xn_state
;
3457 const sipe_xml
*xn_contact
;
3460 const char *user_avail_nil
;
3462 time_t user_avail_since
= 0;
3463 time_t activity_since
= 0;
3465 /* fix for Reuters environment on Linux */
3466 if (data
&& strstr(data
, "encoding=\"utf-16\"")) {
3468 tmp_data
= replace(data
, "encoding=\"utf-16\"", "encoding=\"utf-8\"");
3469 xn_presentity
= sipe_xml_parse(tmp_data
, strlen(tmp_data
));
3472 xn_presentity
= sipe_xml_parse(data
, len
);
3475 xn_availability
= sipe_xml_child(xn_presentity
, "availability");
3476 xn_activity
= sipe_xml_child(xn_presentity
, "activity");
3477 xn_display_name
= sipe_xml_child(xn_presentity
, "displayName");
3478 xn_email
= sipe_xml_child(xn_presentity
, "email");
3479 xn_phone_number
= sipe_xml_child(xn_presentity
, "phoneNumber");
3480 xn_userinfo
= sipe_xml_child(xn_presentity
, "userInfo");
3481 xn_oof
= xn_userinfo
? sipe_xml_child(xn_userinfo
, "oof") : NULL
;
3482 xn_state
= xn_userinfo
? sipe_xml_child(xn_userinfo
, "states/state"): NULL
;
3483 user_avail
= xn_state
? sipe_xml_int_attribute(xn_state
, "avail", 0) : 0;
3484 user_avail_since
= xn_state
? sipe_utils_str_to_time(sipe_xml_attribute(xn_state
, "since")) : 0;
3485 user_avail_nil
= xn_state
? sipe_xml_attribute(xn_state
, "nil") : NULL
;
3486 xn_contact
= xn_userinfo
? sipe_xml_child(xn_userinfo
, "contact") : NULL
;
3487 xn_note
= xn_userinfo
? sipe_xml_child(xn_userinfo
, "note") : NULL
;
3488 note
= xn_note
? sipe_xml_data(xn_note
) : NULL
;
3490 if (sipe_strequal(user_avail_nil
, "true")) { /* null-ed */
3492 user_avail_since
= 0;
3495 name
= sipe_xml_attribute(xn_presentity
, "uri"); /* without 'sip:' prefix */
3496 uri
= sip_uri_from_name(name
);
3497 avl
= sipe_xml_int_attribute(xn_availability
, "aggregate", 0);
3498 epid
= sipe_xml_attribute(xn_availability
, "epid");
3499 act
= sipe_xml_int_attribute(xn_activity
, "aggregate", 0);
3501 status_id
= sipe_get_status_by_act_avail_2005(act
, avl
, &activity
);
3502 res_avail
= sipe_get_availability_by_status(status_id
, NULL
);
3503 if (user_avail
> res_avail
) {
3504 res_avail
= user_avail
;
3505 status_id
= sipe_get_status_by_availability(user_avail
, NULL
);
3508 if (xn_display_name
) {
3509 char *display_name
= g_strdup(sipe_xml_attribute(xn_display_name
, "displayName"));
3510 char *email
= xn_email
? g_strdup(sipe_xml_attribute(xn_email
, "email")) : NULL
;
3511 char *phone_label
= xn_phone_number
? g_strdup(sipe_xml_attribute(xn_phone_number
, "label")) : NULL
;
3512 char *phone_number
= xn_phone_number
? g_strdup(sipe_xml_attribute(xn_phone_number
, "number")) : NULL
;
3513 char *tel_uri
= sip_to_tel_uri(phone_number
);
3515 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_DISPLAY_NAME
, display_name
);
3516 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_EMAIL
, email
);
3517 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_WORK_PHONE
, tel_uri
);
3518 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY
, !is_empty(phone_label
) ? phone_label
: phone_number
);
3521 g_free(phone_label
);
3522 g_free(phone_number
);
3524 g_free(display_name
);
3529 for (node
= sipe_xml_child(xn_contact
, "tel"); node
; node
= sipe_xml_twin(node
))
3531 /* Ex.: <tel type="work">tel:+3222220000</tel> */
3532 const char *phone_type
= sipe_xml_attribute(node
, "type");
3533 char* phone
= sipe_xml_data(node
);
3535 sipe_update_user_phone(sipe_private
, uri
, phone_type
, phone
, NULL
);
3541 /* devicePresence */
3542 for (node
= sipe_xml_child(xn_presentity
, "devices/devicePresence"); node
; node
= sipe_xml_twin(node
)) {
3543 const sipe_xml
*xn_device_name
;
3544 const sipe_xml
*xn_calendar_info
;
3545 const sipe_xml
*xn_state
;
3549 if (sipe_strequal(sipe_xml_attribute(node
, "epid"), epid
)) {
3550 xn_device_name
= sipe_xml_child(node
, "deviceName");
3551 device_name
= xn_device_name
? sipe_xml_attribute(xn_device_name
, "name") : NULL
;
3555 xn_calendar_info
= sipe_xml_child(node
, "calendarInfo");
3556 if (xn_calendar_info
) {
3557 const char *cal_start_time_tmp
= sipe_xml_attribute(xn_calendar_info
, "startTime");
3559 if (cal_start_time
) {
3560 time_t cal_start_time_t
= sipe_utils_str_to_time(cal_start_time
);
3561 time_t cal_start_time_t_tmp
= sipe_utils_str_to_time(cal_start_time_tmp
);
3563 if (cal_start_time_t_tmp
> cal_start_time_t
) {
3564 cal_start_time
= cal_start_time_tmp
;
3565 cal_granularity
= sipe_xml_attribute(xn_calendar_info
, "granularity");
3566 g_free(cal_free_busy_base64
);
3567 cal_free_busy_base64
= sipe_xml_data(xn_calendar_info
);
3569 SIPE_DEBUG_INFO("process_incoming_notify_msrtc: startTime=%s granularity=%s cal_free_busy_base64=\n%s", cal_start_time
, cal_granularity
, cal_free_busy_base64
);
3572 cal_start_time
= cal_start_time_tmp
;
3573 cal_granularity
= sipe_xml_attribute(xn_calendar_info
, "granularity");
3574 g_free(cal_free_busy_base64
);
3575 cal_free_busy_base64
= sipe_xml_data(xn_calendar_info
);
3577 SIPE_DEBUG_INFO("process_incoming_notify_msrtc: startTime=%s granularity=%s cal_free_busy_base64=\n%s", cal_start_time
, cal_granularity
, cal_free_busy_base64
);
3582 xn_state
= sipe_xml_child(node
, "states/state");
3584 int dev_avail
= sipe_xml_int_attribute(xn_state
, "avail", 0);
3585 time_t dev_avail_since
= sipe_utils_str_to_time(sipe_xml_attribute(xn_state
, "since"));
3587 state
= sipe_xml_data(xn_state
);
3588 if (dev_avail_since
> user_avail_since
&&
3589 dev_avail
>= res_avail
)
3591 res_avail
= dev_avail
;
3592 if (!is_empty(state
))
3594 if (sipe_strequal(state
, sipe_activity_map
[SIPE_ACTIVITY_ON_PHONE
].token
)) {
3596 activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_ON_PHONE
));
3597 } else if (sipe_strequal(state
, "presenting")) {
3599 activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_IN_CONF
));
3604 activity_since
= dev_avail_since
;
3606 status_id
= sipe_get_status_by_availability(res_avail
, &activity
);
3613 if (xn_oof
&& res_avail
>= 15000) { /* 12000 in 2007 */
3615 activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_OOF
));
3619 sbuddy
= g_hash_table_lookup(sipe_private
->buddies
, uri
);
3622 g_free(sbuddy
->activity
);
3623 sbuddy
->activity
= activity
;
3626 sbuddy
->activity_since
= activity_since
;
3628 sbuddy
->user_avail
= user_avail
;
3629 sbuddy
->user_avail_since
= user_avail_since
;
3631 g_free(sbuddy
->note
);
3632 sbuddy
->note
= NULL
;
3633 if (!is_empty(note
)) { sbuddy
->note
= g_markup_escape_text(note
, -1); }
3635 sbuddy
->is_oof_note
= (xn_oof
!= NULL
);
3637 g_free(sbuddy
->device_name
);
3638 sbuddy
->device_name
= NULL
;
3639 if (!is_empty(device_name
)) { sbuddy
->device_name
= g_strdup(device_name
); }
3641 if (!is_empty(cal_free_busy_base64
)) {
3642 g_free(sbuddy
->cal_start_time
);
3643 sbuddy
->cal_start_time
= g_strdup(cal_start_time
);
3645 sbuddy
->cal_granularity
= sipe_strcase_equal(cal_granularity
, "PT15M") ? 15 : 0;
3647 g_free(sbuddy
->cal_free_busy_base64
);
3648 sbuddy
->cal_free_busy_base64
= cal_free_busy_base64
;
3649 cal_free_busy_base64
= NULL
;
3651 g_free(sbuddy
->cal_free_busy
);
3652 sbuddy
->cal_free_busy
= NULL
;
3655 sbuddy
->last_non_cal_status_id
= status_id
;
3656 g_free(sbuddy
->last_non_cal_activity
);
3657 sbuddy
->last_non_cal_activity
= g_strdup(sbuddy
->activity
);
3659 if (sipe_strcase_equal(sbuddy
->name
, self_uri
)) {
3660 if (!sipe_strequal(sbuddy
->note
, sip
->note
)) /* not same */
3662 sip
->is_oof_note
= sbuddy
->is_oof_note
;
3665 sip
->note
= g_strdup(sbuddy
->note
);
3667 sip
->note_since
= time(NULL
);
3670 g_free(sip
->status
);
3671 sip
->status
= g_strdup(sbuddy
->last_non_cal_status_id
);
3674 g_free(cal_free_busy_base64
);
3677 SIPE_DEBUG_INFO("process_incoming_notify_msrtc: status(%s)", status_id
);
3678 sipe_core_buddy_got_status(SIPE_CORE_PUBLIC
, uri
, status_id
);
3680 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
) && sipe_strcase_equal(self_uri
, uri
)) {
3681 sipe_user_info_has_updated(sipe_private
, xn_userinfo
);
3685 sipe_xml_free(xn_presentity
);
3690 static void sipe_presence_mime_cb(gpointer user_data
, /* sipe_core_private */
3691 const GSList
*fields
,
3695 const gchar
*type
= sipe_utils_nameval_find(fields
, "Content-Type");
3697 if (strstr(type
,"application/rlmi+xml")) {
3698 process_incoming_notify_rlmi_resub(user_data
, body
, length
);
3699 } else if (strstr(type
, "text/xml+msrtc.pidf")) {
3700 process_incoming_notify_msrtc(user_data
, body
, length
);
3702 process_incoming_notify_rlmi(user_data
, body
, length
);
3706 static void sipe_process_presence(struct sipe_core_private
*sipe_private
,
3709 const char *ctype
= sipmsg_find_header(msg
, "Content-Type");
3711 SIPE_DEBUG_INFO("sipe_process_presence: Content-Type: %s", ctype
? ctype
: "");
3714 (strstr(ctype
, "application/rlmi+xml") ||
3715 strstr(ctype
, "application/msrtc-event-categories+xml")))
3717 if (strstr(ctype
, "multipart"))
3719 sipe_mime_parts_foreach(ctype
, msg
->body
, sipe_presence_mime_cb
, sipe_private
);
3721 else if(strstr(ctype
, "application/msrtc-event-categories+xml") )
3723 process_incoming_notify_rlmi(sipe_private
, msg
->body
, msg
->bodylen
);
3725 else if(strstr(ctype
, "application/rlmi+xml"))
3727 process_incoming_notify_rlmi_resub(sipe_private
, msg
->body
, msg
->bodylen
);
3730 else if(ctype
&& strstr(ctype
, "text/xml+msrtc.pidf"))
3732 process_incoming_notify_msrtc(sipe_private
, msg
->body
, msg
->bodylen
);
3736 process_incoming_notify_pidf(sipe_private
, msg
->body
, msg
->bodylen
);
3740 static void sipe_presence_timeout_mime_cb(gpointer user_data
,
3741 SIPE_UNUSED_PARAMETER
const GSList
*fields
,
3745 GSList
**buddies
= user_data
;
3746 sipe_xml
*xml
= sipe_xml_parse(body
, length
);
3748 if (xml
&& !sipe_strequal(sipe_xml_name(xml
), "list")) {
3749 const gchar
*uri
= sipe_xml_attribute(xml
, "uri");
3750 const sipe_xml
*xn_category
;
3753 * automaton: presence is never expected to change
3755 * see: http://msdn.microsoft.com/en-us/library/ee354295(office.13).aspx
3757 for (xn_category
= sipe_xml_child(xml
, "category");
3759 xn_category
= sipe_xml_twin(xn_category
)) {
3760 if (sipe_strequal(sipe_xml_attribute(xn_category
, "name"),
3762 const sipe_xml
*node
= sipe_xml_child(xn_category
, "contactCard/automaton");
3764 char *boolean
= sipe_xml_data(node
);
3765 if (sipe_strequal(boolean
, "true")) {
3766 SIPE_DEBUG_INFO("sipe_process_presence_timeout: %s is an automaton: - not subscribing to presence updates",
3777 *buddies
= g_slist_append(*buddies
, sip_uri(uri
));
3784 static void sipe_process_presence_timeout(struct sipe_core_private
*sipe_private
,
3785 struct sipmsg
*msg
, gchar
*who
,
3788 const char *ctype
= sipmsg_find_header(msg
, "Content-Type");
3789 gchar
*action_name
= sipe_utils_presence_key(who
);
3791 SIPE_DEBUG_INFO("sipe_process_presence_timeout: Content-Type: %s", ctype
? ctype
: "");
3794 strstr(ctype
, "multipart") &&
3795 (strstr(ctype
, "application/rlmi+xml") ||
3796 strstr(ctype
, "application/msrtc-event-categories+xml"))) {
3797 GSList
*buddies
= NULL
;
3799 sipe_mime_parts_foreach(ctype
, msg
->body
, sipe_presence_timeout_mime_cb
, &buddies
);
3802 struct presence_batched_routed
*payload
= g_malloc(sizeof(struct presence_batched_routed
));
3803 payload
->host
= g_strdup(who
);
3804 payload
->buddies
= buddies
;
3805 sipe_schedule_seconds(sipe_private
,
3809 sipe_subscribe_presence_batched_routed
,
3810 sipe_subscribe_presence_batched_routed_free
);
3811 SIPE_DEBUG_INFO("Resubscription multiple contacts with batched support & route(%s) in %d", who
, timeout
);
3815 sipe_schedule_seconds(sipe_private
,
3819 sipe_subscribe_presence_single
,
3821 SIPE_DEBUG_INFO("Resubscription single contact with batched support(%s) in %d", who
, timeout
);
3823 g_free(action_name
);
3827 * Dispatcher for all incoming subscription information
3828 * whether it comes from NOTIFY, BENOTIFY requests or
3829 * piggy-backed to subscription's OK responce.
3831 * @param request whether initiated from BE/NOTIFY request or OK-response message.
3832 * @param benotify whether initiated from NOTIFY or BENOTIFY request.
3834 void process_incoming_notify(struct sipe_core_private
*sipe_private
,
3836 gboolean request
, gboolean benotify
)
3838 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
3839 const gchar
*content_type
= sipmsg_find_header(msg
, "Content-Type");
3840 const gchar
*event
= sipmsg_find_header(msg
, "Event");
3841 const gchar
*subscription_state
= sipmsg_find_header(msg
, "subscription-state");
3843 SIPE_DEBUG_INFO("process_incoming_notify: subscription_state: %s", subscription_state
? subscription_state
: "");
3845 /* implicit subscriptions */
3846 if (content_type
&& g_str_has_prefix(content_type
, "application/ms-imdn+xml")) {
3847 sipe_process_imdn(sipe_private
, msg
);
3851 /* for one off subscriptions (send with Expire: 0) */
3852 if (sipe_strcase_equal(event
, "vnd-microsoft-provisioning-v2"))
3854 sipe_process_provisioning_v2(sipe_private
, msg
);
3856 else if (sipe_strcase_equal(event
, "vnd-microsoft-provisioning"))
3858 sipe_process_provisioning(sipe_private
, msg
);
3860 else if (sipe_strcase_equal(event
, "presence"))
3862 sipe_process_presence(sipe_private
, msg
);
3864 else if (sipe_strcase_equal(event
, "registration-notify"))
3866 sipe_process_registration_notify(sipe_private
, msg
);
3869 if (!subscription_state
|| strstr(subscription_state
, "active"))
3871 if (sipe_strcase_equal(event
, "vnd-microsoft-roaming-contacts"))
3873 sipe_process_roaming_contacts(sipe_private
, msg
);
3875 else if (sipe_strcase_equal(event
, "vnd-microsoft-roaming-self"))
3877 sipe_process_roaming_self(sipe_private
, msg
);
3879 else if (sipe_strcase_equal(event
, "vnd-microsoft-roaming-ACL"))
3881 sipe_process_roaming_acl(sipe_private
, msg
);
3883 else if (sipe_strcase_equal(event
, "presence.wpending"))
3885 sipe_process_presence_wpending(sipe_private
, msg
);
3887 else if (sipe_strcase_equal(event
, "conference"))
3889 sipe_process_conference(sipe_private
, msg
);
3894 /* The server sends status 'terminated' */
3895 if (subscription_state
&& strstr(subscription_state
, "terminated") ) {
3896 gchar
*who
= parse_from(sipmsg_find_header(msg
, request
? "From" : "To"));
3897 gchar
*key
= sipe_utils_subscription_key(event
, who
);
3899 SIPE_DEBUG_INFO("process_incoming_notify: server says that subscription to %s was terminated.", who
);
3902 sipe_subscriptions_remove(sipe_private
, key
);
3906 if (!request
&& event
) {
3907 const gchar
*expires_header
= sipmsg_find_header(msg
, "Expires");
3908 int timeout
= expires_header
? strtol(expires_header
, NULL
, 10) : 0;
3909 SIPE_DEBUG_INFO("process_incoming_notify: subscription expires:%d", timeout
);
3912 /* 2 min ahead of expiration */
3913 timeout
= (timeout
- 120) > 120 ? (timeout
- 120) : timeout
;
3915 if (sipe_strcase_equal(event
, "presence.wpending") &&
3916 g_slist_find_custom(sip
->allow_events
, "presence.wpending", (GCompareFunc
)g_ascii_strcasecmp
))
3918 gchar
*action_name
= g_strdup_printf("<%s>", "presence.wpending");
3919 sipe_schedule_seconds(sipe_private
,
3923 sipe_subscribe_presence_wpending
,
3925 g_free(action_name
);
3927 else if (sipe_strcase_equal(event
, "presence") &&
3928 g_slist_find_custom(sip
->allow_events
, "presence", (GCompareFunc
)g_ascii_strcasecmp
))
3930 gchar
*who
= parse_from(sipmsg_find_header(msg
, "To"));
3931 gchar
*action_name
= sipe_utils_presence_key(who
);
3933 if (sip
->batched_support
) {
3934 sipe_process_presence_timeout(sipe_private
, msg
, who
, timeout
);
3937 sipe_schedule_seconds(sipe_private
,
3941 sipe_subscribe_presence_single
,
3943 SIPE_DEBUG_INFO("Resubscription single contact (%s) in %d", who
, timeout
);
3945 g_free(action_name
);
3951 /* The client responses on received a NOTIFY message */
3952 if (request
&& !benotify
)
3954 sip_transport_response(sipe_private
, msg
, 200, "OK", NULL
);
3959 * Whether user manually changed status or
3960 * it was changed automatically due to user
3961 * became inactive/active again
3964 sipe_is_user_state(struct sipe_core_private
*sipe_private
)
3966 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
3968 time_t now
= time(NULL
);
3970 SIPE_DEBUG_INFO("sipe_is_user_state: sip->idle_switch : %s", asctime(localtime(&(sip
->idle_switch
))));
3971 SIPE_DEBUG_INFO("sipe_is_user_state: now : %s", asctime(localtime(&now
)));
3973 res
= ((now
- SIPE_IDLE_SET_DELAY
* 2) >= sip
->idle_switch
);
3975 SIPE_DEBUG_INFO("sipe_is_user_state: res = %s", res
? "USER" : "MACHINE");
3980 send_presence_soap0(struct sipe_core_private
*sipe_private
,
3981 gboolean do_publish_calendar
,
3982 gboolean do_reset_status
)
3984 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
3985 struct sipe_calendar
* cal
= sip
->cal
;
3986 int availability
= 0;
3991 gchar
*res_note
= NULL
;
3992 gchar
*res_oof
= NULL
;
3993 const gchar
*note_pub
= NULL
;
3994 gchar
*states
= NULL
;
3995 gchar
*calendar_data
= NULL
;
3996 gchar
*epid
= get_epid(sipe_private
);
3997 time_t now
= time(NULL
);
3998 gchar
*since_time_str
= sipe_utils_time_to_str(now
);
3999 const gchar
*oof_note
= cal
? sipe_ews_get_oof_note(cal
) : NULL
;
4000 const char *user_input
;
4001 gboolean pub_oof
= cal
&& oof_note
&& (!sip
->note
|| cal
->updated
> sip
->note_since
);
4003 if (oof_note
&& sip
->note
) {
4004 SIPE_DEBUG_INFO("cal->oof_start : %s", asctime(localtime(&(cal
->oof_start
))));
4005 SIPE_DEBUG_INFO("sip->note_since : %s", asctime(localtime(&(sip
->note_since
))));
4008 SIPE_DEBUG_INFO("sip->note : %s", sip
->note
? sip
->note
: "");
4010 if (!sip
->initial_state_published
||
4013 g_free(sip
->status
);
4014 sip
->status
= g_strdup(SIPE_STATUS_ID_AVAILABLE
);
4017 sipe_get_act_avail_by_status_2005(sip
->status
, &activity
, &availability
);
4021 note_pub
= oof_note
;
4022 res_oof
= SIPE_SOAP_SET_PRESENCE_OOF_XML
;
4023 cal
->published
= TRUE
;
4024 } else if (sip
->note
) {
4025 if (sip
->is_oof_note
&& !oof_note
) { /* stale OOF note, as it's not present in cal already */
4028 sip
->is_oof_note
= FALSE
;
4029 sip
->note_since
= 0;
4031 note_pub
= sip
->note
;
4032 res_oof
= sip
->is_oof_note
? SIPE_SOAP_SET_PRESENCE_OOF_XML
: "";
4038 /* to protocol internal plain text format */
4039 tmp
= sipe_backend_markup_strip_html(note_pub
);
4040 res_note
= g_markup_printf_escaped(SIPE_SOAP_SET_PRESENCE_NOTE_XML
, tmp
);
4045 if (!do_reset_status
) {
4046 if (sipe_is_user_state(sipe_private
) && !do_publish_calendar
&& sip
->initial_state_published
)
4048 gchar
*activity_token
= NULL
;
4049 int avail_2007
= sipe_get_availability_by_status(sip
->status
, &activity_token
);
4051 states
= g_strdup_printf(SIPE_SOAP_SET_PRESENCE_STATES
,
4056 g_free(activity_token
);
4058 else /* preserve existing publication */
4060 if (sip
->user_states
) {
4061 states
= g_strdup(sip
->user_states
);
4065 /* do nothing - then User state will be erased */
4067 sip
->initial_state_published
= TRUE
;
4070 if (cal
&& (!is_empty(cal
->legacy_dn
) || !is_empty(cal
->email
)) && cal
->fb_start
&& !is_empty(cal
->free_busy
))
4072 char *fb_start_str
= sipe_utils_time_to_str(cal
->fb_start
);
4073 char *free_busy_base64
= sipe_cal_get_freebusy_base64(cal
->free_busy
);
4074 calendar_data
= g_strdup_printf(SIPE_SOAP_SET_PRESENCE_CALENDAR
,
4075 !is_empty(cal
->legacy_dn
) ? cal
->legacy_dn
: cal
->email
,
4078 g_free(fb_start_str
);
4079 g_free(free_busy_base64
);
4082 user_input
= (sipe_is_user_state(sipe_private
) ||
4083 sipe_strequal(sip
->status
, SIPE_STATUS_ID_AVAILABLE
)) ?
4086 /* forming resulting XML */
4087 body
= g_strdup_printf(SIPE_SOAP_SET_PRESENCE
,
4088 sipe_private
->username
,
4091 (tmp
= g_ascii_strup(g_get_host_name(), -1)),
4092 res_note
? res_note
: "",
4093 res_oof
? res_oof
: "",
4094 states
? states
: "",
4095 calendar_data
? calendar_data
: "",
4104 g_free(calendar_data
);
4106 send_soap_request(sipe_private
, body
);
4109 g_free(since_time_str
);
4114 send_presence_soap(struct sipe_core_private
*sipe_private
,
4115 gboolean do_publish_calendar
)
4117 return send_presence_soap0(sipe_private
, do_publish_calendar
, FALSE
);
4122 process_send_presence_category_publish_response(struct sipe_core_private
*sipe_private
,
4124 struct transaction
*trans
)
4126 const gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
4128 if (msg
->response
== 409 && g_str_has_prefix(contenttype
, "application/msrtc-fault+xml")) {
4130 const sipe_xml
*node
;
4134 gboolean has_device_publication
= FALSE
;
4136 xml
= sipe_xml_parse(msg
->body
, msg
->bodylen
);
4138 /* test if version mismatch fault */
4139 fault_code
= sipe_xml_data(sipe_xml_child(xml
, "Faultcode"));
4140 if (!sipe_strequal(fault_code
, "Client.BadCall.WrongDelta")) {
4141 SIPE_DEBUG_INFO("process_send_presence_category_publish_response: unsupported fault code:%s returning.", fault_code
);
4148 /* accumulating information about faulty versions */
4149 faults
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, g_free
);
4150 for (node
= sipe_xml_child(xml
, "details/operation");
4152 node
= sipe_xml_twin(node
))
4154 const gchar
*index
= sipe_xml_attribute(node
, "index");
4155 const gchar
*curVersion
= sipe_xml_attribute(node
, "curVersion");
4157 g_hash_table_insert(faults
, g_strdup(index
), g_strdup(curVersion
));
4158 SIPE_DEBUG_INFO("fault added: index:%s curVersion:%s", index
, curVersion
);
4162 /* here we are parsing our own request to figure out what publication
4163 * referenced here only by index went wrong
4165 xml
= sipe_xml_parse(trans
->msg
->body
, trans
->msg
->bodylen
);
4168 for (node
= sipe_xml_child(xml
, "publications/publication"),
4169 index_our
= 1; /* starts with 1 - our first publication */
4171 node
= sipe_xml_twin(node
), index_our
++)
4173 gchar
*idx
= g_strdup_printf("%d", index_our
);
4174 const gchar
*curVersion
= g_hash_table_lookup(faults
, idx
);
4175 const gchar
*categoryName
= sipe_xml_attribute(node
, "categoryName");
4178 if (sipe_strequal("device", categoryName
)) {
4179 has_device_publication
= TRUE
;
4182 if (curVersion
) { /* fault exist on this index */
4183 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
4184 const gchar
*container
= sipe_xml_attribute(node
, "container");
4185 const gchar
*instance
= sipe_xml_attribute(node
, "instance");
4186 /* key is <category><instance><container> */
4187 gchar
*key
= g_strdup_printf("<%s><%s><%s>", categoryName
, instance
, container
);
4188 GHashTable
*category
= g_hash_table_lookup(sip
->our_publications
, categoryName
);
4191 struct sipe_publication
*publication
=
4192 g_hash_table_lookup(category
, key
);
4194 SIPE_DEBUG_INFO("key is %s", key
);
4197 SIPE_DEBUG_INFO("Updating %s with version %s. Was %d before.",
4198 key
, curVersion
, publication
->version
);
4199 /* updating publication's version to the correct one */
4200 publication
->version
= atoi(curVersion
);
4203 /* We somehow lost this category from our publications... */
4204 struct sipe_publication
*publication
= g_new0(struct sipe_publication
, 1);
4205 publication
->category
= g_strdup(categoryName
);
4206 publication
->instance
= atoi(instance
);
4207 publication
->container
= atoi(container
);
4208 publication
->version
= atoi(curVersion
);
4209 category
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
4210 g_free
, (GDestroyNotify
)free_publication
);
4211 g_hash_table_insert(category
, g_strdup(key
), publication
);
4212 g_hash_table_insert(sip
->our_publications
, g_strdup(categoryName
), category
);
4213 SIPE_DEBUG_INFO("added lost category '%s' key '%s'", categoryName
, key
);
4219 g_hash_table_destroy(faults
);
4221 /* rebublishing with right versions */
4222 if (has_device_publication
) {
4223 send_publish_category_initial(sipe_private
);
4225 send_presence_status(sipe_private
, NULL
);
4232 * Returns 'device' XML part for publication.
4233 * Must be g_free'd after use.
4236 sipe_publish_get_category_device(struct sipe_core_private
*sipe_private
)
4238 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
4241 gchar
*epid
= get_epid(sipe_private
);
4242 gchar
*uuid
= generateUUIDfromEPID(epid
);
4243 guint device_instance
= sipe_get_pub_instance(sipe_private
, SIPE_PUB_DEVICE
);
4244 /* key is <category><instance><container> */
4245 gchar
*key
= g_strdup_printf("<%s><%u><%u>", "device", device_instance
, 2);
4246 struct sipe_publication
*publication
=
4247 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "device"), key
);
4252 uri
= sip_uri_self(sipe_private
);
4253 doc
= g_strdup_printf(SIPE_PUB_XML_DEVICE
,
4255 publication
? publication
->version
: 0,
4258 "00:00:00+01:00", /* @TODO make timezone real*/
4269 * A service method - use
4270 * - send_publish_get_category_state_machine and
4271 * - send_publish_get_category_state_user instead.
4272 * Must be g_free'd after use.
4275 sipe_publish_get_category_state(struct sipe_core_private
*sipe_private
,
4276 gboolean is_user_state
)
4278 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
4279 int availability
= sipe_get_availability_by_status(sip
->status
, NULL
);
4280 guint instance
= is_user_state
? sipe_get_pub_instance(sipe_private
, SIPE_PUB_STATE_USER
) :
4281 sipe_get_pub_instance(sipe_private
, SIPE_PUB_STATE_MACHINE
);
4282 /* key is <category><instance><container> */
4283 gchar
*key_2
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 2);
4284 gchar
*key_3
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 3);
4285 struct sipe_publication
*publication_2
=
4286 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "state"), key_2
);
4287 struct sipe_publication
*publication_3
=
4288 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "state"), key_3
);
4293 if (publication_2
&& (publication_2
->availability
== availability
))
4295 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_state: state has NOT changed. Exiting.");
4296 return NULL
; /* nothing to update */
4299 return g_strdup_printf( is_user_state
? SIPE_PUB_XML_STATE_USER
: SIPE_PUB_XML_STATE_MACHINE
,
4301 publication_2
? publication_2
->version
: 0,
4304 publication_3
? publication_3
->version
: 0,
4309 * Only Busy and OOF calendar event are published.
4310 * Different instances are used for that.
4312 * Must be g_free'd after use.
4315 sipe_publish_get_category_state_calendar(struct sipe_core_private
*sipe_private
,
4316 struct sipe_cal_event
*event
,
4320 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
4321 gchar
*start_time_str
;
4322 int availability
= 0;
4325 guint instance
= (cal_satus
== SIPE_CAL_OOF
) ?
4326 sipe_get_pub_instance(sipe_private
, SIPE_PUB_STATE_CALENDAR_OOF
) :
4327 sipe_get_pub_instance(sipe_private
, SIPE_PUB_STATE_CALENDAR
);
4329 /* key is <category><instance><container> */
4330 gchar
*key_2
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 2);
4331 gchar
*key_3
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 3);
4332 struct sipe_publication
*publication_2
=
4333 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "state"), key_2
);
4334 struct sipe_publication
*publication_3
=
4335 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "state"), key_3
);
4340 if (!publication_3
&& !event
) { /* was nothing, have nothing, exiting */
4341 SIPE_DEBUG_INFO("sipe_publish_get_category_state_calendar: "
4342 "Exiting as no publication and no event for cal_satus:%d", cal_satus
);
4348 (publication_3
->availability
== availability
) &&
4349 sipe_strequal(publication_3
->cal_event_hash
, (tmp
= sipe_cal_event_hash(event
))))
4352 SIPE_DEBUG_INFO("sipe_publish_get_category_state_calendar: "
4353 "cal state has NOT changed for cal_satus:%d. Exiting.", cal_satus
);
4354 return NULL
; /* nothing to update */
4359 (event
->cal_status
== SIPE_CAL_BUSY
||
4360 event
->cal_status
== SIPE_CAL_OOF
))
4362 gchar
*availability_xml_str
= NULL
;
4363 gchar
*activity_xml_str
= NULL
;
4364 gchar
*escaped_subject
= event
->subject
? g_markup_escape_text(event
->subject
, -1) : NULL
;
4365 gchar
*escaped_location
= event
->location
? g_markup_escape_text(event
->location
, -1) : NULL
;
4367 if (event
->cal_status
== SIPE_CAL_BUSY
) {
4368 availability_xml_str
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_AVAIL
, 6500);
4371 if (event
->cal_status
== SIPE_CAL_BUSY
&& event
->is_meeting
) {
4372 activity_xml_str
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY
,
4373 sipe_activity_map
[SIPE_ACTIVITY_IN_MEETING
].token
,
4374 "minAvailability=\"6500\"",
4375 "maxAvailability=\"8999\"");
4376 } else if (event
->cal_status
== SIPE_CAL_OOF
) {
4377 activity_xml_str
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY
,
4378 sipe_activity_map
[SIPE_ACTIVITY_OOF
].token
,
4379 "minAvailability=\"12000\"",
4382 start_time_str
= sipe_utils_time_to_str(event
->start_time
);
4384 res
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR
,
4386 publication_2
? publication_2
->version
: 0,
4389 availability_xml_str
? availability_xml_str
: "",
4390 activity_xml_str
? activity_xml_str
: "",
4391 escaped_subject
? escaped_subject
: "",
4392 escaped_location
? escaped_location
: "",
4395 publication_3
? publication_3
->version
: 0,
4398 availability_xml_str
? availability_xml_str
: "",
4399 activity_xml_str
? activity_xml_str
: "",
4400 escaped_subject
? escaped_subject
: "",
4401 escaped_location
? escaped_location
: ""
4403 g_free(escaped_location
);
4404 g_free(escaped_subject
);
4405 g_free(start_time_str
);
4406 g_free(availability_xml_str
);
4407 g_free(activity_xml_str
);
4410 else /* including !event, SIPE_CAL_FREE, SIPE_CAL_TENTATIVE */
4412 res
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_CLEAR
,
4414 publication_2
? publication_2
->version
: 0,
4417 publication_3
? publication_3
->version
: 0
4425 * Returns 'machineState' XML part for publication.
4426 * Must be g_free'd after use.
4429 sipe_publish_get_category_state_machine(struct sipe_core_private
*sipe_private
)
4431 return sipe_publish_get_category_state(sipe_private
, FALSE
);
4435 * Returns 'userState' XML part for publication.
4436 * Must be g_free'd after use.
4439 sipe_publish_get_category_state_user(struct sipe_core_private
*sipe_private
)
4441 return sipe_publish_get_category_state(sipe_private
, TRUE
);
4445 * Returns 'note' XML part for publication.
4446 * Must be g_free'd after use.
4448 * Protocol format for Note is plain text.
4450 * @param note a note in Sipe internal HTML format
4451 * @param note_type either personal or OOF
4454 sipe_publish_get_category_note(struct sipe_core_private
*sipe_private
,
4455 const char *note
, /* html */
4456 const char *note_type
,
4460 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
4461 guint instance
= sipe_strequal("OOF", note_type
) ? sipe_get_pub_instance(sipe_private
, SIPE_PUB_NOTE_OOF
) : 0;
4462 /* key is <category><instance><container> */
4463 gchar
*key_note_200
= g_strdup_printf("<%s><%u><%u>", "note", instance
, 200);
4464 gchar
*key_note_300
= g_strdup_printf("<%s><%u><%u>", "note", instance
, 300);
4465 gchar
*key_note_400
= g_strdup_printf("<%s><%u><%u>", "note", instance
, 400);
4467 struct sipe_publication
*publication_note_200
=
4468 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "note"), key_note_200
);
4469 struct sipe_publication
*publication_note_300
=
4470 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "note"), key_note_300
);
4471 struct sipe_publication
*publication_note_400
=
4472 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "note"), key_note_400
);
4474 char *tmp
= note
? sipe_backend_markup_strip_html(note
) : NULL
;
4475 char *n1
= tmp
? g_markup_escape_text(tmp
, -1) : NULL
;
4476 const char *n2
= publication_note_200
? publication_note_200
->note
: NULL
;
4477 char *res
, *tmp1
, *tmp2
, *tmp3
;
4478 char *start_time_attr
;
4479 char *end_time_attr
;
4483 g_free(key_note_200
);
4484 g_free(key_note_300
);
4485 g_free(key_note_400
);
4487 /* we even need to republish empty note */
4488 if (sipe_strequal(n1
, n2
))
4490 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_note: note has NOT changed. Exiting.");
4492 return NULL
; /* nothing to update */
4495 start_time_attr
= note_start
? g_strdup_printf(" startTime=\"%s\"", (tmp
= sipe_utils_time_to_str(note_start
))) : NULL
;
4498 end_time_attr
= note_end
? g_strdup_printf(" endTime=\"%s\"", (tmp
= sipe_utils_time_to_str(note_end
))) : NULL
;
4502 tmp1
= g_strdup_printf(SIPE_PUB_XML_NOTE
,
4505 publication_note_200
? publication_note_200
->version
: 0,
4507 start_time_attr
? start_time_attr
: "",
4508 end_time_attr
? end_time_attr
: "",
4511 tmp2
= g_strdup_printf(SIPE_PUB_XML_NOTE
,
4514 publication_note_300
? publication_note_300
->version
: 0,
4516 start_time_attr
? start_time_attr
: "",
4517 end_time_attr
? end_time_attr
: "",
4520 tmp3
= g_strdup_printf(SIPE_PUB_XML_NOTE
,
4523 publication_note_400
? publication_note_400
->version
: 0,
4525 start_time_attr
? start_time_attr
: "",
4526 end_time_attr
? end_time_attr
: "",
4529 tmp1
= g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR
,
4533 publication_note_200
? publication_note_200
->version
: 0,
4535 tmp2
= g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR
,
4539 publication_note_200
? publication_note_200
->version
: 0,
4541 tmp3
= g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR
,
4545 publication_note_200
? publication_note_200
->version
: 0,
4548 res
= g_strconcat(tmp1
, tmp2
, tmp3
, NULL
);
4550 g_free(start_time_attr
);
4551 g_free(end_time_attr
);
4561 * Returns 'calendarData' XML part with WorkingHours for publication.
4562 * Must be g_free'd after use.
4565 sipe_publish_get_category_cal_working_hours(struct sipe_core_private
*sipe_private
)
4567 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
4568 struct sipe_calendar
* cal
= sip
->cal
;
4570 /* key is <category><instance><container> */
4571 gchar
*key_cal_1
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 1);
4572 gchar
*key_cal_100
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 100);
4573 gchar
*key_cal_200
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 200);
4574 gchar
*key_cal_300
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 300);
4575 gchar
*key_cal_400
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 400);
4576 gchar
*key_cal_32000
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 32000);
4578 struct sipe_publication
*publication_cal_1
=
4579 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_1
);
4580 struct sipe_publication
*publication_cal_100
=
4581 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_100
);
4582 struct sipe_publication
*publication_cal_200
=
4583 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_200
);
4584 struct sipe_publication
*publication_cal_300
=
4585 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_300
);
4586 struct sipe_publication
*publication_cal_400
=
4587 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_400
);
4588 struct sipe_publication
*publication_cal_32000
=
4589 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_32000
);
4591 const char *n1
= cal
? cal
->working_hours_xml_str
: NULL
;
4592 const char *n2
= publication_cal_300
? publication_cal_300
->working_hours_xml_str
: NULL
;
4595 g_free(key_cal_100
);
4596 g_free(key_cal_200
);
4597 g_free(key_cal_300
);
4598 g_free(key_cal_400
);
4599 g_free(key_cal_32000
);
4601 if (!cal
|| is_empty(cal
->email
) || is_empty(cal
->working_hours_xml_str
)) {
4602 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_working_hours: no data to publish, exiting");
4606 if (sipe_strequal(n1
, n2
))
4608 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_working_hours: WorkingHours has NOT changed. Exiting.");
4609 return NULL
; /* nothing to update */
4612 return g_strdup_printf(SIPE_PUB_XML_WORKING_HOURS
,
4614 publication_cal_1
? publication_cal_1
->version
: 0,
4616 cal
->working_hours_xml_str
,
4618 publication_cal_100
? publication_cal_100
->version
: 0,
4620 publication_cal_200
? publication_cal_200
->version
: 0,
4622 cal
->working_hours_xml_str
,
4624 publication_cal_300
? publication_cal_300
->version
: 0,
4626 cal
->working_hours_xml_str
,
4627 /* 400 - Personal */
4628 publication_cal_400
? publication_cal_400
->version
: 0,
4630 cal
->working_hours_xml_str
,
4631 /* 32000 - Blocked */
4632 publication_cal_32000
? publication_cal_32000
->version
: 0
4637 * Returns 'calendarData' XML part with FreeBusy for publication.
4638 * Must be g_free'd after use.
4641 sipe_publish_get_category_cal_free_busy(struct sipe_core_private
*sipe_private
)
4643 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
4644 struct sipe_calendar
* cal
= sip
->cal
;
4645 guint cal_data_instance
= sipe_get_pub_instance(sipe_private
, SIPE_PUB_CALENDAR_DATA
);
4647 char *free_busy_base64
;
4648 /* const char *st; */
4649 /* const char *fb; */
4652 /* key is <category><instance><container> */
4653 gchar
*key_cal_1
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 1);
4654 gchar
*key_cal_100
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 100);
4655 gchar
*key_cal_200
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 200);
4656 gchar
*key_cal_300
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 300);
4657 gchar
*key_cal_400
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 400);
4658 gchar
*key_cal_32000
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 32000);
4660 struct sipe_publication
*publication_cal_1
=
4661 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_1
);
4662 struct sipe_publication
*publication_cal_100
=
4663 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_100
);
4664 struct sipe_publication
*publication_cal_200
=
4665 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_200
);
4666 struct sipe_publication
*publication_cal_300
=
4667 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_300
);
4668 struct sipe_publication
*publication_cal_400
=
4669 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_400
);
4670 struct sipe_publication
*publication_cal_32000
=
4671 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_32000
);
4674 g_free(key_cal_100
);
4675 g_free(key_cal_200
);
4676 g_free(key_cal_300
);
4677 g_free(key_cal_400
);
4678 g_free(key_cal_32000
);
4680 if (!cal
|| is_empty(cal
->email
) || !cal
->fb_start
|| is_empty(cal
->free_busy
)) {
4681 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_free_busy: no data to publish, exiting");
4685 fb_start_str
= sipe_utils_time_to_str(cal
->fb_start
);
4686 free_busy_base64
= sipe_cal_get_freebusy_base64(cal
->free_busy
);
4688 /* we will rebuplish the same data to refresh publication time,
4689 * so if data from multiple sources, most recent will be choosen
4691 // st = publication_cal_300 ? publication_cal_300->fb_start_str : NULL;
4692 // fb = publication_cal_300 ? publication_cal_300->free_busy_base64 : NULL;
4694 //if (sipe_strequal(st, fb_start_str) && sipe_strequal(fb, free_busy_base64))
4696 // SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_free_busy: FreeBusy has NOT changed. Exiting.");
4697 // g_free(fb_start_str);
4698 // g_free(free_busy_base64);
4699 // return NULL; /* nothing to update */
4702 res
= g_strdup_printf(SIPE_PUB_XML_FREE_BUSY
,
4705 publication_cal_1
? publication_cal_1
->version
: 0,
4708 publication_cal_100
? publication_cal_100
->version
: 0,
4711 publication_cal_200
? publication_cal_200
->version
: 0,
4717 publication_cal_300
? publication_cal_300
->version
: 0,
4721 /* 400 - Personal */
4723 publication_cal_400
? publication_cal_400
->version
: 0,
4727 /* 32000 - Blocked */
4729 publication_cal_32000
? publication_cal_32000
->version
: 0
4732 g_free(fb_start_str
);
4733 g_free(free_busy_base64
);
4737 static void send_presence_publish(struct sipe_core_private
*sipe_private
,
4738 const char *publications
)
4745 uri
= sip_uri_self(sipe_private
);
4746 doc
= g_strdup_printf(SIPE_SEND_PRESENCE
,
4750 tmp
= get_contact(sipe_private
);
4751 hdr
= g_strdup_printf("Contact: %s\r\n"
4752 "Content-Type: application/msrtc-category-publish+xml\r\n", tmp
);
4754 sip_transport_service(sipe_private
,
4758 process_send_presence_category_publish_response
);
4767 send_publish_category_initial(struct sipe_core_private
*sipe_private
)
4769 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
4770 gchar
*pub_device
= sipe_publish_get_category_device(sipe_private
);
4772 gchar
*publications
;
4774 g_free(sip
->status
);
4775 sip
->status
= g_strdup(SIPE_STATUS_ID_AVAILABLE
); /* our initial state */
4777 pub_machine
= sipe_publish_get_category_state_machine(sipe_private
);
4778 publications
= g_strdup_printf("%s%s",
4780 pub_machine
? pub_machine
: "");
4782 g_free(pub_machine
);
4784 send_presence_publish(sipe_private
, publications
);
4785 g_free(publications
);
4789 send_presence_category_publish(struct sipe_core_private
*sipe_private
)
4791 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
4792 gchar
*pub_state
= sipe_is_user_state(sipe_private
) ?
4793 sipe_publish_get_category_state_user(sipe_private
) :
4794 sipe_publish_get_category_state_machine(sipe_private
);
4795 gchar
*pub_note
= sipe_publish_get_category_note(sipe_private
,
4797 sip
->is_oof_note
? "OOF" : "personal",
4800 gchar
*publications
;
4802 if (!pub_state
&& !pub_note
) {
4803 SIPE_DEBUG_INFO_NOFORMAT("send_presence_category_publish: nothing has changed. Exiting.");
4807 publications
= g_strdup_printf("%s%s",
4808 pub_state
? pub_state
: "",
4809 pub_note
? pub_note
: "");
4814 send_presence_publish(sipe_private
, publications
);
4815 g_free(publications
);
4819 * Publishes self status
4820 * based on own calendar information.
4825 publish_calendar_status_self(struct sipe_core_private
*sipe_private
,
4826 SIPE_UNUSED_PARAMETER
void *unused
)
4828 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
4829 struct sipe_cal_event
* event
= NULL
;
4830 gchar
*pub_cal_working_hours
= NULL
;
4831 gchar
*pub_cal_free_busy
= NULL
;
4832 gchar
*pub_calendar
= NULL
;
4833 gchar
*pub_calendar2
= NULL
;
4834 gchar
*pub_oof_note
= NULL
;
4835 const gchar
*oof_note
;
4836 time_t oof_start
= 0;
4840 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self() no calendar data.");
4844 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self() started.");
4845 if (sip
->cal
->cal_events
) {
4846 event
= sipe_cal_get_event(sip
->cal
->cal_events
, time(NULL
));
4850 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self: current event is NULL");
4852 char *desc
= sipe_cal_event_describe(event
);
4853 SIPE_DEBUG_INFO("publish_calendar_status_self: current event is:\n%s", desc
? desc
: "");
4859 OOF publish, Busy clean
4861 OOF clean, Busy publish
4863 OOF clean, Busy clean
4865 if (event
&& event
->cal_status
== SIPE_CAL_OOF
) {
4866 pub_calendar
= sipe_publish_get_category_state_calendar(sipe_private
, event
, sip
->cal
->email
, SIPE_CAL_OOF
);
4867 pub_calendar2
= sipe_publish_get_category_state_calendar(sipe_private
, NULL
, sip
->cal
->email
, SIPE_CAL_BUSY
);
4868 } else if (event
&& event
->cal_status
== SIPE_CAL_BUSY
) {
4869 pub_calendar
= sipe_publish_get_category_state_calendar(sipe_private
, NULL
, sip
->cal
->email
, SIPE_CAL_OOF
);
4870 pub_calendar2
= sipe_publish_get_category_state_calendar(sipe_private
, event
, sip
->cal
->email
, SIPE_CAL_BUSY
);
4872 pub_calendar
= sipe_publish_get_category_state_calendar(sipe_private
, NULL
, sip
->cal
->email
, SIPE_CAL_OOF
);
4873 pub_calendar2
= sipe_publish_get_category_state_calendar(sipe_private
, NULL
, sip
->cal
->email
, SIPE_CAL_BUSY
);
4876 oof_note
= sipe_ews_get_oof_note(sip
->cal
);
4877 if (sipe_strequal("Scheduled", sip
->cal
->oof_state
)) {
4878 oof_start
= sip
->cal
->oof_start
;
4879 oof_end
= sip
->cal
->oof_end
;
4881 pub_oof_note
= sipe_publish_get_category_note(sipe_private
, oof_note
, "OOF", oof_start
, oof_end
);
4883 pub_cal_working_hours
= sipe_publish_get_category_cal_working_hours(sipe_private
);
4884 pub_cal_free_busy
= sipe_publish_get_category_cal_free_busy(sipe_private
);
4886 if (!pub_cal_working_hours
&& !pub_cal_free_busy
&& !pub_calendar
&& !pub_calendar2
&& !pub_oof_note
) {
4887 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self: nothing has changed.");
4889 gchar
*publications
= g_strdup_printf("%s%s%s%s%s",
4890 pub_cal_working_hours
? pub_cal_working_hours
: "",
4891 pub_cal_free_busy
? pub_cal_free_busy
: "",
4892 pub_calendar
? pub_calendar
: "",
4893 pub_calendar2
? pub_calendar2
: "",
4894 pub_oof_note
? pub_oof_note
: "");
4896 send_presence_publish(sipe_private
, publications
);
4897 g_free(publications
);
4900 g_free(pub_cal_working_hours
);
4901 g_free(pub_cal_free_busy
);
4902 g_free(pub_calendar
);
4903 g_free(pub_calendar2
);
4904 g_free(pub_oof_note
);
4906 /* repeat scheduling */
4907 sipe_sched_calendar_status_self_publish(sipe_private
, time(NULL
));
4910 static void send_presence_status(struct sipe_core_private
*sipe_private
,
4911 SIPE_UNUSED_PARAMETER
void *unused
)
4913 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
4914 PurpleStatus
* status
= purple_account_get_active_status(sip
->account
);
4916 if (!status
) return;
4918 SIPE_DEBUG_INFO("send_presence_status: status: %s (%s)",
4919 purple_status_get_id(status
) ? purple_status_get_id(status
) : "",
4920 sipe_is_user_state(sipe_private
) ? "USER" : "MACHINE");
4922 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
4923 send_presence_category_publish(sipe_private
);
4925 send_presence_soap(sipe_private
, FALSE
);
4929 static guint
sipe_ht_hash_nick(const char *nick
)
4931 char *lc
= g_utf8_strdown(nick
, -1);
4932 guint bucket
= g_str_hash(lc
);
4938 static gboolean
sipe_ht_equals_nick(const char *nick1
, const char *nick2
)
4940 char *nick1_norm
= NULL
;
4941 char *nick2_norm
= NULL
;
4944 if (nick1
== NULL
&& nick2
== NULL
) return TRUE
;
4945 if (nick1
== NULL
|| nick2
== NULL
||
4946 !g_utf8_validate(nick1
, -1, NULL
) ||
4947 !g_utf8_validate(nick2
, -1, NULL
)) return FALSE
;
4949 nick1_norm
= g_utf8_casefold(nick1
, -1);
4950 nick2_norm
= g_utf8_casefold(nick2
, -1);
4951 equal
= g_utf8_collate(nick1_norm
, nick2_norm
) == 0;
4958 /* temporary function */
4959 void sipe_purple_setup(struct sipe_core_public
*sipe_public
,
4960 PurpleConnection
*gc
)
4962 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA
;
4964 sip
->account
= purple_connection_get_account(gc
);
4967 struct sipe_core_public
*sipe_core_allocate(const gchar
*signin_name
,
4968 const gchar
*login_domain
,
4969 const gchar
*login_account
,
4970 const gchar
*password
,
4972 const gchar
*email_url
,
4973 const gchar
**errmsg
)
4975 struct sipe_core_private
*sipe_private
;
4976 struct sipe_account_data
*sip
;
4977 gchar
**user_domain
;
4979 SIPE_DEBUG_INFO("sipe_core_allocate: signin_name '%s'", signin_name
);
4981 /* ensure that sign-in name doesn't contain invalid characters */
4982 if (strpbrk(signin_name
, "\t\v\r\n") != NULL
) {
4983 *errmsg
= _("SIP Exchange user name contains invalid characters");
4987 /* ensure that sign-in name format is name@domain */
4988 if (!strchr(signin_name
, '@') ||
4989 g_str_has_prefix(signin_name
, "@") ||
4990 g_str_has_suffix(signin_name
, "@")) {
4991 *errmsg
= _("User name should be a valid SIP URI\nExample: user@company.com");
4995 /* ensure that email format is name@domain (if provided) */
4996 if (!is_empty(email
) &&
4997 (!strchr(email
, '@') ||
4998 g_str_has_prefix(email
, "@") ||
4999 g_str_has_suffix(email
, "@")))
5001 *errmsg
= _("Email address should be valid if provided\nExample: user@company.com");
5005 /* ensure that user name doesn't contain spaces */
5006 user_domain
= g_strsplit(signin_name
, "@", 2);
5007 SIPE_DEBUG_INFO("sipe_core_allocate: user '%s' domain '%s'", user_domain
[0], user_domain
[1]);
5008 if (strchr(user_domain
[0], ' ') != NULL
) {
5009 g_strfreev(user_domain
);
5010 *errmsg
= _("SIP Exchange user name contains whitespace");
5014 /* ensure that email_url is in proper format if enabled (if provided).
5015 * Example (Exchange): https://server.company.com/EWS/Exchange.asmx
5016 * Example (Domino) : https://[domino_server]/[mail_database_name].nsf
5018 if (!is_empty(email_url
)) {
5019 char *tmp
= g_ascii_strdown(email_url
, -1);
5020 if (!g_str_has_prefix(tmp
, "https://"))
5023 g_strfreev(user_domain
);
5024 *errmsg
= _("Email services URL should be valid if provided\n"
5025 "Example: https://exchange.corp.com/EWS/Exchange.asmx\n"
5026 "Example: https://domino.corp.com/maildatabase.nsf");
5032 sipe_private
= g_new0(struct sipe_core_private
, 1);
5033 sipe_private
->temporary
= sip
= g_new0(struct sipe_account_data
, 1);
5034 sip
->subscribed_buddies
= FALSE
;
5035 sip
->initial_state_published
= FALSE
;
5036 sipe_private
->username
= g_strdup(signin_name
);
5037 sip
->email
= is_empty(email
) ? g_strdup(signin_name
) : g_strdup(email
);
5038 sip
->authdomain
= is_empty(login_domain
) ? NULL
: g_strdup(login_domain
);
5039 sip
->authuser
= is_empty(login_account
) ? NULL
: g_strdup(login_account
);
5040 sip
->password
= g_strdup(password
);
5041 sipe_private
->public.sip_name
= g_strdup(user_domain
[0]);
5042 sipe_private
->public.sip_domain
= g_strdup(user_domain
[1]);
5043 g_strfreev(user_domain
);
5045 sipe_private
->buddies
= g_hash_table_new((GHashFunc
)sipe_ht_hash_nick
, (GEqualFunc
)sipe_ht_equals_nick
);
5046 sip
->our_publications
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
5047 g_free
, (GDestroyNotify
)g_hash_table_destroy
);
5048 sipe_subscriptions_init(sipe_private
);
5049 sip
->status
= g_strdup(SIPE_STATUS_ID_UNKNOWN
);
5051 return((struct sipe_core_public
*)sipe_private
);
5055 sipe_blist_menu_free_containers(struct sipe_core_private
*sipe_private
);
5057 void sipe_connection_cleanup(struct sipe_core_private
*sipe_private
)
5059 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
5061 g_free(sipe_private
->epid
);
5062 sipe_private
->epid
= NULL
;
5064 sip_transport_disconnect(sipe_private
);
5066 sipe_schedule_cancel_all(sipe_private
);
5068 if (sip
->allow_events
) {
5069 GSList
*entry
= sip
->allow_events
;
5071 g_free(entry
->data
);
5072 entry
= entry
->next
;
5075 g_slist_free(sip
->allow_events
);
5077 if (sip
->containers
) {
5078 GSList
*entry
= sip
->containers
;
5080 free_container((struct sipe_container
*)entry
->data
);
5081 entry
= entry
->next
;
5084 g_slist_free(sip
->containers
);
5086 /* libpurple memory leak workaround */
5087 sipe_blist_menu_free_containers(sipe_private
);
5089 if (sipe_private
->contact
)
5090 g_free(sipe_private
->contact
);
5091 sipe_private
->contact
= NULL
;
5093 g_free(sip
->regcallid
);
5094 sip
->regcallid
= NULL
;
5096 if (sipe_private
->focus_factory_uri
)
5097 g_free(sipe_private
->focus_factory_uri
);
5098 sipe_private
->focus_factory_uri
= NULL
;
5101 sipe_cal_calendar_free(sip
->cal
);
5105 sipe_groupchat_free(sipe_private
);
5109 * A callback for g_hash_table_foreach_remove
5111 static gboolean
sipe_buddy_remove(SIPE_UNUSED_PARAMETER gpointer key
, gpointer buddy
,
5112 SIPE_UNUSED_PARAMETER gpointer user_data
)
5114 sipe_free_buddy((struct sipe_buddy
*) buddy
);
5116 /* We must return TRUE as the key/value have already been deleted */
5120 void sipe_buddy_free_all(struct sipe_core_private
*sipe_private
)
5122 g_hash_table_foreach_steal(sipe_private
->buddies
, sipe_buddy_remove
, NULL
);
5125 static void sipe_searchresults_im_buddy(PurpleConnection
*gc
, GList
*row
,
5126 SIPE_UNUSED_PARAMETER
void *user_data
)
5128 PurpleAccount
*acct
= purple_connection_get_account(gc
);
5129 char *id
= sip_uri_from_name((gchar
*)g_list_nth_data(row
, 0));
5130 PurpleConversation
*conv
= purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM
, id
, acct
);
5132 conv
= purple_conversation_new(PURPLE_CONV_TYPE_IM
, acct
, id
);
5133 purple_conversation_present(conv
);
5137 static void sipe_searchresults_add_buddy(PurpleConnection
*gc
, GList
*row
,
5138 SIPE_UNUSED_PARAMETER
void *user_data
)
5141 purple_blist_request_add_buddy(purple_connection_get_account(gc
),
5142 g_list_nth_data(row
, 0), _("Other Contacts"), g_list_nth_data(row
, 1));
5145 static gboolean
process_search_contact_response(struct sipe_core_private
*sipe_private
,
5147 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
5149 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
5150 PurpleNotifySearchResults
*results
;
5151 PurpleNotifySearchColumn
*column
;
5152 sipe_xml
*searchResults
;
5153 const sipe_xml
*mrow
;
5154 int match_count
= 0;
5155 gboolean more
= FALSE
;
5158 SIPE_DEBUG_INFO("process_search_contact_response: body:\n%s", msg
->body
? msg
->body
: "");
5160 searchResults
= sipe_xml_parse(msg
->body
, msg
->bodylen
);
5161 if (!searchResults
) {
5162 SIPE_DEBUG_INFO_NOFORMAT("process_search_contact_response: no parseable searchResults");
5166 results
= purple_notify_searchresults_new();
5168 if (results
== NULL
) {
5169 SIPE_DEBUG_ERROR_NOFORMAT("purple_parse_searchreply: Unable to display the search results.");
5170 purple_notify_error(sip
->gc
, NULL
, _("Unable to display the search results"), NULL
);
5172 sipe_xml_free(searchResults
);
5176 column
= purple_notify_searchresults_column_new(_("User name"));
5177 purple_notify_searchresults_column_add(results
, column
);
5179 column
= purple_notify_searchresults_column_new(_("Name"));
5180 purple_notify_searchresults_column_add(results
, column
);
5182 column
= purple_notify_searchresults_column_new(_("Company"));
5183 purple_notify_searchresults_column_add(results
, column
);
5185 column
= purple_notify_searchresults_column_new(_("Country"));
5186 purple_notify_searchresults_column_add(results
, column
);
5188 column
= purple_notify_searchresults_column_new(_("Email"));
5189 purple_notify_searchresults_column_add(results
, column
);
5191 for (mrow
= sipe_xml_child(searchResults
, "Body/Array/row"); mrow
; mrow
= sipe_xml_twin(mrow
)) {
5194 gchar
**uri_parts
= g_strsplit(sipe_xml_attribute(mrow
, "uri"), ":", 2);
5195 row
= g_list_append(row
, g_strdup(uri_parts
[1]));
5196 g_strfreev(uri_parts
);
5198 row
= g_list_append(row
, g_strdup(sipe_xml_attribute(mrow
, "displayName")));
5199 row
= g_list_append(row
, g_strdup(sipe_xml_attribute(mrow
, "company")));
5200 row
= g_list_append(row
, g_strdup(sipe_xml_attribute(mrow
, "country")));
5201 row
= g_list_append(row
, g_strdup(sipe_xml_attribute(mrow
, "email")));
5203 purple_notify_searchresults_row_add(results
, row
);
5207 if ((mrow
= sipe_xml_child(searchResults
, "Body/directorySearch/moreAvailable")) != NULL
) {
5208 char *data
= sipe_xml_data(mrow
);
5209 more
= (g_strcasecmp(data
, "true") == 0);
5213 secondary
= g_strdup_printf(
5214 dngettext(PACKAGE_NAME
,
5215 "Found %d contact%s:",
5216 "Found %d contacts%s:", match_count
),
5217 match_count
, more
? _(" (more matched your query)") : "");
5219 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_IM
, sipe_searchresults_im_buddy
);
5220 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_ADD
, sipe_searchresults_add_buddy
);
5221 purple_notify_searchresults(sip
->gc
, NULL
, NULL
, secondary
, results
, NULL
, NULL
);
5224 sipe_xml_free(searchResults
);
5228 void sipe_search_contact_with_cb(PurpleConnection
*gc
, PurpleRequestFields
*fields
)
5230 GList
*entries
= purple_request_field_group_get_fields(purple_request_fields_get_groups(fields
)->data
);
5231 gchar
**attrs
= g_new(gchar
*, g_list_length(entries
) + 1);
5237 PurpleRequestField
*field
= entries
->data
;
5238 const char *id
= purple_request_field_get_id(field
);
5239 const char *value
= purple_request_field_string_get_value(field
);
5241 SIPE_DEBUG_INFO("sipe_search_contact_with_cb: %s = '%s'", id
, value
? value
: "");
5243 if (value
!= NULL
) attrs
[i
++] = g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW
, id
, value
);
5244 } while ((entries
= g_list_next(entries
)) != NULL
);
5248 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
5249 gchar
*domain_uri
= sip_uri_from_name(sipe_private
->public.sip_domain
);
5250 gchar
*query
= g_strjoinv(NULL
, attrs
);
5251 gchar
*body
= g_strdup_printf(SIPE_SOAP_SEARCH_CONTACT
, 100, query
);
5252 SIPE_DEBUG_INFO("sipe_search_contact_with_cb: body:\n%s", body
? body
: "");
5253 send_soap_request_with_cb(sipe_private
, domain_uri
, body
,
5254 process_search_contact_response
, NULL
);
5263 static void sipe_publish_get_cat_state_user_to_clear(SIPE_UNUSED_PARAMETER
const char *name
,
5267 struct sipe_publication
*publication
= value
;
5269 g_string_append_printf( str
,
5270 SIPE_PUB_XML_PUBLICATION_CLEAR
,
5271 publication
->category
,
5272 publication
->instance
,
5273 publication
->container
,
5274 publication
->version
,
5278 void sipe_core_reset_status(struct sipe_core_public
*sipe_public
)
5280 struct sipe_core_private
*sipe_private
= SIPE_CORE_PRIVATE
;
5281 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA
;
5282 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) /* 2007+ */
5284 GString
* str
= g_string_new(NULL
);
5285 gchar
*publications
;
5287 if (!sip
->user_state_publications
|| g_hash_table_size(sip
->user_state_publications
) == 0) {
5288 SIPE_DEBUG_INFO_NOFORMAT("sipe_reset_status: no userState publications, exiting.");
5292 g_hash_table_foreach(sip
->user_state_publications
, (GHFunc
)sipe_publish_get_cat_state_user_to_clear
, str
);
5293 publications
= g_string_free(str
, FALSE
);
5295 send_presence_publish(sipe_private
, publications
);
5296 g_free(publications
);
5300 send_presence_soap0(sipe_private
, FALSE
, TRUE
);
5304 /** for Access levels menu */
5305 #define INDENT_FMT " %s"
5307 /** Member is directly placed to access level container.
5308 * For example SIP URI of user is in the container.
5310 #define INDENT_MARKED_FMT "* %s"
5312 /** Member is indirectly belong to access level container.
5313 * For example 'sameEnterprise' is in the container and user
5314 * belongs to that same enterprise.
5316 #define INDENT_MARKED_INHERITED_FMT "= %s"
5318 GSList
*sipe_core_buddy_info(struct sipe_core_public
*sipe_public
,
5320 const gchar
*status_name
,
5323 struct sipe_core_private
*sipe_private
= SIPE_CORE_PRIVATE
;
5325 gboolean is_oof_note
= FALSE
;
5326 const gchar
*activity
= NULL
;
5327 gchar
*calendar
= NULL
;
5328 const gchar
*meeting_subject
= NULL
;
5329 const gchar
*meeting_location
= NULL
;
5330 gchar
*access_text
= NULL
;
5331 GSList
*info
= NULL
;
5333 #define SIPE_ADD_BUDDY_INFO_COMMON(l, t) \
5335 struct sipe_buddy_info *sbi = g_malloc(sizeof(struct sipe_buddy_info)); \
5338 info = g_slist_append(info, sbi); \
5340 #define SIPE_ADD_BUDDY_INFO(l, t) SIPE_ADD_BUDDY_INFO_COMMON((l), g_markup_escape_text((t), -1))
5341 #define SIPE_ADD_BUDDY_INFO_NOESCAPE(l, t) SIPE_ADD_BUDDY_INFO_COMMON((l), (t))
5343 if (sipe_public
) { //happens on pidgin exit
5344 struct sipe_buddy
*sbuddy
= g_hash_table_lookup(sipe_private
->buddies
, name
);
5346 note
= sbuddy
->note
;
5347 is_oof_note
= sbuddy
->is_oof_note
;
5348 activity
= sbuddy
->activity
;
5349 calendar
= sipe_cal_get_description(sbuddy
);
5350 meeting_subject
= sbuddy
->meeting_subject
;
5351 meeting_location
= sbuddy
->meeting_location
;
5353 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
5354 gboolean is_group_access
= FALSE
;
5355 const int container_id
= sipe_find_access_level(sipe_private
, "user", sipe_get_no_sip_uri(name
), &is_group_access
);
5356 const char *access_level
= sipe_get_access_level_name(container_id
);
5357 access_text
= is_group_access
?
5358 g_strdup(access_level
) :
5359 g_strdup_printf(INDENT_MARKED_FMT
, access_level
);
5366 const gchar
*status_str
= activity
? activity
: status_name
;
5368 SIPE_ADD_BUDDY_INFO(_("Status"), status_str
);
5370 if (is_online
&& !is_empty(calendar
))
5372 SIPE_ADD_BUDDY_INFO(_("Calendar"), calendar
);
5375 if (!is_empty(meeting_location
))
5377 SIPE_DEBUG_INFO("sipe_tooltip_text: %s meeting location: '%s'", name
, meeting_location
);
5378 SIPE_ADD_BUDDY_INFO(_("Meeting in"), meeting_location
);
5380 if (!is_empty(meeting_subject
))
5382 SIPE_DEBUG_INFO("sipe_tooltip_text: %s meeting subject: '%s'", name
, meeting_subject
);
5383 SIPE_ADD_BUDDY_INFO(_("Meeting about"), meeting_subject
);
5387 SIPE_DEBUG_INFO("sipe_tooltip_text: %s note: '%s'", name
, note
);
5388 SIPE_ADD_BUDDY_INFO_NOESCAPE(is_oof_note
? _("Out of office note") : _("Note"),
5389 g_strdup_printf("<i>%s</i>", note
));
5392 SIPE_ADD_BUDDY_INFO(_("Access level"), access_text
);
5393 g_free(access_text
);
5399 static PurpleBuddy
*
5400 purple_blist_add_buddy_clone(PurpleGroup
* group
, PurpleBuddy
* buddy
)
5403 const gchar
*server_alias
, *email
;
5404 const PurpleStatus
*status
= purple_presence_get_active_status(purple_buddy_get_presence(buddy
));
5406 clone
= purple_buddy_new(buddy
->account
, buddy
->name
, buddy
->alias
);
5408 purple_blist_add_buddy(clone
, NULL
, group
, NULL
);
5410 server_alias
= purple_buddy_get_server_alias(buddy
);
5412 purple_blist_server_alias_buddy(clone
, server_alias
);
5415 email
= purple_blist_node_get_string(&buddy
->node
, EMAIL_PROP
);
5417 purple_blist_node_set_string(&clone
->node
, EMAIL_PROP
, email
);
5420 purple_presence_set_status_active(purple_buddy_get_presence(clone
), purple_status_get_id(status
), TRUE
);
5422 purple_prpl_got_user_status(clone
->account
, clone
->name
, purple_status_get_id(status
), NULL
);
5427 sipe_buddy_menu_copy_to_cb(PurpleBlistNode
*node
, const char *group_name
)
5429 PurpleBuddy
*buddy
, *b
;
5430 PurpleConnection
*gc
;
5431 PurpleGroup
* group
= purple_find_group(group_name
);
5433 g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node
));
5435 buddy
= (PurpleBuddy
*)node
;
5437 SIPE_DEBUG_INFO("sipe_buddy_menu_copy_to_cb: copying %s to %s", buddy
->name
, group_name
);
5438 gc
= purple_account_get_connection(buddy
->account
);
5440 b
= purple_find_buddy_in_group(buddy
->account
, buddy
->name
, group
);
5442 b
= purple_blist_add_buddy_clone(group
, buddy
);
5445 sipe_add_buddy(gc
, b
, group
);
5449 sipe_buddy_menu_chat_new_cb(PurpleBuddy
*buddy
)
5451 struct sipe_core_private
*sipe_private
= PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE
;
5453 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_new_cb: buddy->name=%s", buddy
->name
);
5455 /* 2007+ conference */
5456 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
))
5458 sipe_conf_add(sipe_private
, buddy
->name
);
5460 else /* 2005- multiparty chat */
5462 gchar
*self
= sip_uri_self(sipe_private
);
5463 struct sip_session
*session
;
5465 session
= sipe_session_add_chat(sipe_private
,
5469 session
->chat_session
->backend
= sipe_backend_chat_create(SIPE_CORE_PUBLIC
,
5470 session
->chat_session
,
5471 session
->chat_session
->title
,
5475 sipe_im_invite(sipe_private
, session
, buddy
->name
, NULL
, NULL
, NULL
, FALSE
);
5480 * For 2007+ conference only.
5483 sipe_buddy_menu_chat_make_leader_cb(PurpleBuddy
*buddy
,
5484 struct sipe_chat_session
*chat_session
)
5486 struct sipe_core_private
*sipe_private
= PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE
;
5487 struct sip_session
*session
;
5489 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_make_leader_cb: buddy->name=%s", buddy
->name
);
5490 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_make_leader_cb: chat_title=%s", chat_session
->title
);
5492 session
= sipe_session_find_chat(sipe_private
, chat_session
);
5494 sipe_conf_modify_user_role(sipe_private
, session
, buddy
->name
);
5498 * For 2007+ conference only.
5501 sipe_buddy_menu_chat_remove_cb(PurpleBuddy
*buddy
,
5502 struct sipe_chat_session
*chat_session
)
5504 struct sipe_core_private
*sipe_private
= PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE
;
5505 struct sip_session
*session
;
5507 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_remove_cb: buddy->name=%s", buddy
->name
);
5508 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_remove_cb: chat_title=%s", chat_session
->title
);
5510 session
= sipe_session_find_chat(sipe_private
, chat_session
);
5512 sipe_conf_delete_user(sipe_private
, session
, buddy
->name
);
5516 sipe_buddy_menu_chat_invite_cb(PurpleBuddy
*buddy
,
5517 struct sipe_chat_session
*chat_session
)
5519 struct sipe_core_private
*sipe_private
= PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE
;
5521 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_invite_cb: buddy->name=%s", buddy
->name
);
5522 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_invite_cb: chat_title=%s", chat_session
->title
);
5524 sipe_core_chat_invite(SIPE_CORE_PUBLIC
, chat_session
, buddy
->name
);
5528 sipe_buddy_menu_make_call_cb(PurpleBuddy
*buddy
, const char *phone
)
5530 struct sipe_core_private
*sipe_private
= PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE
;
5532 SIPE_DEBUG_INFO("sipe_buddy_menu_make_call_cb: buddy->name=%s", buddy
->name
);
5534 char *tel_uri
= sip_to_tel_uri(phone
);
5536 SIPE_DEBUG_INFO("sipe_buddy_menu_make_call_cb: going to call number: %s", tel_uri
? tel_uri
: "");
5537 sip_csta_make_call(sipe_private
, tel_uri
);
5544 sipe_buddy_menu_access_level_help_cb(PurpleBuddy
*buddy
)
5546 /** Translators: replace with URL to localized page
5547 * If it doesn't exist copy the original URL */
5548 purple_notify_uri(buddy
->account
->gc
, _("https://sourceforge.net/apps/mediawiki/sipe/index.php?title=Access_Levels"));
5552 sipe_buddy_menu_send_email_cb(PurpleBuddy
*buddy
)
5555 SIPE_DEBUG_INFO("sipe_buddy_menu_send_email_cb: buddy->name=%s", buddy
->name
);
5557 email
= purple_blist_node_get_string(&buddy
->node
, EMAIL_PROP
);
5560 char *command_line
= g_strdup_printf(
5566 " mailto:%s", email
);
5567 SIPE_DEBUG_INFO("sipe_buddy_menu_send_email_cb: going to call email client: %s", command_line
);
5569 g_spawn_command_line_async(command_line
, NULL
);
5570 g_free(command_line
);
5574 SIPE_DEBUG_INFO("sipe_buddy_menu_send_email_cb: no email address stored for buddy=%s", buddy
->name
);
5579 sipe_buddy_menu_access_level_cb(PurpleBuddy
*buddy
,
5580 struct sipe_container
*container
)
5582 struct sipe_core_private
*sipe_private
= PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE
;
5583 struct sipe_container_member
*member
;
5585 if (!container
|| !container
->members
) return;
5587 member
= ((struct sipe_container_member
*)container
->members
->data
);
5589 if (!member
->type
) return;
5591 SIPE_DEBUG_INFO("sipe_buddy_menu_access_level_cb: container->id=%d, member->type=%s, member->value=%s",
5592 container
->id
, member
->type
, member
->value
? member
->value
: "");
5594 sipe_change_access_level(sipe_private
, container
->id
, member
->type
, member
->value
);
5598 sipe_get_access_control_menu(struct sipe_core_private
*sipe_private
,
5602 * A menu which appear when right-clicking on buddy in contact list.
5605 sipe_buddy_menu(PurpleBuddy
*buddy
)
5607 PurpleBlistNode
*g_node
;
5608 PurpleGroup
*gr_parent
;
5609 PurpleMenuAction
*act
;
5611 GList
*menu_groups
= NULL
;
5612 struct sipe_core_private
*sipe_private
= PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE
;
5613 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
5615 gchar
*self
= sip_uri_self(sipe_private
);
5617 SIPE_SESSION_FOREACH
{
5618 if (!sipe_strcase_equal(self
, buddy
->name
) && session
->chat_session
)
5620 struct sipe_chat_session
*chat_session
= session
->chat_session
;
5621 gboolean is_conf
= (chat_session
->type
== SIPE_CHAT_TYPE_CONFERENCE
);
5623 if (sipe_backend_chat_find(chat_session
->backend
, buddy
->name
))
5625 gboolean conf_op
= sipe_backend_chat_is_operator(chat_session
->backend
, self
);
5628 && !sipe_backend_chat_is_operator(chat_session
->backend
, buddy
->name
) /* Not conf OP */
5629 && conf_op
) /* We are a conf OP */
5631 gchar
*label
= g_strdup_printf(_("Make leader of '%s'"),
5632 chat_session
->title
);
5633 act
= purple_menu_action_new(label
,
5634 PURPLE_CALLBACK(sipe_buddy_menu_chat_make_leader_cb
),
5635 chat_session
, NULL
);
5637 menu
= g_list_prepend(menu
, act
);
5641 && conf_op
) /* We are a conf OP */
5643 gchar
*label
= g_strdup_printf(_("Remove from '%s'"),
5644 chat_session
->title
);
5645 act
= purple_menu_action_new(label
,
5646 PURPLE_CALLBACK(sipe_buddy_menu_chat_remove_cb
),
5647 chat_session
, NULL
);
5649 menu
= g_list_prepend(menu
, act
);
5655 || (is_conf
&& !session
->locked
))
5657 gchar
*label
= g_strdup_printf(_("Invite to '%s'"),
5658 chat_session
->title
);
5659 act
= purple_menu_action_new(label
,
5660 PURPLE_CALLBACK(sipe_buddy_menu_chat_invite_cb
),
5661 chat_session
, NULL
);
5663 menu
= g_list_prepend(menu
, act
);
5667 } SIPE_SESSION_FOREACH_END
;
5669 act
= purple_menu_action_new(_("New chat"),
5670 PURPLE_CALLBACK(sipe_buddy_menu_chat_new_cb
),
5672 menu
= g_list_prepend(menu
, act
);
5674 if (sip
->csta
&& !sip
->csta
->line_status
) {
5676 const char *phone_disp_str
;
5679 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_PROP
);
5680 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_DISPLAY_PROP
);
5682 gchar
*label
= g_strdup_printf(_("Work %s"),
5683 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
5684 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
5688 menu
= g_list_prepend(menu
, act
);
5692 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_MOBILE_PROP
);
5693 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_MOBILE_DISPLAY_PROP
);
5695 gchar
*label
= g_strdup_printf(_("Mobile %s"),
5696 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
5697 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
5701 menu
= g_list_prepend(menu
, act
);
5705 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_HOME_PROP
);
5706 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_HOME_DISPLAY_PROP
);
5708 gchar
*label
= g_strdup_printf(_("Home %s"),
5709 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
5710 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
5714 menu
= g_list_prepend(menu
, act
);
5718 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_OTHER_PROP
);
5719 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_OTHER_DISPLAY_PROP
);
5721 gchar
*label
= g_strdup_printf(_("Other %s"),
5722 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
5723 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
5727 menu
= g_list_prepend(menu
, act
);
5731 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_CUSTOM1_PROP
);
5732 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_CUSTOM1_DISPLAY_PROP
);
5734 gchar
*label
= g_strdup_printf(_("Custom1 %s"),
5735 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
5736 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
5740 menu
= g_list_prepend(menu
, act
);
5744 email
= purple_blist_node_get_string(&buddy
->node
, EMAIL_PROP
);
5746 act
= purple_menu_action_new(_("Send email..."),
5747 PURPLE_CALLBACK(sipe_buddy_menu_send_email_cb
),
5749 menu
= g_list_prepend(menu
, act
);
5753 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
5754 GList
*menu_access_levels
= sipe_get_access_control_menu(sipe_private
, buddy
->name
);
5756 act
= purple_menu_action_new(_("Access level"),
5758 NULL
, menu_access_levels
);
5759 menu
= g_list_prepend(menu
, act
);
5763 gr_parent
= purple_buddy_get_group(buddy
);
5764 for (g_node
= purple_blist_get_root(); g_node
; g_node
= g_node
->next
) {
5767 if (g_node
->type
!= PURPLE_BLIST_GROUP_NODE
)
5770 group
= (PurpleGroup
*)g_node
;
5771 if (group
== gr_parent
)
5774 if (purple_find_buddy_in_group(buddy
->account
, buddy
->name
, group
))
5777 act
= purple_menu_action_new(purple_group_get_name(group
),
5778 PURPLE_CALLBACK(sipe_buddy_menu_copy_to_cb
),
5780 menu_groups
= g_list_prepend(menu_groups
, act
);
5782 menu_groups
= g_list_reverse(menu_groups
);
5784 act
= purple_menu_action_new(_("Copy to"),
5787 menu
= g_list_prepend(menu
, act
);
5789 menu
= g_list_reverse(menu
);
5796 sipe_ask_access_domain_cb(PurpleConnection
*gc
, PurpleRequestFields
*fields
)
5798 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
5799 const char *domain
= purple_request_fields_get_string(fields
, "access_domain");
5800 int index
= purple_request_fields_get_choice(fields
, "container_id");
5801 /* move Blocked first */
5802 int i
= (index
== 4) ? 0 : index
+ 1;
5803 int container_id
= containers
[i
];
5805 SIPE_DEBUG_INFO("sipe_ask_access_domain_cb: domain=%s, container_id=(%d)%d", domain
? domain
: "", index
, container_id
);
5807 sipe_change_access_level(sipe_private
, container_id
, "domain", domain
);
5811 sipe_ask_access_domain(struct sipe_core_private
*sipe_private
)
5813 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
5814 PurpleAccount
*account
= sip
->account
;
5815 PurpleConnection
*gc
= sip
->gc
;
5816 PurpleRequestFields
*fields
;
5817 PurpleRequestFieldGroup
*g
;
5818 PurpleRequestField
*f
;
5820 fields
= purple_request_fields_new();
5822 g
= purple_request_field_group_new(NULL
);
5823 f
= purple_request_field_string_new("access_domain", _("Domain"), "partner-company.com", FALSE
);
5824 purple_request_field_set_required(f
, TRUE
);
5825 purple_request_field_group_add_field(g
, f
);
5827 f
= purple_request_field_choice_new("container_id", _("Access level"), 0);
5828 purple_request_field_choice_add(f
, _("Personal")); /* index 0 */
5829 purple_request_field_choice_add(f
, _("Team"));
5830 purple_request_field_choice_add(f
, _("Company"));
5831 purple_request_field_choice_add(f
, _("Public"));
5832 purple_request_field_choice_add(f
, _("Blocked")); /* index 4 */
5833 purple_request_field_choice_set_default_value(f
, 3); /* index */
5834 purple_request_field_set_required(f
, TRUE
);
5835 purple_request_field_group_add_field(g
, f
);
5837 purple_request_fields_add_group(fields
, g
);
5839 purple_request_fields(gc
, _("Add new domain"),
5840 _("Add new domain"), NULL
, fields
,
5841 _("Add"), G_CALLBACK(sipe_ask_access_domain_cb
),
5843 account
, NULL
, NULL
, gc
);
5847 sipe_buddy_menu_access_level_add_domain_cb(PurpleBuddy
*buddy
)
5849 sipe_ask_access_domain(PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE
);
5853 * Workaround for missing libpurple API to release resources allocated
5854 * during blist_node_menu() callback. See also:
5856 * <http://developer.pidgin.im/ticket/12597>
5858 * We remember all memory blocks in a list and deallocate them when
5860 * - the next time we enter the callback, or
5861 * - the account is disconnected
5863 * That means that after the buddy menu has been closed we have unused
5864 * resources but at least we don't leak them anymore...
5867 sipe_blist_menu_free_containers(struct sipe_core_private
*sipe_private
)
5869 GSList
*entry
= sipe_private
->blist_menu_containers
;
5871 free_container(entry
->data
);
5872 entry
= entry
->next
;
5874 g_slist_free(sipe_private
->blist_menu_containers
);
5875 sipe_private
->blist_menu_containers
= NULL
;
5879 sipe_blist_menu_remember_container(struct sipe_core_private
*sipe_private
,
5880 struct sipe_container
*container
)
5882 sipe_private
->blist_menu_containers
= g_slist_prepend(sipe_private
->blist_menu_containers
,
5887 sipe_get_access_levels_menu(struct sipe_core_private
*sipe_private
,
5888 const char* member_type
,
5889 const char* member_value
,
5890 const gboolean extra_menu
)
5892 GList
*menu_access_levels
= NULL
;
5895 PurpleMenuAction
*act
;
5896 struct sipe_container
*container
;
5897 struct sipe_container_member
*member
;
5898 gboolean is_group_access
= FALSE
;
5899 int container_id
= sipe_find_access_level(sipe_private
, member_type
, member_value
, &is_group_access
);
5901 for (i
= 1; i
<= CONTAINERS_LEN
; i
++) {
5902 /* to put Blocked level last in menu list.
5903 * Blocked should remaim in the first place in the containers[] array.
5905 unsigned int j
= (i
== CONTAINERS_LEN
) ? 0 : i
;
5906 const char *acc_level_name
= sipe_get_access_level_name(containers
[j
]);
5908 container
= g_new0(struct sipe_container
, 1);
5909 member
= g_new0(struct sipe_container_member
, 1);
5910 container
->id
= containers
[j
];
5911 container
->members
= g_slist_append(container
->members
, member
);
5912 member
->type
= g_strdup(member_type
);
5913 member
->value
= g_strdup(member_value
);
5915 /* libpurple memory leak workaround */
5916 sipe_blist_menu_remember_container(sipe_private
, container
);
5918 /* current container/access level */
5919 if (((int)containers
[j
]) == container_id
) {
5920 menu_name
= is_group_access
?
5921 g_strdup_printf(INDENT_MARKED_INHERITED_FMT
, acc_level_name
) :
5922 g_strdup_printf(INDENT_MARKED_FMT
, acc_level_name
);
5924 menu_name
= g_strdup_printf(INDENT_FMT
, acc_level_name
);
5927 act
= purple_menu_action_new(menu_name
,
5928 PURPLE_CALLBACK(sipe_buddy_menu_access_level_cb
),
5931 menu_access_levels
= g_list_prepend(menu_access_levels
, act
);
5934 if (extra_menu
&& (container_id
>= 0)) {
5936 act
= purple_menu_action_new(" --------------", NULL
, NULL
, NULL
);
5937 menu_access_levels
= g_list_prepend(menu_access_levels
, act
);
5939 if (!is_group_access
) {
5940 container
= g_new0(struct sipe_container
, 1);
5941 member
= g_new0(struct sipe_container_member
, 1);
5943 container
->members
= g_slist_append(container
->members
, member
);
5944 member
->type
= g_strdup(member_type
);
5945 member
->value
= g_strdup(member_value
);
5947 /* libpurple memory leak workaround */
5948 sipe_blist_menu_remember_container(sipe_private
, container
);
5950 /* Translators: remove (clear) previously assigned access level */
5951 menu_name
= g_strdup_printf(INDENT_FMT
, _("Unspecify"));
5952 act
= purple_menu_action_new(menu_name
,
5953 PURPLE_CALLBACK(sipe_buddy_menu_access_level_cb
),
5956 menu_access_levels
= g_list_prepend(menu_access_levels
, act
);
5960 menu_access_levels
= g_list_reverse(menu_access_levels
);
5961 return menu_access_levels
;
5965 sipe_get_access_groups_menu(struct sipe_core_private
*sipe_private
)
5967 GList
*menu_access_groups
= NULL
;
5968 PurpleMenuAction
*act
;
5969 GSList
*access_domains
= NULL
;
5974 act
= purple_menu_action_new(_("People in my company"),
5976 NULL
, sipe_get_access_levels_menu(sipe_private
, "sameEnterprise", NULL
, FALSE
));
5977 menu_access_groups
= g_list_prepend(menu_access_groups
, act
);
5979 /* this is original name, don't edit */
5980 act
= purple_menu_action_new(_("People in domains connected with my company"),
5982 NULL
, sipe_get_access_levels_menu(sipe_private
, "federated", NULL
, FALSE
));
5983 menu_access_groups
= g_list_prepend(menu_access_groups
, act
);
5985 act
= purple_menu_action_new(_("People in public domains"),
5987 NULL
, sipe_get_access_levels_menu(sipe_private
, "publicCloud", NULL
, TRUE
));
5988 menu_access_groups
= g_list_prepend(menu_access_groups
, act
);
5990 access_domains
= sipe_get_access_domains(sipe_private
);
5991 entry
= access_domains
;
5993 domain
= entry
->data
;
5995 menu_name
= g_strdup_printf(_("People at %s"), domain
);
5996 act
= purple_menu_action_new(menu_name
,
5998 NULL
, sipe_get_access_levels_menu(sipe_private
, "domain", g_strdup(domain
), TRUE
));
5999 menu_access_groups
= g_list_prepend(menu_access_groups
, act
);
6002 entry
= entry
->next
;
6006 /* People in domains connected with my company */
6007 act
= purple_menu_action_new("-------------------------------------------", NULL
, NULL
, NULL
);
6008 menu_access_groups
= g_list_prepend(menu_access_groups
, act
);
6010 act
= purple_menu_action_new(_("Add new domain..."),
6011 PURPLE_CALLBACK(sipe_buddy_menu_access_level_add_domain_cb
),
6013 menu_access_groups
= g_list_prepend(menu_access_groups
, act
);
6015 menu_access_groups
= g_list_reverse(menu_access_groups
);
6017 return menu_access_groups
;
6021 sipe_get_access_control_menu(struct sipe_core_private
*sipe_private
,
6024 GList
*menu_access_levels
= NULL
;
6025 GList
*menu_access_groups
= NULL
;
6027 PurpleMenuAction
*act
;
6029 /* libpurple memory leak workaround */
6030 sipe_blist_menu_free_containers(sipe_private
);
6032 menu_access_levels
= sipe_get_access_levels_menu(sipe_private
, "user", sipe_get_no_sip_uri(uri
), TRUE
);
6034 menu_access_groups
= sipe_get_access_groups_menu(sipe_private
);
6036 menu_name
= g_strdup_printf(INDENT_FMT
, _("Access groups"));
6037 act
= purple_menu_action_new(menu_name
,
6039 NULL
, menu_access_groups
);
6041 menu_access_levels
= g_list_append(menu_access_levels
, act
);
6043 menu_name
= g_strdup_printf(INDENT_FMT
, _("Online help..."));
6044 act
= purple_menu_action_new(menu_name
,
6045 PURPLE_CALLBACK(sipe_buddy_menu_access_level_help_cb
),
6048 menu_access_levels
= g_list_append(menu_access_levels
, act
);
6050 return menu_access_levels
;
6054 process_get_info_response(struct sipe_core_private
*sipe_private
,
6055 struct sipmsg
*msg
, struct transaction
*trans
)
6057 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
6058 char *uri
= trans
->payload
->data
;
6060 PurpleNotifyUserInfo
*info
;
6061 PurpleBuddy
*pbuddy
= NULL
;
6062 struct sipe_buddy
*sbuddy
;
6063 const char *alias
= NULL
;
6064 char *device_name
= NULL
;
6065 char *server_alias
= NULL
;
6066 char *phone_number
= NULL
;
6069 char *first_name
= NULL
;
6070 char *last_name
= NULL
;
6072 if (!sip
) return FALSE
;
6074 SIPE_DEBUG_INFO("Fetching %s's user info for %s", uri
, sipe_private
->username
);
6076 pbuddy
= purple_find_buddy((PurpleAccount
*)sip
->account
, uri
);
6077 alias
= purple_buddy_get_local_alias(pbuddy
);
6079 //will query buddy UA's capabilities and send answer to log
6080 sipe_options_request(sipe_private
, uri
);
6082 sbuddy
= g_hash_table_lookup(sipe_private
->buddies
, uri
);
6084 device_name
= sbuddy
->device_name
? g_strdup(sbuddy
->device_name
) : NULL
;
6087 info
= purple_notify_user_info_new();
6089 if (msg
->response
!= 200) {
6090 SIPE_DEBUG_INFO("process_get_info_response: SERVICE response is %d", msg
->response
);
6092 sipe_xml
*searchResults
;
6093 const sipe_xml
*mrow
;
6095 SIPE_DEBUG_INFO("process_get_info_response: body:\n%s", msg
->body
? msg
->body
: "");
6096 searchResults
= sipe_xml_parse(msg
->body
, msg
->bodylen
);
6097 if (!searchResults
) {
6098 SIPE_DEBUG_INFO_NOFORMAT("process_get_info_response: no parseable searchResults");
6099 } else if ((mrow
= sipe_xml_child(searchResults
, "Body/Array/row"))) {
6101 server_alias
= g_strdup(sipe_xml_attribute(mrow
, "displayName"));
6102 email
= g_strdup(sipe_xml_attribute(mrow
, "email"));
6103 phone_number
= g_strdup(sipe_xml_attribute(mrow
, "phone"));
6105 /* For 2007 system we will take this from ContactCard -
6106 * it has cleaner tel: URIs at least
6108 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
6109 char *tel_uri
= sip_to_tel_uri(phone_number
);
6110 /* trims its parameters, so call first */
6111 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_DISPLAY_NAME
, server_alias
);
6112 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_EMAIL
, email
);
6113 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_WORK_PHONE
, tel_uri
);
6114 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY
, phone_number
);
6118 if (server_alias
&& strlen(server_alias
) > 0) {
6119 purple_notify_user_info_add_pair(info
, _("Display name"), server_alias
);
6121 if ((value
= sipe_xml_attribute(mrow
, "title")) && strlen(value
) > 0) {
6122 purple_notify_user_info_add_pair(info
, _("Job title"), value
);
6124 if ((value
= sipe_xml_attribute(mrow
, "office")) && strlen(value
) > 0) {
6125 purple_notify_user_info_add_pair(info
, _("Office"), value
);
6127 if (phone_number
&& strlen(phone_number
) > 0) {
6128 purple_notify_user_info_add_pair(info
, _("Business phone"), phone_number
);
6130 if ((value
= sipe_xml_attribute(mrow
, "company")) && strlen(value
) > 0) {
6131 purple_notify_user_info_add_pair(info
, _("Company"), value
);
6133 if ((value
= sipe_xml_attribute(mrow
, "city")) && strlen(value
) > 0) {
6134 purple_notify_user_info_add_pair(info
, _("City"), value
);
6136 if ((value
= sipe_xml_attribute(mrow
, "state")) && strlen(value
) > 0) {
6137 purple_notify_user_info_add_pair(info
, _("State"), value
);
6139 if ((value
= sipe_xml_attribute(mrow
, "country")) && strlen(value
) > 0) {
6140 purple_notify_user_info_add_pair(info
, _("Country"), value
);
6142 if (email
&& strlen(email
) > 0) {
6143 purple_notify_user_info_add_pair(info
, _("Email address"), email
);
6147 sipe_xml_free(searchResults
);
6150 purple_notify_user_info_add_section_break(info
);
6152 if (is_empty(server_alias
)) {
6153 g_free(server_alias
);
6154 server_alias
= g_strdup(purple_buddy_get_server_alias(pbuddy
));
6156 purple_notify_user_info_add_pair(info
, _("Display name"), server_alias
);
6160 /* present alias if it differs from server alias */
6161 if (alias
&& !sipe_strequal(alias
, server_alias
))
6163 purple_notify_user_info_add_pair(info
, _("Alias"), alias
);
6166 if (is_empty(email
)) {
6168 email
= g_strdup(purple_blist_node_get_string(&pbuddy
->node
, EMAIL_PROP
));
6170 purple_notify_user_info_add_pair(info
, _("Email address"), email
);
6174 site
= purple_blist_node_get_string(&pbuddy
->node
, SITE_PROP
);
6176 purple_notify_user_info_add_pair(info
, _("Site"), site
);
6179 sipe_get_first_last_names(sipe_private
, uri
, &first_name
, &last_name
);
6180 if (first_name
&& last_name
) {
6181 char *link
= g_strconcat("http://www.linkedin.com/pub/dir/", first_name
, "/", last_name
, NULL
);
6183 purple_notify_user_info_add_pair(info
, _("Find on LinkedIn"), link
);
6190 purple_notify_user_info_add_pair(info
, _("Device"), device_name
);
6193 /* show a buddy's user info in a nice dialog box */
6194 purple_notify_userinfo(sip
->gc
, /* connection the buddy info came through */
6195 uri
, /* buddy's URI */
6197 NULL
, /* callback called when dialog closed */
6198 NULL
); /* userdata for callback */
6200 g_free(phone_number
);
6201 g_free(server_alias
);
6203 g_free(device_name
);
6209 * AD search first, LDAP based
6211 void sipe_get_info(PurpleConnection
*gc
, const char *username
)
6213 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
6214 gchar
*domain_uri
= sip_uri_from_name(sipe_private
->public.sip_domain
);
6215 char *row
= g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW
, "msRTCSIP-PrimaryUserAddress", username
);
6216 gchar
*body
= g_strdup_printf(SIPE_SOAP_SEARCH_CONTACT
, 1, row
);
6217 struct transaction_payload
*payload
= g_new0(struct transaction_payload
, 1);
6219 payload
->destroy
= g_free
;
6220 payload
->data
= g_strdup(username
);
6222 SIPE_DEBUG_INFO("sipe_get_contact_data: body:\n%s", body
? body
: "");
6223 send_soap_request_with_cb(sipe_private
, domain_uri
, body
,
6224 process_get_info_response
, payload
);