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-dialog.h"
79 #include "sipe-domino.h"
80 #include "sipe-groupchat.h"
82 #include "sipe-mime.h"
84 #include "sipe-schedule.h"
85 #include "sipe-session.h"
86 #include "sipe-subscriptions.h"
88 #include "sipe-media.h"
90 #include "sipe-utils.h"
95 #define SIPE_IDLE_SET_DELAY 1 /* 1 sec */
97 #define UPDATE_CALENDAR_DELAY 1*60 /* 1 min */
98 #define UPDATE_CALENDAR_INTERVAL 30*60 /* 30 min */
100 /* Status identifiers (see also: sipe_status_types()) */
101 #define SIPE_STATUS_ID_UNKNOWN purple_primitive_get_id_from_type(PURPLE_STATUS_UNSET) /* Unset (primitive) */
102 #define SIPE_STATUS_ID_OFFLINE purple_primitive_get_id_from_type(PURPLE_STATUS_OFFLINE) /* Offline (primitive) */
103 #define SIPE_STATUS_ID_AVAILABLE purple_primitive_get_id_from_type(PURPLE_STATUS_AVAILABLE) /* Online */
104 /* PURPLE_STATUS_UNAVAILABLE: */
105 #define SIPE_STATUS_ID_BUSY "busy" /* Busy */
106 #define SIPE_STATUS_ID_BUSYIDLE "busyidle" /* BusyIdle */
107 #define SIPE_STATUS_ID_DND "do-not-disturb" /* Do Not Disturb */
108 #define SIPE_STATUS_ID_IN_MEETING "in-a-meeting" /* In a meeting */
109 #define SIPE_STATUS_ID_IN_CONF "in-a-conference" /* In a conference */
110 #define SIPE_STATUS_ID_ON_PHONE "on-the-phone" /* On the phone */
111 #define SIPE_STATUS_ID_INVISIBLE purple_primitive_get_id_from_type(PURPLE_STATUS_INVISIBLE) /* Appear Offline */
112 /* PURPLE_STATUS_AWAY: */
113 #define SIPE_STATUS_ID_IDLE "idle" /* Idle/Inactive */
114 #define SIPE_STATUS_ID_BRB "be-right-back" /* Be Right Back */
115 #define SIPE_STATUS_ID_AWAY purple_primitive_get_id_from_type(PURPLE_STATUS_AWAY) /* Away (primitive) */
116 /** Reuters status (user settable) */
117 #define SIPE_STATUS_ID_LUNCH "out-to-lunch" /* Out To Lunch */
118 /* ??? PURPLE_STATUS_EXTENDED_AWAY */
119 /* ??? PURPLE_STATUS_MOBILE */
120 /* ??? PURPLE_STATUS_TUNE */
122 /* Status attributes (see also sipe_status_types() */
123 #define SIPE_STATUS_ATTR_ID_MESSAGE "message"
125 static struct sipe_activity_map_struct
130 const char *status_id
;
132 } const sipe_activity_map
[] =
134 /* This has nothing to do with Availability numbers, like 3500 (online).
135 * Just a mapping of Communicator Activities to Purple statuses to be able display them in Pidgin.
137 { SIPE_ACTIVITY_UNSET
, "unset", NULL
, NULL
},
138 { SIPE_ACTIVITY_ONLINE
, "online", NULL
, NULL
},
139 { SIPE_ACTIVITY_INACTIVE
, SIPE_STATUS_ID_IDLE
, N_("Inactive") , NULL
},
140 { SIPE_ACTIVITY_BUSY
, SIPE_STATUS_ID_BUSY
, N_("Busy") , SIPE_STATUS_ID_BUSY
},
141 { SIPE_ACTIVITY_BUSYIDLE
, SIPE_STATUS_ID_BUSYIDLE
, N_("Busy-Idle") , NULL
},
142 { SIPE_ACTIVITY_DND
, SIPE_STATUS_ID_DND
, NULL
, SIPE_STATUS_ID_DND
},
143 { SIPE_ACTIVITY_BRB
, SIPE_STATUS_ID_BRB
, N_("Be right back") , SIPE_STATUS_ID_BRB
},
144 { SIPE_ACTIVITY_AWAY
, "away", NULL
, NULL
},
145 { SIPE_ACTIVITY_LUNCH
, SIPE_STATUS_ID_LUNCH
, N_("Out to lunch") , NULL
},
146 { SIPE_ACTIVITY_OFFLINE
, "offline", NULL
, NULL
},
147 { SIPE_ACTIVITY_ON_PHONE
, SIPE_STATUS_ID_ON_PHONE
, N_("In a call") , NULL
},
148 { SIPE_ACTIVITY_IN_CONF
, SIPE_STATUS_ID_IN_CONF
, N_("In a conference") , NULL
},
149 { SIPE_ACTIVITY_IN_MEETING
, SIPE_STATUS_ID_IN_MEETING
, N_("In a meeting") , NULL
},
150 { SIPE_ACTIVITY_OOF
, "out-of-office", N_("Out of office") , NULL
},
151 { SIPE_ACTIVITY_URGENT_ONLY
, "urgent-interruptions-only", N_("Urgent interruptions only") , NULL
}
153 /** @param x is sipe_activity */
154 #define SIPE_ACTIVITY_I18N(x) gettext(sipe_activity_map[x].desc)
157 sipe_get_activity_by_token(const char *token
)
161 for (i
= 0; i
< SIPE_ACTIVITY_NUM_TYPES
; i
++)
163 if (sipe_strequal(token
, sipe_activity_map
[i
].token
))
164 return sipe_activity_map
[i
].type
;
167 return sipe_activity_map
[0].type
;
171 sipe_get_activity_desc_by_token(const char *token
)
173 if (!token
) return NULL
;
175 return SIPE_ACTIVITY_I18N(sipe_get_activity_by_token(token
));
178 static void send_presence_status(struct sipe_core_private
*sipe_private
,
182 * @param from0 from URI (with 'sip:' prefix). Will be filled with self-URI if NULL passed.
185 send_soap_request_with_cb(struct sipe_core_private
*sipe_private
,
188 TransCallback callback
,
189 struct transaction_payload
*payload
)
191 gchar
*from
= from0
? g_strdup(from0
) : sip_uri_self(sipe_private
);
192 gchar
*contact
= get_contact(sipe_private
);
193 gchar
*hdr
= g_strdup_printf("Contact: %s\r\n"
194 "Content-Type: application/SOAP+xml\r\n",contact
);
196 struct transaction
*trans
= sip_transport_service(sipe_private
,
201 trans
->payload
= payload
;
208 static void send_soap_request(struct sipe_core_private
*sipe_private
,
211 send_soap_request_with_cb(sipe_private
, NULL
, body
, NULL
, NULL
);
215 * Returns pointer to URI without sip: prefix if any
217 * @param sip_uri SIP URI possibly with sip: prefix. Example: sip:first.last@hq.company.com
218 * @return pointer to URL without sip: prefix. Coresponding example: first.last@hq.company.com
220 * Doesn't allocate memory
223 sipe_get_no_sip_uri(const char *sip_uri
)
225 const char *prefix
= "sip:";
226 if (!sip_uri
) return NULL
;
228 if (g_str_has_prefix(sip_uri
, prefix
)) {
229 return (sip_uri
+strlen(prefix
));
236 sipe_contact_set_acl (struct sipe_core_private
*sipe_private
,
240 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
241 gchar
* body
= g_strdup_printf(SIPE_SOAP_ALLOW_DENY
, who
, rights
, sip
->acl_delta
++);
242 send_soap_request(sipe_private
, body
);
247 sipe_change_access_level(struct sipe_core_private
*sipe_private
,
248 const int container_id
,
253 sipe_core_contact_allow_deny (struct sipe_core_public
*sipe_public
,
254 const gchar
* who
, gboolean allow
)
256 struct sipe_core_private
*sipe_private
= SIPE_CORE_PRIVATE
;
259 SIPE_DEBUG_INFO("Authorizing contact %s", who
);
261 SIPE_DEBUG_INFO("Blocking contact %s", who
);
264 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
265 sipe_change_access_level(sipe_private
, (allow
? -1 : 32000), "user", sipe_get_no_sip_uri(who
));
267 sipe_contact_set_acl(sipe_private
, who
, allow
? "AA" : "BD");
272 void sipe_auth_user_cb(void * data
)
274 struct sipe_auth_job
* job
= (struct sipe_auth_job
*) data
;
277 sipe_core_contact_allow_deny((struct sipe_core_public
*)job
->sipe_private
, job
->who
, TRUE
);
282 void sipe_deny_user_cb(void * data
)
284 struct sipe_auth_job
* job
= (struct sipe_auth_job
*) data
;
287 sipe_core_contact_allow_deny((struct sipe_core_public
*)job
->sipe_private
, job
->who
, FALSE
);
291 /** @applicable: 2005-
294 sipe_process_presence_wpending (struct sipe_core_private
*sipe_private
,
298 const sipe_xml
*watcher
;
299 // Ensure it's either not a response (eg it's a BENOTIFY) or that it's a 200 OK response
300 if (msg
->response
!= 0 && msg
->response
!= 200) return;
302 if (msg
->bodylen
== 0 || msg
->body
== NULL
|| sipe_strequal(sipmsg_find_header(msg
, "Event"), "msrtc.wpending")) return;
304 watchers
= sipe_xml_parse(msg
->body
, msg
->bodylen
);
305 if (!watchers
) return;
307 for (watcher
= sipe_xml_child(watchers
, "watcher"); watcher
; watcher
= sipe_xml_twin(watcher
)) {
308 gchar
* remote_user
= g_strdup(sipe_xml_attribute(watcher
, "uri"));
309 gchar
* alias
= g_strdup(sipe_xml_attribute(watcher
, "displayName"));
310 gboolean on_list
= g_hash_table_lookup(sipe_private
->buddies
, remote_user
) != NULL
;
312 // TODO pull out optional displayName to pass as alias
314 struct sipe_auth_job
* job
= g_new0(struct sipe_auth_job
, 1);
315 job
->who
= remote_user
;
316 job
->sipe_private
= sipe_private
;
317 sipe_backend_buddy_request_authorization(SIPE_CORE_PUBLIC
,
328 sipe_xml_free(watchers
);
333 sipe_group_add(struct sipe_core_private
*sipe_private
,
334 struct sipe_group
* group
)
336 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
337 if (sipe_backend_buddy_group_add(SIPE_CORE_PUBLIC
,group
->name
))
339 SIPE_DEBUG_INFO("added group %s (id %d)", group
->name
, group
->id
);
340 sip
->groups
= g_slist_append(sip
->groups
, group
);
344 SIPE_DEBUG_INFO("did not add group %s", group
->name
? group
->name
: "");
348 static struct sipe_group
*sipe_group_find_by_id(struct sipe_core_private
*sipe_private
,
351 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
352 struct sipe_group
*group
;
361 if (group
->id
== id
) {
369 static struct sipe_group
*sipe_group_find_by_name(struct sipe_core_private
*sipe_private
,
372 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
373 struct sipe_group
*group
;
382 if (sipe_strequal(group
->name
, name
)) {
391 sipe_group_rename(struct sipe_core_private
*sipe_private
,
392 struct sipe_group
*group
,
395 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
397 SIPE_DEBUG_INFO("Renaming group %s to %s", group
->name
, name
);
398 body
= g_markup_printf_escaped(SIPE_SOAP_MOD_GROUP
, group
->id
, name
, sip
->contacts_delta
++);
399 send_soap_request(sipe_private
, body
);
402 group
->name
= g_strdup(name
);
406 * Only appends if no such value already stored.
409 GSList
* slist_insert_unique_sorted(GSList
*list
, gpointer data
, GCompareFunc func
) {
411 if (!g_slist_find_custom(list
, data
, func
)) {
412 res
= g_slist_insert_sorted(list
, data
, func
);
418 sipe_group_compare(struct sipe_group
*group1
, struct sipe_group
*group2
) {
419 return group1
->id
- group2
->id
;
423 * Returns string like "2 4 7 8" - group ids buddy belong to.
426 sipe_get_buddy_groups_string (struct sipe_buddy
*buddy
) {
429 //creating array from GList, converting int to gchar*
430 gchar
**ids_arr
= g_new(gchar
*, g_slist_length(buddy
->groups
) + 1);
431 GSList
*entry
= buddy
->groups
;
433 if (!ids_arr
) return NULL
;
436 struct sipe_group
* group
= entry
->data
;
437 ids_arr
[i
] = g_strdup_printf("%d", group
->id
);
442 res
= g_strjoinv(" ", ids_arr
);
448 * Sends buddy update to server
451 sipe_core_group_set_user(struct sipe_core_public
*sipe_public
, const gchar
* who
)
453 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA
;
454 struct sipe_buddy
*buddy
= g_hash_table_lookup(SIPE_CORE_PRIVATE
->buddies
, who
);
455 sipe_backend_buddy backend_buddy
= sipe_backend_buddy_find(sipe_public
, who
, NULL
);
457 if (buddy
&& backend_buddy
) {
458 gchar
*alias
= sipe_backend_buddy_get_alias(sipe_public
, backend_buddy
);
459 gchar
*groups
= sipe_get_buddy_groups_string(buddy
);
462 SIPE_DEBUG_INFO("Saving buddy %s with alias %s and groups %s", who
, alias
, groups
);
464 body
= g_markup_printf_escaped(SIPE_SOAP_SET_CONTACT
,
465 alias
, groups
, "true", buddy
->name
, sip
->contacts_delta
++
467 send_soap_request(SIPE_CORE_PRIVATE
, body
);
475 static gboolean
process_add_group_response(struct sipe_core_private
*sipe_private
,
477 struct transaction
*trans
)
479 if (msg
->response
== 200) {
480 struct sipe_group
*group
;
481 struct group_user_context
*ctx
= trans
->payload
->data
;
483 const sipe_xml
*node
;
485 struct sipe_buddy
*buddy
;
487 xml
= sipe_xml_parse(msg
->body
, msg
->bodylen
);
492 node
= sipe_xml_child(xml
, "Body/addGroup/groupID");
498 group_id
= sipe_xml_data(node
);
504 group
= g_new0(struct sipe_group
, 1);
505 group
->id
= (int)g_ascii_strtod(group_id
, NULL
);
507 group
->name
= g_strdup(ctx
->group_name
);
509 sipe_group_add(sipe_private
, group
);
511 if (ctx
->user_name
) {
512 buddy
= g_hash_table_lookup(sipe_private
->buddies
, ctx
->user_name
);
514 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, group
, (GCompareFunc
)sipe_group_compare
);
517 sipe_core_group_set_user(SIPE_CORE_PUBLIC
, ctx
->user_name
);
526 static void sipe_group_context_destroy(gpointer data
)
528 struct group_user_context
*ctx
= data
;
529 g_free(ctx
->group_name
);
530 g_free(ctx
->user_name
);
534 static void sipe_group_create (struct sipe_core_private
*sipe_private
,
535 const gchar
*name
, const gchar
* who
)
537 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
538 struct transaction_payload
*payload
= g_new0(struct transaction_payload
, 1);
539 struct group_user_context
*ctx
= g_new0(struct group_user_context
, 1);
540 const gchar
*soap_name
= sipe_strequal(name
, _("Other Contacts")) ? "~" : name
;
542 ctx
->group_name
= g_strdup(name
);
543 ctx
->user_name
= g_strdup(who
);
544 payload
->destroy
= sipe_group_context_destroy
;
547 body
= g_markup_printf_escaped(SIPE_SOAP_ADD_GROUP
, soap_name
, sip
->contacts_delta
++);
548 send_soap_request_with_cb(sipe_private
, NULL
, body
, process_add_group_response
, payload
);
553 sipe_sched_calendar_status_update(struct sipe_core_private
*sipe_private
,
554 time_t calculate_from
);
557 sipe_get_availability_by_status(const char* sipe_status_id
, char** activity_token
);
560 sipe_get_status_by_availability(int avail
,
564 sipe_set_purple_account_status_and_note(const PurpleAccount
*account
,
565 const char *status_id
,
567 time_t do_not_publish
[]);
570 sipe_apply_calendar_status(struct sipe_core_private
*sipe_private
,
571 struct sipe_buddy
*sbuddy
,
572 const char *status_id
)
574 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
575 time_t cal_avail_since
;
576 int cal_status
= sipe_cal_get_status(sbuddy
, time(NULL
), &cal_avail_since
);
582 if (cal_status
< SIPE_CAL_NO_DATA
) {
583 SIPE_DEBUG_INFO("sipe_apply_calendar_status: cal_status : %d for %s", cal_status
, sbuddy
->name
);
584 SIPE_DEBUG_INFO("sipe_apply_calendar_status: cal_avail_since : %s", asctime(localtime(&cal_avail_since
)));
587 /* scheduled Cal update call */
589 status_id
= sbuddy
->last_non_cal_status_id
;
590 g_free(sbuddy
->activity
);
591 sbuddy
->activity
= g_strdup(sbuddy
->last_non_cal_activity
);
595 SIPE_DEBUG_INFO("sipe_apply_calendar_status: status_id is NULL for %s, exiting.",
596 sbuddy
->name
? sbuddy
->name
: "" );
600 /* adjust to calendar status */
601 if (cal_status
!= SIPE_CAL_NO_DATA
) {
602 SIPE_DEBUG_INFO("sipe_apply_calendar_status: user_avail_since: %s", asctime(localtime(&sbuddy
->user_avail_since
)));
604 if (cal_status
== SIPE_CAL_BUSY
605 && cal_avail_since
> sbuddy
->user_avail_since
606 && 6500 >= sipe_get_availability_by_status(status_id
, NULL
))
608 status_id
= SIPE_STATUS_ID_BUSY
;
609 g_free(sbuddy
->activity
);
610 sbuddy
->activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_IN_MEETING
));
612 avail
= sipe_get_availability_by_status(status_id
, NULL
);
614 SIPE_DEBUG_INFO("sipe_apply_calendar_status: activity_since : %s", asctime(localtime(&sbuddy
->activity_since
)));
615 if (cal_avail_since
> sbuddy
->activity_since
) {
616 if (cal_status
== SIPE_CAL_OOF
617 && avail
>= 15000) /* 12000 in 2007 */
619 g_free(sbuddy
->activity
);
620 sbuddy
->activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_OOF
));
625 /* then set status_id actually */
626 SIPE_DEBUG_INFO("sipe_apply_calendar_status: to %s for %s", status_id
, sbuddy
->name
? sbuddy
->name
: "" );
627 sipe_backend_buddy_set_status(SIPE_CORE_PUBLIC
, sbuddy
->name
, status_id
);
629 /* set our account state to the one in roaming (including calendar info) */
630 self_uri
= sip_uri_self(sipe_private
);
631 if (sip
->initial_state_published
&& sipe_strcase_equal(sbuddy
->name
, self_uri
)) {
632 if (sipe_strequal(status_id
, SIPE_STATUS_ID_OFFLINE
)) {
633 status_id
= g_strdup(SIPE_STATUS_ID_INVISIBLE
); /* not not let offline status switch us off */
636 SIPE_DEBUG_INFO("sipe_apply_calendar_status: switch to '%s' for the account", sip
->status
);
637 sipe_set_purple_account_status_and_note(sip
->account
, status_id
, sip
->note
, sip
->do_not_publish
);
643 sipe_core_buddy_got_status(struct sipe_core_public
*sipe_public
,
645 const gchar
*status_id
)
647 struct sipe_core_private
*sipe_private
= SIPE_CORE_PRIVATE
;
648 struct sipe_buddy
*sbuddy
= g_hash_table_lookup(sipe_private
->buddies
, uri
);
652 /* Check if on 2005 system contact's calendar,
653 * then set/preserve it.
655 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
656 sipe_apply_calendar_status(sipe_private
, sbuddy
, status_id
);
658 sipe_backend_buddy_set_status(sipe_public
, uri
, status_id
);
663 update_calendar_status_cb(SIPE_UNUSED_PARAMETER
char *name
,
664 struct sipe_buddy
*sbuddy
,
665 struct sipe_core_private
*sipe_private
)
667 sipe_apply_calendar_status(sipe_private
, sbuddy
, NULL
);
671 * Updates contact's status
672 * based on their calendar information.
674 * Applicability: 2005 systems
677 update_calendar_status(struct sipe_core_private
*sipe_private
,
678 SIPE_UNUSED_PARAMETER
void *unused
)
680 SIPE_DEBUG_INFO_NOFORMAT("update_calendar_status() started.");
681 g_hash_table_foreach(sipe_private
->buddies
, (GHFunc
)update_calendar_status_cb
, sipe_private
);
683 /* repeat scheduling */
684 sipe_sched_calendar_status_update(sipe_private
, time(NULL
) + 3*60 /* 3 min */);
688 * Schedules process of contacts' status update
689 * based on their calendar information.
690 * Should be scheduled to the beginning of every
691 * 15 min interval, like:
692 * 13:00, 13:15, 13:30, 13:45, etc.
694 * Applicability: 2005 systems
697 sipe_sched_calendar_status_update(struct sipe_core_private
*sipe_private
,
698 time_t calculate_from
)
700 int interval
= 15*60;
701 /** start of the beginning of closest 15 min interval. */
702 time_t next_start
= ((time_t)((int)((int)calculate_from
)/interval
+ 1)*interval
);
704 SIPE_DEBUG_INFO("sipe_sched_calendar_status_update: calculate_from time: %s",
705 asctime(localtime(&calculate_from
)));
706 SIPE_DEBUG_INFO("sipe_sched_calendar_status_update: next start time : %s",
707 asctime(localtime(&next_start
)));
709 sipe_schedule_seconds(sipe_private
,
710 "<+2005-cal-status>",
712 next_start
- time(NULL
),
713 update_calendar_status
,
718 * Schedules process of self status publish
719 * based on own calendar information.
720 * Should be scheduled to the beginning of every
721 * 15 min interval, like:
722 * 13:00, 13:15, 13:30, 13:45, etc.
724 * Applicability: 2007+ systems
727 sipe_sched_calendar_status_self_publish(struct sipe_core_private
*sipe_private
,
728 time_t calculate_from
)
731 /** start of the beginning of closest 5 min interval. */
732 time_t next_start
= ((time_t)((int)((int)calculate_from
)/interval
+ 1)*interval
);
734 SIPE_DEBUG_INFO("sipe_sched_calendar_status_self_publish: calculate_from time: %s",
735 asctime(localtime(&calculate_from
)));
736 SIPE_DEBUG_INFO("sipe_sched_calendar_status_self_publish: next start time : %s",
737 asctime(localtime(&next_start
)));
739 sipe_schedule_seconds(sipe_private
,
740 "<+2007-cal-status>",
742 next_start
- time(NULL
),
743 publish_calendar_status_self
,
747 static void sipe_subscribe_resource_uri(const char *name
,
748 SIPE_UNUSED_PARAMETER gpointer value
,
749 gchar
**resources_uri
)
751 gchar
*tmp
= *resources_uri
;
752 *resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"/>\n", tmp
, name
);
756 static void sipe_subscribe_resource_uri_with_context(const char *name
, gpointer value
, gchar
**resources_uri
)
758 struct sipe_buddy
*sbuddy
= (struct sipe_buddy
*)value
;
759 gchar
*context
= sbuddy
&& sbuddy
->just_added
? "><context/></resource>" : "/>";
760 gchar
*tmp
= *resources_uri
;
762 if (sbuddy
) sbuddy
->just_added
= FALSE
; /* should be enought to include context one time */
764 *resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"%s\n", tmp
, name
, context
);
769 * Support for Batch Category SUBSCRIBE [MS-PRES] - msrtc-event-categories+xml OCS 2007
770 * Support for Batch Category SUBSCRIBE [MS-SIP] - adrl+xml LCS 2005
771 * The user sends an initial batched category SUBSCRIBE request against all contacts on his roaming list in only a request
772 * A batch category SUBSCRIBE request MUST have the same To-URI and From-URI.
773 * This header will be send only if adhoclist there is a "Supported: adhoclist" in REGISTER answer else will be send a Single Category SUBSCRIBE
776 static void sipe_subscribe_presence_batched_to(struct sipe_core_private
*sipe_private
,
777 gchar
*resources_uri
,
780 gchar
*contact
= get_contact(sipe_private
);
785 gchar
*autoextend
= "";
788 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
789 require
= ", categoryList";
790 accept
= ", application/msrtc-event-categories+xml, application/xpidf+xml, application/pidf+xml";
791 content_type
= "application/msrtc-adrl-categorylist+xml";
792 content
= g_strdup_printf(
793 "<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
794 "<action name=\"subscribe\" id=\"63792024\">\n"
795 "<adhocList>\n%s</adhocList>\n"
796 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
797 "<category name=\"calendarData\"/>\n"
798 "<category name=\"contactCard\"/>\n"
799 "<category name=\"note\"/>\n"
800 "<category name=\"state\"/>\n"
803 "</batchSub>", sipe_private
->username
, resources_uri
);
805 autoextend
= "Supported: com.microsoft.autoextend\r\n";
806 content_type
= "application/adrl+xml";
807 content
= g_strdup_printf(
808 "<adhoclist xmlns=\"urn:ietf:params:xml:ns:adrl\" uri=\"sip:%s\" name=\"sip:%s\">\n"
809 "<create xmlns=\"\">\n%s</create>\n"
810 "</adhoclist>\n", sipe_private
->username
, sipe_private
->username
, resources_uri
);
812 g_free(resources_uri
);
814 request
= g_strdup_printf(
815 "Require: adhoclist%s\r\n"
816 "Supported: eventlist\r\n"
817 "Accept: application/rlmi+xml, multipart/related, text/xml+msrtc.pidf%s\r\n"
818 "Supported: ms-piggyback-first-notify\r\n"
819 "%sSupported: ms-benotify\r\n"
820 "Proxy-Require: ms-benotify\r\n"
821 "Event: presence\r\n"
822 "Content-Type: %s\r\n"
823 "Contact: %s\r\n", require
, accept
, autoextend
, content_type
, contact
);
826 sipe_subscribe_presence_buddy(sipe_private
, to
, request
, content
);
833 static void sipe_subscribe_presence_batched(struct sipe_core_private
*sipe_private
,
834 SIPE_UNUSED_PARAMETER
void *unused
)
836 gchar
*to
= sip_uri_self(sipe_private
);
837 gchar
*resources_uri
= g_strdup("");
838 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
839 g_hash_table_foreach(sipe_private
->buddies
, (GHFunc
) sipe_subscribe_resource_uri_with_context
, &resources_uri
);
841 g_hash_table_foreach(sipe_private
->buddies
, (GHFunc
) sipe_subscribe_resource_uri
, &resources_uri
);
844 sipe_subscribe_presence_batched_to(sipe_private
, resources_uri
, to
);
847 struct presence_batched_routed
{
852 static void sipe_subscribe_presence_batched_routed_free(void *payload
)
854 struct presence_batched_routed
*data
= payload
;
855 GSList
*buddies
= data
->buddies
;
857 g_free(buddies
->data
);
858 buddies
= buddies
->next
;
860 g_slist_free(data
->buddies
);
865 static void sipe_subscribe_presence_batched_routed(struct sipe_core_private
*sipe_private
,
868 struct presence_batched_routed
*data
= payload
;
869 GSList
*buddies
= data
->buddies
;
870 gchar
*resources_uri
= g_strdup("");
872 gchar
*tmp
= resources_uri
;
873 resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"/>\n", tmp
, (char *) buddies
->data
);
875 buddies
= buddies
->next
;
877 sipe_subscribe_presence_batched_to(sipe_private
, resources_uri
,
878 g_strdup(data
->host
));
882 * Single Category SUBSCRIBE [MS-PRES] ; To send when the server returns a 200 OK message with state="resubscribe" in response.
883 * The user sends a single SUBSCRIBE request to the subscribed contact.
884 * The To-URI and the URI listed in the resource list MUST be the same for a single category SUBSCRIBE request.
888 static void sipe_subscribe_presence_single(struct sipe_core_private
*sipe_private
,
891 gchar
*to
= sip_uri((char *)buddy_name
);
892 gchar
*tmp
= get_contact(sipe_private
);
894 gchar
*content
= NULL
;
895 gchar
*autoextend
= "";
896 gchar
*content_type
= "";
897 struct sipe_buddy
*sbuddy
= g_hash_table_lookup(sipe_private
->buddies
, to
);
898 gchar
*context
= sbuddy
&& sbuddy
->just_added
? "><context/></resource>" : "/>";
900 if (sbuddy
) sbuddy
->just_added
= FALSE
;
902 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
903 content_type
= "Content-Type: application/msrtc-adrl-categorylist+xml\r\n";
905 autoextend
= "Supported: com.microsoft.autoextend\r\n";
908 request
= g_strdup_printf(
909 "Accept: application/msrtc-event-categories+xml, text/xml+msrtc.pidf, application/xpidf+xml, application/pidf+xml, application/rlmi+xml, multipart/related\r\n"
910 "Supported: ms-piggyback-first-notify\r\n"
911 "%s%sSupported: ms-benotify\r\n"
912 "Proxy-Require: ms-benotify\r\n"
913 "Event: presence\r\n"
914 "Contact: %s\r\n", autoextend
, content_type
, tmp
);
916 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
917 content
= g_strdup_printf(
918 "<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
919 "<action name=\"subscribe\" id=\"63792024\"><adhocList>\n"
920 "<resource uri=\"%s\"%s\n"
922 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
923 "<category name=\"calendarData\"/>\n"
924 "<category name=\"contactCard\"/>\n"
925 "<category name=\"note\"/>\n"
926 "<category name=\"state\"/>\n"
929 "</batchSub>", sipe_private
->username
, to
, context
);
934 sipe_subscribe_presence_buddy(sipe_private
, to
, request
, content
);
941 void sipe_set_status(PurpleAccount
*account
, PurpleStatus
*status
)
943 SIPE_DEBUG_INFO("sipe_set_status: status=%s", purple_status_get_id(status
));
945 if (!purple_status_is_active(status
))
949 struct sipe_core_private
*sipe_private
= PURPLE_ACCOUNT_TO_SIPE_CORE_PRIVATE
;
950 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
955 time_t now
= time(NULL
);
956 const char *status_id
= purple_status_get_id(status
);
957 const char *note
= purple_status_get_attr_string(status
, SIPE_STATUS_ATTR_ID_MESSAGE
);
958 sipe_activity activity
= sipe_get_activity_by_token(status_id
);
959 gboolean do_not_publish
= ((now
- sip
->do_not_publish
[activity
]) <= 2);
961 /* when other point of presence clears note, but we are keeping
964 if (do_not_publish
&& !note
&& sip
->cal
&& sip
->cal
->oof_note
) {
965 SIPE_DEBUG_INFO_NOFORMAT("sipe_set_status: enabling publication as OOF note keepers.");
966 do_not_publish
= FALSE
;
969 SIPE_DEBUG_INFO("sipe_set_status: was: sip->do_not_publish[%s]=%d [?] now(time)=%d",
970 status_id
, (int)sip
->do_not_publish
[activity
], (int)now
);
972 sip
->do_not_publish
[activity
] = 0;
973 SIPE_DEBUG_INFO("sipe_set_status: set: sip->do_not_publish[%s]=%d [0]",
974 status_id
, (int)sip
->do_not_publish
[activity
]);
978 SIPE_DEBUG_INFO_NOFORMAT("sipe_set_status: publication was switched off, exiting.");
983 sip
->status
= g_strdup(status_id
);
985 /* hack to escape apostrof before comparison */
986 tmp
= note
? sipe_utils_str_replace(note
, "'", "'") : NULL
;
988 /* this will preserve OOF flag as well */
989 if (!sipe_strequal(tmp
, sip
->note
)) {
990 sip
->is_oof_note
= FALSE
;
992 sip
->note
= g_strdup(note
);
993 sip
->note_since
= time(NULL
);
997 /* schedule 2 sec to capture idle flag */
998 action_name
= g_strdup_printf("<%s>", "+set-status");
999 sipe_schedule_seconds(sipe_private
,
1002 SIPE_IDLE_SET_DELAY
,
1003 send_presence_status
,
1005 g_free(action_name
);
1011 sipe_set_idle(PurpleConnection
* gc
,
1014 SIPE_DEBUG_INFO("sipe_set_idle: interval=%d", interval
);
1017 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
1018 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
1021 sip
->idle_switch
= time(NULL
);
1022 SIPE_DEBUG_INFO("sipe_set_idle: sip->idle_switch : %s", asctime(localtime(&(sip
->idle_switch
))));
1028 sipe_group_buddy(PurpleConnection
*gc
,
1030 const char *old_group_name
,
1031 const char *new_group_name
)
1033 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
1034 struct sipe_buddy
* buddy
= g_hash_table_lookup(sipe_private
->buddies
, who
);
1035 struct sipe_group
* old_group
= NULL
;
1036 struct sipe_group
* new_group
;
1038 SIPE_DEBUG_INFO("sipe_group_buddy[CB]: who:%s old_group_name:%s new_group_name:%s",
1039 who
? who
: "", old_group_name
? old_group_name
: "", new_group_name
? new_group_name
: "");
1041 if(!buddy
) { // buddy not in roaming list
1045 if (old_group_name
) {
1046 old_group
= sipe_group_find_by_name(sipe_private
, old_group_name
);
1048 new_group
= sipe_group_find_by_name(sipe_private
, new_group_name
);
1051 buddy
->groups
= g_slist_remove(buddy
->groups
, old_group
);
1052 SIPE_DEBUG_INFO("buddy %s removed from old group %s", who
, old_group_name
);
1056 sipe_group_create(sipe_private
, new_group_name
, who
);
1058 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, new_group
, (GCompareFunc
)sipe_group_compare
);
1059 sipe_core_group_set_user(SIPE_CORE_PUBLIC
, who
);
1063 void sipe_add_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
1065 SIPE_DEBUG_INFO("sipe_add_buddy[CB]: buddy:%s group:%s", buddy
? buddy
->name
: "", group
? group
->name
: "");
1067 /* libpurple can call us with undefined buddy or group */
1068 if (buddy
&& group
) {
1069 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
1071 /* Buddy name must be lower case as we use purple_normalize_nocase() to compare */
1072 gchar
*buddy_name
= g_ascii_strdown(buddy
->name
, -1);
1073 purple_blist_rename_buddy(buddy
, buddy_name
);
1076 /* Prepend sip: if needed */
1077 if (!g_str_has_prefix(buddy
->name
, "sip:")) {
1078 gchar
*buf
= sip_uri_from_name(buddy
->name
);
1079 purple_blist_rename_buddy(buddy
, buf
);
1083 if (!g_hash_table_lookup(sipe_private
->buddies
, buddy
->name
)) {
1084 struct sipe_buddy
*b
= g_new0(struct sipe_buddy
, 1);
1085 SIPE_DEBUG_INFO("sipe_add_buddy: adding %s", buddy
->name
);
1086 b
->name
= g_strdup(buddy
->name
);
1087 b
->just_added
= TRUE
;
1088 g_hash_table_insert(sipe_private
->buddies
, b
->name
, b
);
1089 /* @TODO should go to callback */
1090 sipe_subscribe_presence_single(sipe_private
,
1093 SIPE_DEBUG_INFO("sipe_add_buddy: buddy %s already in internal list", buddy
->name
);
1096 sipe_group_buddy(gc
, buddy
->name
, NULL
, group
->name
);
1100 static void sipe_free_buddy(struct sipe_buddy
*buddy
)
1104 * We are calling g_hash_table_foreach_steal(). That means that no
1105 * key/value deallocation functions are called. Therefore the glib
1106 * hash code does not touch the key (buddy->name) or value (buddy)
1107 * of the to-be-deleted hash node at all. It follows that we
1109 * - MUST free the memory for the key ourselves and
1110 * - ARE allowed to do it in this function
1112 * Conclusion: glib must be broken on the Windows platform if sipe
1113 * crashes with SIGTRAP when closing. You'll have to live
1114 * with the memory leak until this is fixed.
1116 g_free(buddy
->name
);
1118 g_free(buddy
->activity
);
1119 g_free(buddy
->meeting_subject
);
1120 g_free(buddy
->meeting_location
);
1121 g_free(buddy
->note
);
1123 g_free(buddy
->cal_start_time
);
1124 g_free(buddy
->cal_free_busy_base64
);
1125 g_free(buddy
->cal_free_busy
);
1126 g_free(buddy
->last_non_cal_activity
);
1128 sipe_cal_free_working_hours(buddy
->cal_working_hours
);
1130 g_free(buddy
->device_name
);
1131 g_slist_free(buddy
->groups
);
1136 * Unassociates buddy from group first.
1137 * Then see if no groups left, removes buddy completely.
1138 * Otherwise updates buddy groups on server.
1140 void sipe_remove_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
1142 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
1143 struct sipe_buddy
*b
;
1144 struct sipe_group
*g
= NULL
;
1146 SIPE_DEBUG_INFO("sipe_remove_buddy[CB]: buddy:%s group:%s", buddy
? buddy
->name
: "", group
? group
->name
: "");
1149 b
= g_hash_table_lookup(sipe_private
->buddies
, buddy
->name
);
1153 g
= sipe_group_find_by_name(sipe_private
, group
->name
);
1157 b
->groups
= g_slist_remove(b
->groups
, g
);
1158 SIPE_DEBUG_INFO("buddy %s removed from group %s", buddy
->name
, g
->name
);
1161 if (g_slist_length(b
->groups
) < 1) {
1162 gchar
*action_name
= sipe_utils_presence_key(buddy
->name
);
1163 sipe_schedule_cancel(sipe_private
, action_name
);
1164 g_free(action_name
);
1166 g_hash_table_remove(sipe_private
->buddies
, buddy
->name
);
1169 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
1170 gchar
* body
= g_strdup_printf(SIPE_SOAP_DEL_CONTACT
, b
->name
, sip
->contacts_delta
++);
1171 send_soap_request(sipe_private
, body
);
1177 //updates groups on server
1178 sipe_core_group_set_user(SIPE_CORE_PUBLIC
, b
->name
);
1184 sipe_rename_group(PurpleConnection
*gc
,
1185 const char *old_name
,
1187 SIPE_UNUSED_PARAMETER GList
*moved_buddies
)
1189 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
1190 struct sipe_group
* s_group
= sipe_group_find_by_name(sipe_private
, old_name
);
1192 sipe_group_rename(sipe_private
, s_group
, group
->name
);
1194 SIPE_DEBUG_INFO("Cannot find group %s to rename", old_name
);
1199 sipe_remove_group(PurpleConnection
*gc
, PurpleGroup
*group
)
1201 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
1202 struct sipe_group
* s_group
= sipe_group_find_by_name(sipe_private
, group
->name
);
1204 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
1206 SIPE_DEBUG_INFO("Deleting group %s", group
->name
);
1207 body
= g_strdup_printf(SIPE_SOAP_DEL_GROUP
, s_group
->id
, sip
->contacts_delta
++);
1208 send_soap_request(sipe_private
, body
);
1211 sip
->groups
= g_slist_remove(sip
->groups
, s_group
);
1212 g_free(s_group
->name
);
1215 SIPE_DEBUG_INFO("Cannot find group %s to delete", group
->name
);
1220 * A callback for g_hash_table_foreach
1223 sipe_buddy_subscribe_cb(char *buddy_name
,
1224 SIPE_UNUSED_PARAMETER
struct sipe_buddy
*buddy
,
1225 struct sipe_core_private
*sipe_private
)
1227 gchar
*action_name
= sipe_utils_presence_key(buddy_name
);
1228 /* g_hash_table_size() can never return 0, otherwise this function wouldn't be called :-) */
1229 guint time_range
= (g_hash_table_size(sipe_private
->buddies
) * 1000) / 25; /* time interval for 25 requests per sec. In msec. */
1230 guint timeout
= ((guint
) rand()) / (RAND_MAX
/ time_range
) + 1; /* random period within the range but never 0! */
1232 sipe_schedule_mseconds(sipe_private
,
1234 g_strdup(buddy_name
),
1236 sipe_subscribe_presence_single
,
1238 g_free(action_name
);
1242 * Removes entries from local buddy list
1243 * that does not correspond ones in the roaming contact list.
1245 static void sipe_cleanup_local_blist(struct sipe_core_private
*sipe_private
) {
1246 GSList
*buddies
= sipe_backend_buddy_find_all(SIPE_CORE_PUBLIC
, NULL
, NULL
);
1247 GSList
*entry
= buddies
;
1248 struct sipe_buddy
*buddy
;
1249 sipe_backend_buddy b
;
1253 SIPE_DEBUG_INFO("sipe_cleanup_local_blist: overall %d backend buddies (including clones)", g_slist_length(buddies
));
1254 SIPE_DEBUG_INFO("sipe_cleanup_local_blist: %d sipe buddies (unique)", g_hash_table_size(sipe_private
->buddies
));
1257 gname
= sipe_backend_buddy_get_group_name(SIPE_CORE_PUBLIC
, b
);
1258 bname
= sipe_backend_buddy_get_name(SIPE_CORE_PUBLIC
, b
);
1259 buddy
= g_hash_table_lookup(sipe_private
->buddies
, bname
);
1262 gboolean in_sipe_groups
= FALSE
;
1263 GSList
*entry2
= buddy
->groups
;
1265 struct sipe_group
*group
= entry2
->data
;
1266 if (sipe_strequal(group
->name
, gname
)) {
1267 in_sipe_groups
= TRUE
;
1270 entry2
= entry2
->next
;
1272 if(!in_sipe_groups
) {
1273 SIPE_DEBUG_INFO("*** REMOVING %s from blist group: %s as not having this group in roaming list", bname
, gname
);
1274 sipe_backend_buddy_remove(SIPE_CORE_PUBLIC
, b
);
1277 SIPE_DEBUG_INFO("*** REMOVING %s from blist group: %s as this buddy not in roaming list", bname
, gname
);
1278 sipe_backend_buddy_remove(SIPE_CORE_PUBLIC
, b
);
1281 entry
= entry
->next
;
1283 g_slist_free(buddies
);
1287 sipe_find_access_level(struct sipe_core_private
*sipe_private
,
1290 gboolean
*is_group_access
);
1293 sipe_refresh_blocked_status_cb(char *buddy_name
,
1294 SIPE_UNUSED_PARAMETER
struct sipe_buddy
*buddy
,
1295 struct sipe_core_private
*sipe_private
)
1297 int container_id
= sipe_find_access_level(sipe_private
, "user", buddy_name
, NULL
);
1298 gboolean blocked
= (container_id
== 32000);
1299 gboolean blocked_in_blist
= sipe_backend_buddy_is_blocked(SIPE_CORE_PUBLIC
, buddy_name
);
1301 /* SIPE_DEBUG_INFO("sipe_refresh_blocked_status_cb: buddy_name=%s, blocked=%s, blocked_in_blist=%s",
1302 buddy_name, blocked ? "T" : "F", blocked_in_blist ? "T" : "F"); */
1304 if (blocked
!= blocked_in_blist
) {
1305 sipe_backend_buddy_set_blocked_status(SIPE_CORE_PUBLIC
, buddy_name
, blocked
);
1310 sipe_refresh_blocked_status(struct sipe_core_private
*sipe_private
)
1312 g_hash_table_foreach(sipe_private
->buddies
,
1313 (GHFunc
) sipe_refresh_blocked_status_cb
,
1317 static gboolean
sipe_process_roaming_contacts(struct sipe_core_private
*sipe_private
,
1320 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
1321 int len
= msg
->bodylen
;
1323 const gchar
*tmp
= sipmsg_find_header(msg
, "Event");
1324 const sipe_xml
*item
;
1326 const gchar
*contacts_delta
;
1327 const sipe_xml
*group_node
;
1328 if (!g_str_has_prefix(tmp
, "vnd-microsoft-roaming-contacts")) {
1332 /* Convert the contact from XML to backend Buddies */
1333 isc
= sipe_xml_parse(msg
->body
, len
);
1338 contacts_delta
= sipe_xml_attribute(isc
, "deltaNum");
1339 if (contacts_delta
) {
1340 sip
->contacts_delta
= (int)g_ascii_strtod(contacts_delta
, NULL
);
1343 if (sipe_strequal(sipe_xml_name(isc
), "contactList")) {
1346 for (group_node
= sipe_xml_child(isc
, "group"); group_node
; group_node
= sipe_xml_twin(group_node
)) {
1347 struct sipe_group
* group
= g_new0(struct sipe_group
, 1);
1348 const char *name
= sipe_xml_attribute(group_node
, "name");
1350 if (g_str_has_prefix(name
, "~")) {
1351 name
= _("Other Contacts");
1353 group
->name
= g_strdup(name
);
1354 group
->id
= (int)g_ascii_strtod(sipe_xml_attribute(group_node
, "id"), NULL
);
1356 sipe_group_add(sipe_private
, group
);
1359 // Make sure we have at least one group
1360 if (g_slist_length(sip
->groups
) == 0) {
1361 sipe_group_create(sipe_private
, _("Other Contacts"), NULL
);
1364 /* Parse contacts */
1365 for (item
= sipe_xml_child(isc
, "contact"); item
; item
= sipe_xml_twin(item
)) {
1366 const gchar
*uri
= sipe_xml_attribute(item
, "uri");
1367 const gchar
*name
= sipe_xml_attribute(item
, "name");
1369 struct sipe_buddy
*buddy
= NULL
;
1371 gchar
**item_groups
;
1374 /* Buddy name must be lower case as we use purple_normalize_nocase() to compare */
1375 tmp
= sip_uri_from_name(uri
);
1376 buddy_name
= g_ascii_strdown(tmp
, -1);
1379 /* assign to group Other Contacts if nothing else received */
1380 tmp
= g_strdup(sipe_xml_attribute(item
, "groups"));
1382 struct sipe_group
*group
= sipe_group_find_by_name(sipe_private
, _("Other Contacts"));
1384 tmp
= group
? g_strdup_printf("%d", group
->id
) : g_strdup("1");
1386 item_groups
= g_strsplit(tmp
, " ", 0);
1389 while (item_groups
[i
]) {
1390 struct sipe_group
*group
= sipe_group_find_by_id(sipe_private
, g_ascii_strtod(item_groups
[i
], NULL
));
1392 // If couldn't find the right group for this contact, just put them in the first group we have
1393 if (group
== NULL
&& g_slist_length(sip
->groups
) > 0) {
1394 group
= sip
->groups
->data
;
1397 if (group
!= NULL
) {
1399 sipe_backend_buddy b
= sipe_backend_buddy_find(SIPE_CORE_PUBLIC
, buddy_name
, group
->name
);
1401 b
= sipe_backend_buddy_add(SIPE_CORE_PUBLIC
, buddy_name
, uri
, group
->name
);
1402 SIPE_DEBUG_INFO("Created new buddy %s with alias %s", buddy_name
, uri
);
1405 b_alias
= sipe_backend_buddy_get_alias(SIPE_CORE_PUBLIC
, b
);
1406 if (sipe_strcase_equal(uri
, b_alias
)) {
1407 if (name
!= NULL
&& strlen(name
) != 0) {
1408 sipe_backend_buddy_set_alias(SIPE_CORE_PUBLIC
, b
, name
);
1410 SIPE_DEBUG_INFO("Replaced buddy %s alias with %s", buddy_name
, name
);
1416 buddy
= g_new0(struct sipe_buddy
, 1);
1417 buddy
->name
= sipe_backend_buddy_get_name(SIPE_CORE_PUBLIC
, b
);
1418 g_hash_table_insert(sipe_private
->buddies
, buddy
->name
, buddy
);
1420 SIPE_DEBUG_INFO("Added SIPE buddy %s", buddy
->name
);
1423 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, group
, (GCompareFunc
)sipe_group_compare
);
1425 SIPE_DEBUG_INFO("Added buddy %s to group %s", buddy
->name
, group
->name
);
1427 SIPE_DEBUG_INFO("No group found for contact %s! Unable to add to buddy list",
1432 } // while, contact groups
1433 g_strfreev(item_groups
);
1438 sipe_cleanup_local_blist(sipe_private
);
1440 /* Add self-contact if not there yet. 2005 systems. */
1441 /* This will resemble subscription to roaming_self in 2007 systems */
1442 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
1443 gchar
*self_uri
= sip_uri_self(sipe_private
);
1444 struct sipe_buddy
*buddy
= g_hash_table_lookup(sipe_private
->buddies
, self_uri
);
1447 buddy
= g_new0(struct sipe_buddy
, 1);
1448 buddy
->name
= g_strdup(self_uri
);
1449 g_hash_table_insert(sipe_private
->buddies
, buddy
->name
, buddy
);
1456 /* subscribe to buddies */
1457 if (!sip
->subscribed_buddies
) { //do it once, then count Expire field to schedule resubscribe.
1458 if (sip
->batched_support
) {
1459 sipe_subscribe_presence_batched(sipe_private
, NULL
);
1461 g_hash_table_foreach(sipe_private
->buddies
,
1462 (GHFunc
)sipe_buddy_subscribe_cb
,
1465 sip
->subscribed_buddies
= TRUE
;
1467 /* for 2005 systems schedule contacts' status update
1468 * based on their calendar information
1470 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
1471 sipe_sched_calendar_status_update(sipe_private
, time(NULL
));
1478 * Fires on deregistration event initiated by server.
1479 * [MS-SIPREGE] SIP extension.
1484 // Content-Type: text/registration-event
1485 // subscription-state: terminated;expires=0
1486 // ms-diagnostics-public: 4141;reason="User disabled"
1488 // deregistered;event=rejected
1490 static void sipe_process_registration_notify(struct sipe_core_private
*sipe_private
,
1493 const gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
1494 gchar
*event
= NULL
;
1495 gchar
*reason
= NULL
;
1496 const gchar
*diagnostics
= sipmsg_find_header(msg
, "ms-diagnostics");
1499 diagnostics
= diagnostics
? diagnostics
: sipmsg_find_header(msg
, "ms-diagnostics-public");
1500 SIPE_DEBUG_INFO_NOFORMAT("sipe_process_registration_notify: deregistration received.");
1502 if (!g_ascii_strncasecmp(contenttype
, "text/registration-event", 23)) {
1503 event
= sipmsg_find_part_of_header(msg
->body
, "event=", NULL
, NULL
);
1504 //@TODO have proper parameter extraction _by_name_ func, case insesitive.
1505 event
= event
? event
: sipmsg_find_part_of_header(msg
->body
, "event=", ";", NULL
);
1507 SIPE_DEBUG_INFO_NOFORMAT("sipe_process_registration_notify: unknown content type, exiting.");
1511 if (diagnostics
!= NULL
) {
1512 reason
= sipmsg_find_part_of_header(diagnostics
, "reason=\"", "\"", NULL
);
1513 } else { // for LCS2005
1514 if (event
&& sipe_strcase_equal(event
, "unregistered")) {
1515 //reason = g_strdup(_("User logged out")); // [MS-OCER]
1516 reason
= g_strdup(_("you are already signed in at another location"));
1517 } else if (event
&& sipe_strcase_equal(event
, "rejected")) {
1518 reason
= g_strdup(_("user disabled")); // [MS-OCER]
1519 } else if (event
&& sipe_strcase_equal(event
, "deactivated")) {
1520 reason
= g_strdup(_("user moved")); // [MS-OCER]
1524 warning
= g_strdup_printf(_("You have been rejected by the server: %s"), reason
? reason
: _("no reason given"));
1527 sipe_backend_connection_error(SIPE_CORE_PUBLIC
,
1528 SIPE_CONNECTION_ERROR_INVALID_USERNAME
,
1534 static void sipe_process_provisioning_v2(struct sipe_core_private
*sipe_private
,
1537 sipe_xml
*xn_provision_group_list
;
1538 const sipe_xml
*node
;
1540 xn_provision_group_list
= sipe_xml_parse(msg
->body
, msg
->bodylen
);
1542 /* provisionGroup */
1543 for (node
= sipe_xml_child(xn_provision_group_list
, "provisionGroup"); node
; node
= sipe_xml_twin(node
)) {
1544 if (sipe_strequal("ServerConfiguration", sipe_xml_attribute(node
, "name"))) {
1545 g_free(sipe_private
->focus_factory_uri
);
1546 sipe_private
->focus_factory_uri
= sipe_xml_data(sipe_xml_child(node
, "focusFactoryUri"));
1547 SIPE_DEBUG_INFO("sipe_process_provisioning_v2: sipe_private->focus_factory_uri=%s",
1548 sipe_private
->focus_factory_uri
? sipe_private
->focus_factory_uri
: "");
1551 g_free(sipe_private
->mras_uri
);
1552 sipe_private
->mras_uri
= g_strstrip(sipe_xml_data(sipe_xml_child(node
, "mrasUri")));
1553 SIPE_DEBUG_INFO("sipe_process_provisioning_v2: sipe_private->mras_uri=%s",
1554 sipe_private
->mras_uri
? sipe_private
->mras_uri
: "");
1556 if (sipe_private
->mras_uri
)
1557 sipe_media_get_av_edge_credentials(sipe_private
);
1562 sipe_xml_free(xn_provision_group_list
);
1565 /** for 2005 system */
1567 sipe_process_provisioning(struct sipe_core_private
*sipe_private
,
1570 sipe_xml
*xn_provision
;
1571 const sipe_xml
*node
;
1573 xn_provision
= sipe_xml_parse(msg
->body
, msg
->bodylen
);
1574 if ((node
= sipe_xml_child(xn_provision
, "user"))) {
1575 SIPE_DEBUG_INFO("sipe_process_provisioning: uri=%s", sipe_xml_attribute(node
, "uri"));
1576 if ((node
= sipe_xml_child(node
, "line"))) {
1577 const gchar
*line_uri
= sipe_xml_attribute(node
, "uri");
1578 const gchar
*server
= sipe_xml_attribute(node
, "server");
1579 SIPE_DEBUG_INFO("sipe_process_provisioning: line_uri=%s server=%s", line_uri
, server
);
1580 sip_csta_open(sipe_private
, line_uri
, server
);
1583 sipe_xml_free(xn_provision
);
1586 static void sipe_process_roaming_acl(struct sipe_core_private
*sipe_private
,
1589 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
1590 const gchar
*contacts_delta
;
1593 xml
= sipe_xml_parse(msg
->body
, msg
->bodylen
);
1599 contacts_delta
= sipe_xml_attribute(xml
, "deltaNum");
1602 sip
->acl_delta
= (int)g_ascii_strtod(contacts_delta
, NULL
);
1608 /** MS-PRES container */
1609 struct sipe_container
{
1614 /** MS-PRES container member */
1615 struct sipe_container_member
{
1616 /** user, domain, sameEnterprise, federated, publicCloud; everyone */
1622 free_container_member(struct sipe_container_member
*member
)
1624 if (!member
) return;
1626 g_free(member
->type
);
1627 g_free(member
->value
);
1632 free_container(struct sipe_container
*container
)
1636 if (!container
) return;
1638 entry
= container
->members
;
1640 void *data
= entry
->data
;
1641 entry
= g_slist_remove(entry
, data
);
1642 free_container_member((struct sipe_container_member
*)data
);
1648 sipe_send_container_members_prepare(const guint container_id
,
1649 const guint container_version
,
1650 const gchar
*action
,
1653 char **container_xmls
)
1655 gchar
*value_str
= value
? g_strdup_printf(" value=\"%s\"", value
) : g_strdup("");
1658 if (!container_xmls
) return;
1660 body
= g_strdup_printf(
1661 "<container id=\"%d\" version=\"%d\"><member action=\"%s\" type=\"%s\"%s/></container>",
1669 if ((*container_xmls
) == NULL
) {
1670 *container_xmls
= body
;
1672 char *tmp
= *container_xmls
;
1674 *container_xmls
= g_strconcat(*container_xmls
, body
, NULL
);
1681 sipe_send_set_container_members(struct sipe_core_private
*sipe_private
,
1682 char *container_xmls
)
1689 if (!container_xmls
) return;
1691 self
= sip_uri_self(sipe_private
);
1692 body
= g_strdup_printf(
1693 "<setContainerMembers xmlns=\"http://schemas.microsoft.com/2006/09/sip/container-management\">"
1695 "</setContainerMembers>",
1698 contact
= get_contact(sipe_private
);
1699 hdr
= g_strdup_printf("Contact: %s\r\n"
1700 "Content-Type: application/msrtc-setcontainermembers+xml\r\n", contact
);
1703 sip_transport_service(sipe_private
,
1715 * Finds locally stored MS-PRES container member
1717 static struct sipe_container_member
*
1718 sipe_find_container_member(struct sipe_container
*container
,
1722 struct sipe_container_member
*member
;
1725 if (container
== NULL
|| type
== NULL
) {
1729 entry
= container
->members
;
1731 member
= entry
->data
;
1732 if (sipe_strcase_equal(member
->type
, type
) &&
1733 sipe_strcase_equal(member
->value
, value
))
1737 entry
= entry
->next
;
1743 * Finds locally stored MS-PRES container by id
1745 static struct sipe_container
*
1746 sipe_find_container(struct sipe_core_private
*sipe_private
,
1749 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
1750 struct sipe_container
*container
;
1757 entry
= sip
->containers
;
1759 container
= entry
->data
;
1760 if (id
== container
->id
) {
1763 entry
= entry
->next
;
1769 sipe_get_access_domains(struct sipe_core_private
*sipe_private
)
1771 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
1772 struct sipe_container
*container
;
1773 struct sipe_container_member
*member
;
1778 if (!sip
) return NULL
;
1780 entry
= sip
->containers
;
1782 container
= entry
->data
;
1784 entry2
= container
->members
;
1786 member
= entry2
->data
;
1787 if (sipe_strcase_equal(member
->type
, "domain"))
1789 res
= slist_insert_unique_sorted(res
, g_strdup(member
->value
), (GCompareFunc
)g_ascii_strcasecmp
);
1791 entry2
= entry2
->next
;
1793 entry
= entry
->next
;
1799 * Returns pointer to domain part in provided Email URL
1801 * @param email an email URL. Example: first.last@hq.company.com
1802 * @return pointer to domain part of email URL. Coresponding example: hq.company.com
1804 * Doesn't allocate memory
1807 sipe_get_domain(const char *email
)
1811 if (!email
) return NULL
;
1813 tmp
= strstr(email
, "@");
1815 if (tmp
&& ((tmp
+1) < (email
+ strlen(email
)))) {
1823 /* @TODO: replace with binary search for faster access? */
1824 /** source: http://support.microsoft.com/kb/897567 */
1825 static const char * const public_domains
[] = {
1826 "aol.com", "icq.com", "love.com", "mac.com", "br.live.com",
1827 "hotmail.co.il", "hotmail.co.jp", "hotmail.co.th", "hotmail.co.uk",
1828 "hotmail.com", "hotmail.com.ar", "hotmail.com.tr", "hotmail.es",
1829 "hotmail.de", "hotmail.fr", "hotmail.it", "live.at", "live.be",
1830 "live.ca", "live.cl", "live.cn", "live.co.in", "live.co.kr",
1831 "live.co.uk", "live.co.za", "live.com", "live.com.ar", "live.com.au",
1832 "live.com.co", "live.com.mx", "live.com.my", "live.com.pe",
1833 "live.com.ph", "live.com.pk", "live.com.pt", "live.com.sg",
1834 "live.com.ve", "live.de", "live.dk", "live.fr", "live.hk", "live.ie",
1835 "live.in", "live.it", "live.jp", "live.nl", "live.no", "live.ph",
1836 "live.ru", "live.se", "livemail.com.br", "livemail.tw",
1837 "messengeruser.com", "msn.com", "passport.com", "sympatico.ca",
1838 "tw.live.com", "webtv.net", "windowslive.com", "windowslive.es",
1843 sipe_is_public_domain(const char *domain
)
1846 while (public_domains
[i
]) {
1847 if (sipe_strcase_equal(public_domains
[i
], domain
)) {
1864 sipe_get_access_level_name(int container_id
)
1866 switch(container_id
) {
1867 case 32000: return _("Blocked");
1868 case 400: return _("Personal");
1869 case 300: return _("Team");
1870 case 200: return _("Company");
1871 case 100: return _("Public");
1873 return _("Unknown");
1876 static const guint containers
[] = {32000, 400, 300, 200, 100};
1877 #define CONTAINERS_LEN (sizeof(containers) / sizeof(guint))
1881 sipe_find_member_access_level(struct sipe_core_private
*sipe_private
,
1886 const gchar
*value_mod
= value
;
1888 if (!type
) return -1;
1890 if (sipe_strequal("user", type
)) {
1891 value_mod
= sipe_get_no_sip_uri(value
);
1894 for (i
= 0; i
< CONTAINERS_LEN
; i
++) {
1895 struct sipe_container_member
*member
;
1896 struct sipe_container
*container
= sipe_find_container(sipe_private
, containers
[i
]);
1897 if (!container
) continue;
1899 member
= sipe_find_container_member(container
, type
, value_mod
);
1900 if (member
) return containers
[i
];
1906 /** Member type: user, domain, sameEnterprise, federated, publicCloud; everyone */
1908 sipe_find_access_level(struct sipe_core_private
*sipe_private
,
1911 gboolean
*is_group_access
)
1913 int container_id
= -1;
1915 if (sipe_strequal("user", type
)) {
1917 const char *no_sip_uri
= sipe_get_no_sip_uri(value
);
1919 container_id
= sipe_find_member_access_level(sipe_private
, "user", no_sip_uri
);
1920 if (container_id
>= 0) {
1921 if (is_group_access
) *is_group_access
= FALSE
;
1922 return container_id
;
1925 domain
= sipe_get_domain(no_sip_uri
);
1926 container_id
= sipe_find_member_access_level(sipe_private
, "domain", domain
);
1927 if (container_id
>= 0) {
1928 if (is_group_access
) *is_group_access
= TRUE
;
1929 return container_id
;
1932 container_id
= sipe_find_member_access_level(sipe_private
, "sameEnterprise", NULL
);
1933 if ((container_id
>= 0) && sipe_strcase_equal(sipe_private
->public.sip_domain
, domain
)) {
1934 if (is_group_access
) *is_group_access
= TRUE
;
1935 return container_id
;
1938 container_id
= sipe_find_member_access_level(sipe_private
, "publicCloud", NULL
);
1939 if ((container_id
>= 0) && sipe_is_public_domain(domain
)) {
1940 if (is_group_access
) *is_group_access
= TRUE
;
1941 return container_id
;
1944 container_id
= sipe_find_member_access_level(sipe_private
, "everyone", NULL
);
1945 if ((container_id
>= 0)) {
1946 if (is_group_access
) *is_group_access
= TRUE
;
1947 return container_id
;
1950 container_id
= sipe_find_member_access_level(sipe_private
, type
, value
);
1951 if (is_group_access
) *is_group_access
= FALSE
;
1954 return container_id
;
1958 * @param container_id a new access level. If -1 then current access level
1959 * is just removed (I.e. the member is removed from all containers).
1960 * @param type a type of member. E.g. "user", "sameEnterprise", etc.
1961 * @param value a value for member. E.g. SIP URI for "user" member type.
1964 sipe_change_access_level(struct sipe_core_private
*sipe_private
,
1965 const int container_id
,
1970 int current_container_id
= -1;
1971 char *container_xmls
= NULL
;
1973 /* for each container: find/delete */
1974 for (i
= 0; i
< CONTAINERS_LEN
; i
++) {
1975 struct sipe_container_member
*member
;
1976 struct sipe_container
*container
= sipe_find_container(sipe_private
, containers
[i
]);
1978 if (!container
) continue;
1980 member
= sipe_find_container_member(container
, type
, value
);
1982 current_container_id
= containers
[i
];
1983 /* delete/publish current access level */
1984 if (container_id
< 0 || container_id
!= current_container_id
) {
1985 sipe_send_container_members_prepare(current_container_id
, container
->version
, "remove", type
, value
, &container_xmls
);
1986 /* remove member from our cache, to be able to recalculate AL below */
1987 container
->members
= g_slist_remove(container
->members
, member
);
1988 current_container_id
= -1;
1993 /* recalculate AL below */
1994 current_container_id
= sipe_find_access_level(sipe_private
, type
, value
, NULL
);
1996 /* assign/publish new access level */
1997 if (container_id
!= current_container_id
&& container_id
>= 0) {
1998 struct sipe_container
*container
= sipe_find_container(sipe_private
, container_id
);
1999 guint version
= container
? container
->version
: 0;
2001 sipe_send_container_members_prepare(container_id
, version
, "add", type
, value
, &container_xmls
);
2004 if (container_xmls
) {
2005 sipe_send_set_container_members(sipe_private
, container_xmls
);
2007 g_free(container_xmls
);
2011 free_publication(struct sipe_publication
*publication
)
2013 g_free(publication
->category
);
2014 g_free(publication
->cal_event_hash
);
2015 g_free(publication
->note
);
2017 g_free(publication
->working_hours_xml_str
);
2018 g_free(publication
->fb_start_str
);
2019 g_free(publication
->free_busy_base64
);
2021 g_free(publication
);
2024 /* key is <category><instance><container> */
2026 sipe_is_our_publication(struct sipe_core_private
*sipe_private
,
2029 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
2032 /* filling keys for our publications if not yet cached */
2033 if (!sip
->our_publication_keys
) {
2034 guint device_instance
= sipe_get_pub_instance(sipe_private
, SIPE_PUB_DEVICE
);
2035 guint machine_instance
= sipe_get_pub_instance(sipe_private
, SIPE_PUB_STATE_MACHINE
);
2036 guint user_instance
= sipe_get_pub_instance(sipe_private
, SIPE_PUB_STATE_USER
);
2037 guint calendar_instance
= sipe_get_pub_instance(sipe_private
, SIPE_PUB_STATE_CALENDAR
);
2038 guint cal_oof_instance
= sipe_get_pub_instance(sipe_private
, SIPE_PUB_STATE_CALENDAR_OOF
);
2039 guint cal_data_instance
= sipe_get_pub_instance(sipe_private
, SIPE_PUB_CALENDAR_DATA
);
2040 guint note_oof_instance
= sipe_get_pub_instance(sipe_private
, SIPE_PUB_NOTE_OOF
);
2042 SIPE_DEBUG_INFO_NOFORMAT("* Our Publication Instances *");
2043 SIPE_DEBUG_INFO("\tDevice : %u\t0x%08X", device_instance
, device_instance
);
2044 SIPE_DEBUG_INFO("\tMachine State : %u\t0x%08X", machine_instance
, machine_instance
);
2045 SIPE_DEBUG_INFO("\tUser Stare : %u\t0x%08X", user_instance
, user_instance
);
2046 SIPE_DEBUG_INFO("\tCalendar State : %u\t0x%08X", calendar_instance
, calendar_instance
);
2047 SIPE_DEBUG_INFO("\tCalendar OOF State : %u\t0x%08X", cal_oof_instance
, cal_oof_instance
);
2048 SIPE_DEBUG_INFO("\tCalendar FreeBusy : %u\t0x%08X", cal_data_instance
, cal_data_instance
);
2049 SIPE_DEBUG_INFO("\tOOF Note : %u\t0x%08X", note_oof_instance
, note_oof_instance
);
2050 SIPE_DEBUG_INFO("\tNote : %u", 0);
2051 SIPE_DEBUG_INFO("\tCalendar WorkingHours: %u", 0);
2054 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2055 g_strdup_printf("<%s><%u><%u>", "device", device_instance
, 2));
2057 /* state:machineState */
2058 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2059 g_strdup_printf("<%s><%u><%u>", "state", machine_instance
, 2));
2060 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2061 g_strdup_printf("<%s><%u><%u>", "state", machine_instance
, 3));
2063 /* state:userState */
2064 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2065 g_strdup_printf("<%s><%u><%u>", "state", user_instance
, 2));
2066 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2067 g_strdup_printf("<%s><%u><%u>", "state", user_instance
, 3));
2069 /* state:calendarState */
2070 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2071 g_strdup_printf("<%s><%u><%u>", "state", calendar_instance
, 2));
2072 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2073 g_strdup_printf("<%s><%u><%u>", "state", calendar_instance
, 3));
2075 /* state:calendarState OOF */
2076 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2077 g_strdup_printf("<%s><%u><%u>", "state", cal_oof_instance
, 2));
2078 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2079 g_strdup_printf("<%s><%u><%u>", "state", cal_oof_instance
, 3));
2082 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2083 g_strdup_printf("<%s><%u><%u>", "note", 0, 200));
2084 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2085 g_strdup_printf("<%s><%u><%u>", "note", 0, 300));
2086 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2087 g_strdup_printf("<%s><%u><%u>", "note", 0, 400));
2090 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2091 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance
, 200));
2092 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2093 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance
, 300));
2094 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2095 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance
, 400));
2097 /* calendarData:WorkingHours */
2098 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2099 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 1));
2100 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2101 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 100));
2102 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2103 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 200));
2104 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2105 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 300));
2106 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2107 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 400));
2108 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2109 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 32000));
2111 /* calendarData:FreeBusy */
2112 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2113 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 1));
2114 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2115 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 100));
2116 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2117 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 200));
2118 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2119 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 300));
2120 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2121 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 400));
2122 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2123 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 32000));
2125 //SIPE_DEBUG_INFO("sipe_is_our_publication: sip->our_publication_keys length=%d",
2126 // sip->our_publication_keys ? (int) g_slist_length(sip->our_publication_keys) : -1);
2129 //SIPE_DEBUG_INFO("sipe_is_our_publication: key=%s", key);
2131 entry
= sip
->our_publication_keys
;
2133 //SIPE_DEBUG_INFO(" sipe_is_our_publication: entry->data=%s", entry->data);
2134 if (sipe_strequal(entry
->data
, key
)) {
2137 entry
= entry
->next
;
2142 /** Property names to store in blist.xml */
2143 #define ALIAS_PROP "alias"
2144 #define EMAIL_PROP "email"
2145 #define PHONE_PROP "phone"
2146 #define PHONE_DISPLAY_PROP "phone-display"
2147 #define PHONE_MOBILE_PROP "phone-mobile"
2148 #define PHONE_MOBILE_DISPLAY_PROP "phone-mobile-display"
2149 #define PHONE_HOME_PROP "phone-home"
2150 #define PHONE_HOME_DISPLAY_PROP "phone-home-display"
2151 #define PHONE_OTHER_PROP "phone-other"
2152 #define PHONE_OTHER_DISPLAY_PROP "phone-other-display"
2153 #define PHONE_CUSTOM1_PROP "phone-custom1"
2154 #define PHONE_CUSTOM1_DISPLAY_PROP "phone-custom1-display"
2155 #define SITE_PROP "site"
2156 #define COMPANY_PROP "company"
2157 #define DEPARTMENT_PROP "department"
2158 #define TITLE_PROP "title"
2159 #define OFFICE_PROP "office"
2160 /** implies work address */
2161 #define ADDRESS_STREET_PROP "address-street"
2162 #define ADDRESS_CITY_PROP "address-city"
2163 #define ADDRESS_STATE_PROP "address-state"
2164 #define ADDRESS_ZIPCODE_PROP "address-zipcode"
2165 #define ADDRESS_COUNTRYCODE_PROP "address-country-code"
2168 * Tries to figure out user first and last name
2169 * based on Display Name and email properties.
2171 * Allocates memory - must be g_free()'d
2173 * Examples to parse:
2175 * First Last - Company Name
2178 * Last, First (C)(STP) (Company)
2179 * first.last@company.com (preprocessed as "first last")
2180 * first.last.company.com@reuters.net (preprocessed as "first last company com")
2182 * Unusable examples:
2183 * user@company.com (preprocessed as "user")
2184 * first.m.last@company.com (preprocessed as "first m last")
2185 * user.company.com@reuters.net (preprocessed as "user company com")
2188 sipe_get_first_last_names(struct sipe_core_private
*sipe_private
,
2193 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
2194 sipe_backend_buddy p_buddy
;
2197 const char *first
, *last
;
2200 gboolean has_comma
= FALSE
;
2202 if (!sip
|| !uri
) return;
2204 p_buddy
= sipe_backend_buddy_find(SIPE_CORE_PUBLIC
, uri
, NULL
);
2206 if (!p_buddy
) return;
2208 display_name
= sipe_backend_buddy_get_alias(SIPE_CORE_PUBLIC
, p_buddy
);
2209 email
= sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC
, p_buddy
, SIPE_BUDDY_INFO_EMAIL
);
2211 if (!display_name
&& !email
) return;
2213 /* if no display name, make "first last anything_else" out of email */
2214 if (email
&& !display_name
) {
2215 display_name
= g_strndup(email
, strstr(email
, "@") - email
);
2216 display_name
= sipe_utils_str_replace((tmp
= display_name
), ".", " ");
2221 has_comma
= (strstr(display_name
, ",") != NULL
);
2222 display_name
= sipe_utils_str_replace((tmp
= display_name
), ", ", " ");
2224 display_name
= sipe_utils_str_replace((tmp
= display_name
), ",", " ");
2228 parts
= g_strsplit(display_name
, " ", 0);
2230 if (!parts
[0] || !parts
[1]) {
2232 g_free(display_name
);
2246 *first_name
= g_strstrip(g_strdup(first
));
2250 *last_name
= g_strstrip(g_strdup(last
));
2254 g_free(display_name
);
2259 * Update user information
2261 * @param uri buddy SIP URI with 'sip:' prefix whose info we want to change.
2262 * @param property_name
2263 * @param property_value may be modified to strip white space
2266 sipe_update_user_info(struct sipe_core_private
*sipe_private
,
2268 sipe_buddy_info_fields propkey
,
2269 char *property_value
)
2271 GSList
*buddies
, *entry
;
2274 property_value
= g_strstrip(property_value
);
2276 entry
= buddies
= sipe_backend_buddy_find_all(SIPE_CORE_PUBLIC
, uri
, NULL
); /* all buddies in different groups */
2279 gchar
*server_alias
;
2281 sipe_backend_buddy p_buddy
= entry
->data
;
2283 /* for Display Name */
2284 if (propkey
== SIPE_BUDDY_INFO_DISPLAY_NAME
) {
2285 alias
= sipe_backend_buddy_get_alias(SIPE_CORE_PUBLIC
, p_buddy
);
2286 if (property_value
&& sipe_is_bad_alias(uri
, alias
)) {
2287 SIPE_DEBUG_INFO("Replacing alias for %s with %s", uri
, property_value
);
2288 sipe_backend_buddy_set_alias(SIPE_CORE_PUBLIC
, p_buddy
, property_value
);
2292 server_alias
= sipe_backend_buddy_get_server_alias(SIPE_CORE_PUBLIC
, p_buddy
);
2293 if (!is_empty(property_value
) &&
2294 (!sipe_strequal(property_value
, server_alias
) || is_empty(server_alias
)) )
2296 SIPE_DEBUG_INFO("Replacing service alias for %s with %s", uri
, property_value
);
2297 sipe_backend_buddy_set_server_alias(SIPE_CORE_PUBLIC
, p_buddy
, property_value
);
2299 g_free(server_alias
);
2301 /* for other properties */
2303 if (!is_empty(property_value
)) {
2304 prop_str
= sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC
, p_buddy
, propkey
);
2305 if (!prop_str
|| !sipe_strcase_equal(prop_str
, property_value
)) {
2306 sipe_backend_buddy_set_string(SIPE_CORE_PUBLIC
, p_buddy
, propkey
, property_value
);
2312 entry
= entry
->next
;
2314 g_slist_free(buddies
);
2319 * Suitable for both 2005 and 2007 systems.
2321 * @param uri buddy SIP URI with 'sip:' prefix whose info we want to change.
2323 * @param phone may be modified to strip white space
2324 * @param phone_display_string may be modified to strip white space
2327 sipe_update_user_phone(struct sipe_core_private
*sipe_private
,
2329 const gchar
*phone_type
,
2331 gchar
*phone_display_string
)
2333 sipe_buddy_info_fields phone_node
= SIPE_BUDDY_INFO_WORK_PHONE
; /* work phone by default */
2334 sipe_buddy_info_fields phone_display_node
= SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY
; /* work phone by default */
2336 if(!phone
|| strlen(phone
) == 0) return;
2338 if ((sipe_strequal(phone_type
, "mobile") || sipe_strequal(phone_type
, "cell"))) {
2339 phone_node
= SIPE_BUDDY_INFO_MOBILE_PHONE
;
2340 phone_display_node
= SIPE_BUDDY_INFO_MOBILE_PHONE_DISPLAY
;
2341 } else if (sipe_strequal(phone_type
, "home")) {
2342 phone_node
= SIPE_BUDDY_INFO_HOME_PHONE
;
2343 phone_display_node
= SIPE_BUDDY_INFO_HOME_PHONE_DISPLAY
;
2344 } else if (sipe_strequal(phone_type
, "other")) {
2345 phone_node
= SIPE_BUDDY_INFO_OTHER_PHONE
;
2346 phone_display_node
= SIPE_BUDDY_INFO_OTHER_PHONE_DISPLAY
;
2347 } else if (sipe_strequal(phone_type
, "custom1")) {
2348 phone_node
= SIPE_BUDDY_INFO_CUSTOM1_PHONE
;
2349 phone_display_node
= SIPE_BUDDY_INFO_CUSTOM1_PHONE_DISPLAY
;
2352 sipe_update_user_info(sipe_private
, uri
, phone_node
, phone
);
2353 if (phone_display_string
) {
2354 sipe_update_user_info(sipe_private
, uri
, phone_display_node
, phone_display_string
);
2359 sipe_core_update_calendar(struct sipe_core_public
*sipe_public
)
2361 SIPE_DEBUG_INFO_NOFORMAT("sipe_core_update_calendar: started.");
2364 * If failed, the branch will be disabled for subsequent calls.
2365 * Can't rely that user turned the functionality on in account settings.
2367 sipe_ews_update_calendar(SIPE_CORE_PRIVATE
);
2368 sipe_domino_update_calendar(SIPE_CORE_PRIVATE
);
2370 /* schedule repeat */
2371 sipe_schedule_seconds(SIPE_CORE_PRIVATE
,
2372 "<+update-calendar>",
2374 UPDATE_CALENDAR_INTERVAL
,
2375 (sipe_schedule_action
)sipe_core_update_calendar
,
2378 SIPE_DEBUG_INFO_NOFORMAT("sipe_core_update_calendar: finished.");
2382 * This method motivates Purple's Host (e.g. Pidgin) to update its UI
2383 * by using standard Purple's means of signals and saved statuses.
2385 * Thus all UI elements get updated: Status Button with Note, docklet.
2386 * This is ablolutely important as both our status and note can come
2387 * inbound (roaming) or be updated programmatically (e.g. based on our
2391 sipe_set_purple_account_status_and_note(const PurpleAccount
*account
,
2392 const char *status_id
,
2393 const char *message
,
2394 time_t do_not_publish
[])
2396 PurpleStatus
*status
= purple_account_get_active_status(account
);
2397 gboolean changed
= TRUE
;
2399 if (g_str_equal(status_id
, purple_status_get_id(status
)) &&
2400 sipe_strequal(message
, purple_status_get_attr_string(status
, SIPE_STATUS_ATTR_ID_MESSAGE
)))
2405 if (purple_savedstatus_is_idleaway()) {
2410 PurpleSavedStatus
*saved_status
;
2411 const PurpleStatusType
*acct_status_type
=
2412 purple_status_type_find_with_id(account
->status_types
, status_id
);
2413 PurpleStatusPrimitive primitive
= purple_status_type_get_primitive(acct_status_type
);
2414 sipe_activity activity
= sipe_get_activity_by_token(status_id
);
2416 saved_status
= purple_savedstatus_find_transient_by_type_and_message(primitive
, message
);
2418 purple_savedstatus_set_substatus(saved_status
, account
, acct_status_type
, message
);
2421 /* If this type+message is unique then create a new transient saved status
2422 * Ref: gtkstatusbox.c
2424 if (!saved_status
) {
2426 GList
*active_accts
= purple_accounts_get_all_active();
2428 saved_status
= purple_savedstatus_new(NULL
, primitive
);
2429 purple_savedstatus_set_message(saved_status
, message
);
2431 for (tmp
= active_accts
; tmp
!= NULL
; tmp
= tmp
->next
) {
2432 purple_savedstatus_set_substatus(saved_status
,
2433 (PurpleAccount
*)tmp
->data
, acct_status_type
, message
);
2435 g_list_free(active_accts
);
2438 do_not_publish
[activity
] = time(NULL
);
2439 SIPE_DEBUG_INFO("sipe_set_purple_account_status_and_note: do_not_publish[%s]=%d [now]",
2440 status_id
, (int)do_not_publish
[activity
]);
2442 /* Set the status for each account */
2443 purple_savedstatus_activate(saved_status
);
2447 struct hash_table_delete_payload
{
2448 GHashTable
*hash_table
;
2453 sipe_remove_category_container_publications_cb(const char *name
,
2454 struct sipe_publication
*publication
,
2455 struct hash_table_delete_payload
*payload
)
2457 if (publication
->container
== payload
->container
) {
2458 g_hash_table_remove(payload
->hash_table
, name
);
2462 sipe_remove_category_container_publications(GHashTable
*our_publications
,
2463 const char *category
,
2466 struct hash_table_delete_payload payload
;
2467 payload
.hash_table
= g_hash_table_lookup(our_publications
, category
);
2469 if (!payload
.hash_table
) return;
2471 payload
.container
= container
;
2472 g_hash_table_foreach(payload
.hash_table
, (GHFunc
)sipe_remove_category_container_publications_cb
, &payload
);
2476 send_publish_category_initial(struct sipe_core_private
*sipe_private
);
2479 * When we receive some self (BE) NOTIFY with a new subscriber
2480 * we sends a setSubscribers request to him [SIP-PRES] 4.8
2483 static void sipe_process_roaming_self(struct sipe_core_private
*sipe_private
,
2486 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
2490 const sipe_xml
*node
;
2491 const sipe_xml
*node2
;
2492 char *display_name
= NULL
;
2494 GSList
*category_names
= NULL
;
2495 int aggreg_avail
= 0;
2496 gboolean do_update_status
= FALSE
;
2497 gboolean has_note_cleaned
= FALSE
;
2499 SIPE_DEBUG_INFO_NOFORMAT("sipe_process_roaming_self");
2501 xml
= sipe_xml_parse(msg
->body
, msg
->bodylen
);
2504 contact
= get_contact(sipe_private
);
2505 to
= sip_uri_self(sipe_private
);
2509 /* set list of categories participating in this XML */
2510 for (node
= sipe_xml_child(xml
, "categories/category"); node
; node
= sipe_xml_twin(node
)) {
2511 const gchar
*name
= sipe_xml_attribute(node
, "name");
2512 category_names
= slist_insert_unique_sorted(category_names
, (gchar
*)name
, (GCompareFunc
)strcmp
);
2514 SIPE_DEBUG_INFO("sipe_process_roaming_self: category_names length=%d",
2515 category_names
? (int) g_slist_length(category_names
) : -1);
2516 /* drop category information */
2517 if (category_names
) {
2518 GSList
*entry
= category_names
;
2520 GHashTable
*cat_publications
;
2521 const gchar
*category
= entry
->data
;
2522 entry
= entry
->next
;
2523 SIPE_DEBUG_INFO("sipe_process_roaming_self: dropping category: %s", category
);
2524 cat_publications
= g_hash_table_lookup(sip
->our_publications
, category
);
2525 if (cat_publications
) {
2526 g_hash_table_remove(sip
->our_publications
, category
);
2527 SIPE_DEBUG_INFO("sipe_process_roaming_self: dropped category: %s", category
);
2531 g_slist_free(category_names
);
2532 /* filling our categories reflected in roaming data */
2533 for (node
= sipe_xml_child(xml
, "categories/category"); node
; node
= sipe_xml_twin(node
)) {
2535 const gchar
*name
= sipe_xml_attribute(node
, "name");
2536 guint container
= sipe_xml_int_attribute(node
, "container", -1);
2537 guint instance
= sipe_xml_int_attribute(node
, "instance", -1);
2538 guint version
= sipe_xml_int_attribute(node
, "version", 0);
2539 time_t publish_time
= (tmp
= sipe_xml_attribute(node
, "publishTime")) ?
2540 sipe_utils_str_to_time(tmp
) : 0;
2542 GHashTable
*cat_publications
= g_hash_table_lookup(sip
->our_publications
, name
);
2544 /* Ex. clear note: <category name="note"/> */
2545 if (container
== (guint
)-1) {
2548 do_update_status
= TRUE
;
2552 /* Ex. clear note: <category name="note" container="200"/> */
2553 if (instance
== (guint
)-1) {
2554 if (container
== 200) {
2557 do_update_status
= TRUE
;
2559 SIPE_DEBUG_INFO("sipe_process_roaming_self: removing publications for: %s/%u", name
, container
);
2560 sipe_remove_category_container_publications(
2561 sip
->our_publications
, name
, container
);
2565 /* key is <category><instance><container> */
2566 key
= g_strdup_printf("<%s><%u><%u>", name
, instance
, container
);
2567 SIPE_DEBUG_INFO("sipe_process_roaming_self: key=%s version=%d", key
, version
);
2569 /* capture all userState publication for later clean up if required */
2570 if (sipe_strequal(name
, "state") && (container
== 2 || container
== 3)) {
2571 const sipe_xml
*xn_state
= sipe_xml_child(node
, "state");
2573 if (xn_state
&& sipe_strequal(sipe_xml_attribute(xn_state
, "type"), "userState")) {
2574 struct sipe_publication
*publication
= g_new0(struct sipe_publication
, 1);
2575 publication
->category
= g_strdup(name
);
2576 publication
->instance
= instance
;
2577 publication
->container
= container
;
2578 publication
->version
= version
;
2580 if (!sip
->user_state_publications
) {
2581 sip
->user_state_publications
= g_hash_table_new_full(
2582 g_str_hash
, g_str_equal
,
2583 g_free
, (GDestroyNotify
)free_publication
);
2585 g_hash_table_insert(sip
->user_state_publications
, g_strdup(key
), publication
);
2586 SIPE_DEBUG_INFO("sipe_process_roaming_self: added to user_state_publications key=%s version=%d",
2591 if (sipe_is_our_publication(sipe_private
, key
)) {
2592 struct sipe_publication
*publication
= g_new0(struct sipe_publication
, 1);
2594 publication
->category
= g_strdup(name
);
2595 publication
->instance
= instance
;
2596 publication
->container
= container
;
2597 publication
->version
= version
;
2599 /* filling publication->availability */
2600 if (sipe_strequal(name
, "state")) {
2601 const sipe_xml
*xn_state
= sipe_xml_child(node
, "state");
2602 const sipe_xml
*xn_avail
= sipe_xml_child(xn_state
, "availability");
2605 gchar
*avail_str
= sipe_xml_data(xn_avail
);
2607 publication
->availability
= atoi(avail_str
);
2611 /* for calendarState */
2612 if (xn_state
&& sipe_strequal(sipe_xml_attribute(xn_state
, "type"), "calendarState")) {
2613 const sipe_xml
*xn_activity
= sipe_xml_child(xn_state
, "activity");
2614 struct sipe_cal_event
*event
= g_new0(struct sipe_cal_event
, 1);
2616 event
->start_time
= sipe_utils_str_to_time(sipe_xml_attribute(xn_state
, "startTime"));
2618 if (sipe_strequal(sipe_xml_attribute(xn_activity
, "token"),
2619 sipe_activity_map
[SIPE_ACTIVITY_IN_MEETING
].token
))
2621 event
->is_meeting
= TRUE
;
2624 event
->subject
= sipe_xml_data(sipe_xml_child(xn_state
, "meetingSubject"));
2625 event
->location
= sipe_xml_data(sipe_xml_child(xn_state
, "meetingLocation"));
2627 publication
->cal_event_hash
= sipe_cal_event_hash(event
);
2628 SIPE_DEBUG_INFO("sipe_process_roaming_self: hash=%s",
2629 publication
->cal_event_hash
);
2630 sipe_cal_event_free(event
);
2633 /* filling publication->note */
2634 if (sipe_strequal(name
, "note")) {
2635 const sipe_xml
*xn_body
= sipe_xml_child(node
, "note/body");
2637 if (!has_note_cleaned
) {
2638 has_note_cleaned
= TRUE
;
2642 sip
->note_since
= publish_time
;
2644 do_update_status
= TRUE
;
2647 g_free(publication
->note
);
2648 publication
->note
= NULL
;
2652 publication
->note
= g_markup_escape_text((tmp
= sipe_xml_data(xn_body
)), -1);
2654 if (publish_time
>= sip
->note_since
) {
2656 sip
->note
= g_strdup(publication
->note
);
2657 sip
->note_since
= publish_time
;
2658 sip
->is_oof_note
= sipe_strequal(sipe_xml_attribute(xn_body
, "type"), "OOF");
2660 do_update_status
= TRUE
;
2665 /* filling publication->fb_start_str, free_busy_base64, working_hours_xml_str */
2666 if (sipe_strequal(name
, "calendarData") && (publication
->container
== 300)) {
2667 const sipe_xml
*xn_free_busy
= sipe_xml_child(node
, "calendarData/freeBusy");
2668 const sipe_xml
*xn_working_hours
= sipe_xml_child(node
, "calendarData/WorkingHours");
2670 publication
->fb_start_str
= g_strdup(sipe_xml_attribute(xn_free_busy
, "startTime"));
2671 publication
->free_busy_base64
= sipe_xml_data(xn_free_busy
);
2673 if (xn_working_hours
) {
2674 publication
->working_hours_xml_str
= sipe_xml_stringify(xn_working_hours
);
2678 if (!cat_publications
) {
2679 cat_publications
= g_hash_table_new_full(
2680 g_str_hash
, g_str_equal
,
2681 g_free
, (GDestroyNotify
)free_publication
);
2682 g_hash_table_insert(sip
->our_publications
, g_strdup(name
), cat_publications
);
2683 SIPE_DEBUG_INFO("sipe_process_roaming_self: added GHashTable cat=%s", name
);
2685 g_hash_table_insert(cat_publications
, g_strdup(key
), publication
);
2686 SIPE_DEBUG_INFO("sipe_process_roaming_self: added key=%s version=%d", key
, version
);
2690 /* aggregateState (not an our publication) from 2-nd container */
2691 if (sipe_strequal(name
, "state") && container
== 2) {
2692 const sipe_xml
*xn_state
= sipe_xml_child(node
, "state");
2694 if (xn_state
&& sipe_strequal(sipe_xml_attribute(xn_state
, "type"), "aggregateState")) {
2695 const sipe_xml
*xn_avail
= sipe_xml_child(xn_state
, "availability");
2698 gchar
*avail_str
= sipe_xml_data(xn_avail
);
2700 aggreg_avail
= atoi(avail_str
);
2705 do_update_status
= TRUE
;
2709 /* userProperties published by server from AD */
2710 if (!sip
->csta
&& sipe_strequal(name
, "userProperties")) {
2711 const sipe_xml
*line
;
2712 /* line, for Remote Call Control (RCC) */
2713 for (line
= sipe_xml_child(node
, "userProperties/lines/line"); line
; line
= sipe_xml_twin(line
)) {
2714 const gchar
*line_server
= sipe_xml_attribute(line
, "lineServer");
2715 const gchar
*line_type
= sipe_xml_attribute(line
, "lineType");
2718 if (!line_server
|| !(sipe_strequal(line_type
, "Rcc") || sipe_strequal(line_type
, "Dual"))) continue;
2720 line_uri
= sipe_xml_data(line
);
2722 SIPE_DEBUG_INFO("sipe_process_roaming_self: line_uri=%s server=%s", line_uri
, line_server
);
2723 sip_csta_open(sipe_private
, line_uri
, line_server
);
2731 SIPE_DEBUG_INFO("sipe_process_roaming_self: sip->our_publications size=%d",
2732 sip
->our_publications
? (int) g_hash_table_size(sip
->our_publications
) : -1);
2735 for (node
= sipe_xml_child(xml
, "containers/container"); node
; node
= sipe_xml_twin(node
)) {
2736 guint id
= sipe_xml_int_attribute(node
, "id", 0);
2737 struct sipe_container
*container
= sipe_find_container(sipe_private
, id
);
2740 sip
->containers
= g_slist_remove(sip
->containers
, container
);
2741 SIPE_DEBUG_INFO("sipe_process_roaming_self: removed existing container id=%d v%d", container
->id
, container
->version
);
2742 free_container(container
);
2744 container
= g_new0(struct sipe_container
, 1);
2746 container
->version
= sipe_xml_int_attribute(node
, "version", 0);
2747 sip
->containers
= g_slist_append(sip
->containers
, container
);
2748 SIPE_DEBUG_INFO("sipe_process_roaming_self: added container id=%d v%d", container
->id
, container
->version
);
2750 for (node2
= sipe_xml_child(node
, "member"); node2
; node2
= sipe_xml_twin(node2
)) {
2751 struct sipe_container_member
*member
= g_new0(struct sipe_container_member
, 1);
2752 member
->type
= g_strdup(sipe_xml_attribute(node2
, "type"));
2753 member
->value
= g_strdup(sipe_xml_attribute(node2
, "value"));
2754 container
->members
= g_slist_append(container
->members
, member
);
2755 SIPE_DEBUG_INFO("sipe_process_roaming_self: added container member type=%s value=%s",
2756 member
->type
, member
->value
? member
->value
: "");
2760 SIPE_DEBUG_INFO("sipe_process_roaming_self: sip->access_level_set=%s", sip
->access_level_set
? "TRUE" : "FALSE");
2761 if (!sip
->access_level_set
&& sipe_xml_child(xml
, "containers")) {
2762 char *container_xmls
= NULL
;
2763 int sameEnterpriseAL
= sipe_find_access_level(sipe_private
, "sameEnterprise", NULL
, NULL
);
2764 int federatedAL
= sipe_find_access_level(sipe_private
, "federated", NULL
, NULL
);
2766 SIPE_DEBUG_INFO("sipe_process_roaming_self: sameEnterpriseAL=%d", sameEnterpriseAL
);
2767 SIPE_DEBUG_INFO("sipe_process_roaming_self: federatedAL=%d", federatedAL
);
2768 /* initial set-up to let counterparties see your status */
2769 if (sameEnterpriseAL
< 0) {
2770 struct sipe_container
*container
= sipe_find_container(sipe_private
, 200);
2771 guint version
= container
? container
->version
: 0;
2772 sipe_send_container_members_prepare(200, version
, "add", "sameEnterprise", NULL
, &container_xmls
);
2774 if (federatedAL
< 0) {
2775 struct sipe_container
*container
= sipe_find_container(sipe_private
, 100);
2776 guint version
= container
? container
->version
: 0;
2777 sipe_send_container_members_prepare(100, version
, "add", "federated", NULL
, &container_xmls
);
2779 sip
->access_level_set
= TRUE
;
2781 if (container_xmls
) {
2782 sipe_send_set_container_members(sipe_private
, container_xmls
);
2784 g_free(container_xmls
);
2787 /* Refresh contacts' blocked status */
2788 sipe_refresh_blocked_status(sipe_private
);
2791 for (node
= sipe_xml_child(xml
, "subscribers/subscriber"); node
; node
= sipe_xml_twin(node
)) {
2793 const char *acknowledged
;
2797 user
= sipe_xml_attribute(node
, "user"); /* without 'sip:' prefix */
2798 if (!user
) continue;
2799 SIPE_DEBUG_INFO("sipe_process_roaming_self: user %s", user
);
2800 display_name
= g_strdup(sipe_xml_attribute(node
, "displayName"));
2801 uri
= sip_uri_from_name(user
);
2803 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_DISPLAY_NAME
, display_name
);
2805 acknowledged
= sipe_xml_attribute(node
, "acknowledged");
2806 if(sipe_strcase_equal(acknowledged
,"false")){
2807 SIPE_DEBUG_INFO("sipe_process_roaming_self: user added you %s", user
);
2808 if (!sipe_backend_buddy_find(SIPE_CORE_PUBLIC
, uri
, NULL
)) {
2809 sipe_backend_buddy_request_add(SIPE_CORE_PUBLIC
, uri
, display_name
);
2812 hdr
= g_strdup_printf(
2814 "Content-Type: application/msrtc-presence-setsubscriber+xml\r\n", contact
);
2816 body
= g_strdup_printf(
2817 "<setSubscribers xmlns=\"http://schemas.microsoft.com/2006/09/sip/presence-subscribers\">"
2818 "<subscriber user=\"%s\" acknowledged=\"true\"/>"
2819 "</setSubscribers>", user
);
2821 sip_transport_service(sipe_private
,
2829 g_free(display_name
);
2836 /* Publish initial state if not yet.
2837 * Assuming this happens on initial responce to subscription to roaming-self
2838 * so we've already updated our roaming data in full.
2841 if (!sip
->initial_state_published
) {
2842 send_publish_category_initial(sipe_private
);
2843 sipe_groupchat_init(sipe_private
);
2844 sip
->initial_state_published
= TRUE
;
2846 sipe_schedule_seconds(sipe_private
,
2847 "<+update-calendar>",
2849 UPDATE_CALENDAR_DELAY
,
2850 (sipe_schedule_action
)sipe_core_update_calendar
,
2852 do_update_status
= FALSE
;
2853 } else if (aggreg_avail
) {
2855 g_free(sip
->status
);
2856 if (aggreg_avail
&& aggreg_avail
< 18000) { /* not offline */
2857 sip
->status
= g_strdup(sipe_get_status_by_availability(aggreg_avail
, NULL
));
2859 sip
->status
= g_strdup(SIPE_STATUS_ID_INVISIBLE
); /* not not let offline status switch us off */
2863 if (do_update_status
) {
2864 SIPE_DEBUG_INFO("sipe_process_roaming_self: switch to '%s' for the account", sip
->status
);
2865 sipe_set_purple_account_status_and_note(sip
->account
, sip
->status
, sip
->note
, sip
->do_not_publish
);
2871 /* IM Session (INVITE and MESSAGE methods) */
2874 process_options_response(SIPE_UNUSED_PARAMETER
struct sipe_core_private
*sipe_private
,
2876 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
2878 gboolean ret
= TRUE
;
2880 if (msg
->response
!= 200) {
2881 SIPE_DEBUG_INFO("process_options_response: OPTIONS response is %d", msg
->response
);
2885 SIPE_DEBUG_INFO("process_options_response: body:\n%s", msg
->body
? msg
->body
: "");
2891 * Asks UA/proxy about its capabilities.
2893 static void sipe_options_request(struct sipe_core_private
*sipe_private
,
2896 gchar
*to
= sip_uri(who
);
2897 gchar
*contact
= get_contact(sipe_private
);
2898 gchar
*request
= g_strdup_printf(
2899 "Accept: application/sdp\r\n"
2900 "Contact: %s\r\n", contact
);
2903 sip_transport_request(sipe_private
,
2910 process_options_response
);
2917 sipe_convo_closed(PurpleConnection
* gc
, const char *who
)
2919 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
2921 SIPE_DEBUG_INFO("conversation with %s closed", who
);
2922 sipe_session_close(sipe_private
,
2923 sipe_session_find_im(sipe_private
, who
));
2927 * Returns 2005-style activity and Availability.
2929 * @param status Sipe statis id.
2932 sipe_get_act_avail_by_status_2005(const char *status
,
2936 int avail
= 300; /* online */
2937 int act
= 400; /* Available */
2939 if (sipe_strequal(status
, SIPE_STATUS_ID_AWAY
)) {
2941 //} else if (sipe_strequal(status, SIPE_STATUS_ID_LUNCH)) {
2943 } else if (sipe_strequal(status
, SIPE_STATUS_ID_BRB
)) {
2945 } else if (sipe_strequal(status
, SIPE_STATUS_ID_AVAILABLE
)) {
2947 //} else if (sipe_strequal(status, SIPE_STATUS_ID_ON_PHONE)) {
2949 } else if (sipe_strequal(status
, SIPE_STATUS_ID_BUSY
) ||
2950 sipe_strequal(status
, SIPE_STATUS_ID_DND
)) {
2952 } else if (sipe_strequal(status
, SIPE_STATUS_ID_INVISIBLE
) ||
2953 sipe_strequal(status
, SIPE_STATUS_ID_OFFLINE
)) {
2954 avail
= 0; /* offline */
2957 act
= 400; /* Available */
2960 if (activity
) *activity
= act
;
2961 if (availability
) *availability
= avail
;
2967 * @param activity 2005 aggregated activity. Ex.: 600
2968 * @param availablity 2005 aggregated availablity. Ex.: 300
2971 sipe_get_status_by_act_avail_2005(const int activity
,
2972 const int availablity
,
2973 char **activity_desc
)
2975 const char *status_id
= NULL
;
2976 const char *act
= NULL
;
2978 if (activity
< 150) {
2979 status_id
= SIPE_STATUS_ID_AWAY
;
2980 } else if (activity
< 200) {
2981 //status_id = SIPE_STATUS_ID_LUNCH;
2982 status_id
= SIPE_STATUS_ID_AWAY
;
2983 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_LUNCH
);
2984 } else if (activity
< 300) {
2985 //status_id = SIPE_STATUS_ID_IDLE;
2986 status_id
= SIPE_STATUS_ID_AWAY
;
2987 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_INACTIVE
);
2988 } else if (activity
< 400) {
2989 status_id
= SIPE_STATUS_ID_BRB
;
2990 } else if (activity
< 500) {
2991 status_id
= SIPE_STATUS_ID_AVAILABLE
;
2992 } else if (activity
< 600) {
2993 //status_id = SIPE_STATUS_ID_ON_PHONE;
2994 status_id
= SIPE_STATUS_ID_BUSY
;
2995 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_ON_PHONE
);
2996 } else if (activity
< 700) {
2997 status_id
= SIPE_STATUS_ID_BUSY
;
2998 } else if (activity
< 800) {
2999 status_id
= SIPE_STATUS_ID_AWAY
;
3001 status_id
= SIPE_STATUS_ID_AVAILABLE
;
3004 if (availablity
< 100)
3005 status_id
= SIPE_STATUS_ID_OFFLINE
;
3007 if (activity_desc
&& act
) {
3008 g_free(*activity_desc
);
3009 *activity_desc
= g_strdup(act
);
3016 * [MS-PRES] Table 3: Conversion of legacyInterop elements and attributes to MSRTC elements and attributes.
3019 sipe_get_status_by_availability(int avail
,
3020 char** activity_desc
)
3023 const char *act
= NULL
;
3026 status
= SIPE_STATUS_ID_OFFLINE
;
3027 } else if (avail
< 4500) {
3028 status
= SIPE_STATUS_ID_AVAILABLE
;
3029 } else if (avail
< 6000) {
3030 //status = SIPE_STATUS_ID_IDLE;
3031 status
= SIPE_STATUS_ID_AVAILABLE
;
3032 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_INACTIVE
);
3033 } else if (avail
< 7500) {
3034 status
= SIPE_STATUS_ID_BUSY
;
3035 } else if (avail
< 9000) {
3036 //status = SIPE_STATUS_ID_BUSYIDLE;
3037 status
= SIPE_STATUS_ID_BUSY
;
3038 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_BUSYIDLE
);
3039 } else if (avail
< 12000) {
3040 status
= SIPE_STATUS_ID_DND
;
3041 } else if (avail
< 15000) {
3042 status
= SIPE_STATUS_ID_BRB
;
3043 } else if (avail
< 18000) {
3044 status
= SIPE_STATUS_ID_AWAY
;
3046 status
= SIPE_STATUS_ID_OFFLINE
;
3049 if (activity_desc
&& act
) {
3050 g_free(*activity_desc
);
3051 *activity_desc
= g_strdup(act
);
3058 * Returns 2007-style availability value
3060 * @param sipe_status_id (in)
3061 * @param activity_token (out) Must be g_free()'d after use if consumed.
3064 sipe_get_availability_by_status(const char* sipe_status_id
, char** activity_token
)
3067 sipe_activity activity
= SIPE_ACTIVITY_UNSET
;
3069 if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_AWAY
)) {
3070 availability
= 15500;
3071 if (!activity_token
|| !(*activity_token
)) {
3072 activity
= SIPE_ACTIVITY_AWAY
;
3074 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_BRB
)) {
3075 availability
= 12500;
3076 activity
= SIPE_ACTIVITY_BRB
;
3077 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_DND
)) {
3078 availability
= 9500;
3079 activity
= SIPE_ACTIVITY_DND
;
3080 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_BUSY
)) {
3081 availability
= 6500;
3082 if (!activity_token
|| !(*activity_token
)) {
3083 activity
= SIPE_ACTIVITY_BUSY
;
3085 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_AVAILABLE
)) {
3086 availability
= 3500;
3087 activity
= SIPE_ACTIVITY_ONLINE
;
3088 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_UNKNOWN
)) {
3091 // Offline or invisible
3092 availability
= 18500;
3093 activity
= SIPE_ACTIVITY_OFFLINE
;
3096 if (activity_token
) {
3097 *activity_token
= g_strdup(sipe_activity_map
[activity
].token
);
3099 return availability
;
3102 static void process_incoming_notify_rlmi(struct sipe_core_private
*sipe_private
,
3103 const gchar
*data
, unsigned len
)
3105 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
3107 sipe_xml
*xn_categories
;
3108 const sipe_xml
*xn_category
;
3109 const char *status
= NULL
;
3110 gboolean do_update_status
= FALSE
;
3111 gboolean has_note_cleaned
= FALSE
;
3112 gboolean has_free_busy_cleaned
= FALSE
;
3114 xn_categories
= sipe_xml_parse(data
, len
);
3115 uri
= sipe_xml_attribute(xn_categories
, "uri"); /* with 'sip:' prefix */
3117 for (xn_category
= sipe_xml_child(xn_categories
, "category");
3119 xn_category
= sipe_xml_twin(xn_category
) )
3121 const sipe_xml
*xn_node
;
3123 const char *attrVar
= sipe_xml_attribute(xn_category
, "name");
3124 time_t publish_time
= (tmp
= sipe_xml_attribute(xn_category
, "publishTime")) ?
3125 sipe_utils_str_to_time(tmp
) : 0;
3128 if (sipe_strequal(attrVar
, "contactCard"))
3130 const sipe_xml
*card
= sipe_xml_child(xn_category
, "contactCard");
3133 const sipe_xml
*node
;
3134 /* identity - Display Name and email */
3135 node
= sipe_xml_child(card
, "identity");
3137 char* display_name
= sipe_xml_data(
3138 sipe_xml_child(node
, "name/displayName"));
3139 char* email
= sipe_xml_data(
3140 sipe_xml_child(node
, "email"));
3142 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_DISPLAY_NAME
, display_name
);
3143 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_EMAIL
, email
);
3145 g_free(display_name
);
3149 node
= sipe_xml_child(card
, "company");
3151 char* company
= sipe_xml_data(node
);
3152 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_COMPANY
, company
);
3156 node
= sipe_xml_child(card
, "department");
3158 char* department
= sipe_xml_data(node
);
3159 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_DEPARTMENT
, department
);
3163 node
= sipe_xml_child(card
, "title");
3165 char* title
= sipe_xml_data(node
);
3166 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_JOB_TITLE
, title
);
3170 node
= sipe_xml_child(card
, "office");
3172 char* office
= sipe_xml_data(node
);
3173 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_OFFICE
, office
);
3177 node
= sipe_xml_child(card
, "url");
3179 char* site
= sipe_xml_data(node
);
3180 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_SITE
, site
);
3184 for (node
= sipe_xml_child(card
, "phone");
3186 node
= sipe_xml_twin(node
))
3188 const char *phone_type
= sipe_xml_attribute(node
, "type");
3189 char* phone
= sipe_xml_data(sipe_xml_child(node
, "uri"));
3190 char* phone_display_string
= sipe_xml_data(sipe_xml_child(node
, "displayString"));
3192 sipe_update_user_phone(sipe_private
, uri
, phone_type
, phone
, phone_display_string
);
3195 g_free(phone_display_string
);
3198 for (node
= sipe_xml_child(card
, "address");
3200 node
= sipe_xml_twin(node
))
3202 if (sipe_strequal(sipe_xml_attribute(node
, "type"), "work")) {
3203 char* street
= sipe_xml_data(sipe_xml_child(node
, "street"));
3204 char* city
= sipe_xml_data(sipe_xml_child(node
, "city"));
3205 char* state
= sipe_xml_data(sipe_xml_child(node
, "state"));
3206 char* zipcode
= sipe_xml_data(sipe_xml_child(node
, "zipcode"));
3207 char* country_code
= sipe_xml_data(sipe_xml_child(node
, "countryCode"));
3209 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_STREET
, street
);
3210 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_CITY
, city
);
3211 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_STATE
, state
);
3212 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_ZIPCODE
, zipcode
);
3213 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_COUNTRY
, country_code
);
3219 g_free(country_code
);
3227 else if (sipe_strequal(attrVar
, "note"))
3230 struct sipe_buddy
*sbuddy
= g_hash_table_lookup(sipe_private
->buddies
, uri
);
3232 if (!has_note_cleaned
) {
3233 has_note_cleaned
= TRUE
;
3235 g_free(sbuddy
->note
);
3236 sbuddy
->note
= NULL
;
3237 sbuddy
->is_oof_note
= FALSE
;
3238 sbuddy
->note_since
= publish_time
;
3240 do_update_status
= TRUE
;
3242 if (sbuddy
&& (publish_time
>= sbuddy
->note_since
)) {
3243 /* clean up in case no 'note' element is supplied
3244 * which indicate note removal in client
3246 g_free(sbuddy
->note
);
3247 sbuddy
->note
= NULL
;
3248 sbuddy
->is_oof_note
= FALSE
;
3249 sbuddy
->note_since
= publish_time
;
3251 xn_node
= sipe_xml_child(xn_category
, "note/body");
3254 sbuddy
->note
= g_markup_escape_text((tmp
= sipe_xml_data(xn_node
)), -1);
3256 sbuddy
->is_oof_note
= sipe_strequal(sipe_xml_attribute(xn_node
, "type"), "OOF");
3257 sbuddy
->note_since
= publish_time
;
3259 SIPE_DEBUG_INFO("process_incoming_notify_rlmi: uri(%s), note(%s)",
3260 uri
, sbuddy
->note
? sbuddy
->note
: "");
3262 /* to trigger UI refresh in case no status info is supplied in this update */
3263 do_update_status
= TRUE
;
3268 else if(sipe_strequal(attrVar
, "state"))
3272 const sipe_xml
*xn_availability
;
3273 const sipe_xml
*xn_activity
;
3274 const sipe_xml
*xn_meeting_subject
;
3275 const sipe_xml
*xn_meeting_location
;
3276 struct sipe_buddy
*sbuddy
= uri
? g_hash_table_lookup(sipe_private
->buddies
, uri
) : NULL
;
3278 xn_node
= sipe_xml_child(xn_category
, "state");
3279 if (!xn_node
) continue;
3280 xn_availability
= sipe_xml_child(xn_node
, "availability");
3281 if (!xn_availability
) continue;
3282 xn_activity
= sipe_xml_child(xn_node
, "activity");
3283 xn_meeting_subject
= sipe_xml_child(xn_node
, "meetingSubject");
3284 xn_meeting_location
= sipe_xml_child(xn_node
, "meetingLocation");
3286 tmp
= sipe_xml_data(xn_availability
);
3287 availability
= atoi(tmp
);
3290 /* activity, meeting_subject, meeting_location */
3295 g_free(sbuddy
->activity
);
3296 sbuddy
->activity
= NULL
;
3298 const char *token
= sipe_xml_attribute(xn_activity
, "token");
3299 const sipe_xml
*xn_custom
= sipe_xml_child(xn_activity
, "custom");
3302 if (!is_empty(token
)) {
3303 sbuddy
->activity
= g_strdup(sipe_get_activity_desc_by_token(token
));
3305 /* from custom element */
3307 char *custom
= sipe_xml_data(xn_custom
);
3309 if (!is_empty(custom
)) {
3310 sbuddy
->activity
= custom
;
3316 /* meeting_subject */
3317 g_free(sbuddy
->meeting_subject
);
3318 sbuddy
->meeting_subject
= NULL
;
3319 if (xn_meeting_subject
) {
3320 char *meeting_subject
= sipe_xml_data(xn_meeting_subject
);
3322 if (!is_empty(meeting_subject
)) {
3323 sbuddy
->meeting_subject
= meeting_subject
;
3324 meeting_subject
= NULL
;
3326 g_free(meeting_subject
);
3328 /* meeting_location */
3329 g_free(sbuddy
->meeting_location
);
3330 sbuddy
->meeting_location
= NULL
;
3331 if (xn_meeting_location
) {
3332 char *meeting_location
= sipe_xml_data(xn_meeting_location
);
3334 if (!is_empty(meeting_location
)) {
3335 sbuddy
->meeting_location
= meeting_location
;
3336 meeting_location
= NULL
;
3338 g_free(meeting_location
);
3341 status
= sipe_get_status_by_availability(availability
, &tmp
);
3342 if (sbuddy
->activity
&& tmp
) {
3343 char *tmp2
= sbuddy
->activity
;
3345 sbuddy
->activity
= g_strdup_printf("%s, %s", sbuddy
->activity
, tmp
);
3349 sbuddy
->activity
= tmp
;
3353 do_update_status
= TRUE
;
3356 else if(sipe_strequal(attrVar
, "calendarData"))
3358 struct sipe_buddy
*sbuddy
= uri
? g_hash_table_lookup(sipe_private
->buddies
, uri
) : NULL
;
3359 const sipe_xml
*xn_free_busy
= sipe_xml_child(xn_category
, "calendarData/freeBusy");
3360 const sipe_xml
*xn_working_hours
= sipe_xml_child(xn_category
, "calendarData/WorkingHours");
3362 if (sbuddy
&& xn_free_busy
) {
3363 if (!has_free_busy_cleaned
) {
3364 has_free_busy_cleaned
= TRUE
;
3366 g_free(sbuddy
->cal_start_time
);
3367 sbuddy
->cal_start_time
= NULL
;
3369 g_free(sbuddy
->cal_free_busy_base64
);
3370 sbuddy
->cal_free_busy_base64
= NULL
;
3372 g_free(sbuddy
->cal_free_busy
);
3373 sbuddy
->cal_free_busy
= NULL
;
3375 sbuddy
->cal_free_busy_published
= publish_time
;
3378 if (publish_time
>= sbuddy
->cal_free_busy_published
) {
3379 g_free(sbuddy
->cal_start_time
);
3380 sbuddy
->cal_start_time
= g_strdup(sipe_xml_attribute(xn_free_busy
, "startTime"));
3382 sbuddy
->cal_granularity
= sipe_strcase_equal(sipe_xml_attribute(xn_free_busy
, "granularity"), "PT15M") ?
3385 g_free(sbuddy
->cal_free_busy_base64
);
3386 sbuddy
->cal_free_busy_base64
= sipe_xml_data(xn_free_busy
);
3388 g_free(sbuddy
->cal_free_busy
);
3389 sbuddy
->cal_free_busy
= NULL
;
3391 sbuddy
->cal_free_busy_published
= publish_time
;
3393 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
);
3397 if (sbuddy
&& xn_working_hours
) {
3398 sipe_cal_parse_working_hours(xn_working_hours
, sbuddy
);
3403 if (do_update_status
) {
3404 if (!status
) { /* no status category in this update, using contact's current status */
3405 PurpleBuddy
*pbuddy
= purple_find_buddy((PurpleAccount
*)sip
->account
, uri
);
3406 const PurplePresence
*presence
= purple_buddy_get_presence(pbuddy
);
3407 const PurpleStatus
*pstatus
= purple_presence_get_active_status(presence
);
3408 status
= purple_status_get_id(pstatus
);
3411 SIPE_DEBUG_INFO("process_incoming_notify_rlmi: %s", status
);
3412 sipe_core_buddy_got_status(SIPE_CORE_PUBLIC
, uri
, status
);
3415 sipe_xml_free(xn_categories
);
3418 static void sipe_subscribe_poolfqdn_resource_uri(const char *host
,
3420 struct sipe_core_private
*sipe_private
)
3422 struct presence_batched_routed
*payload
= g_malloc(sizeof(struct presence_batched_routed
));
3423 SIPE_DEBUG_INFO("process_incoming_notify_rlmi_resub: pool(%s)", host
);
3424 payload
->host
= g_strdup(host
);
3425 payload
->buddies
= server
;
3426 sipe_subscribe_presence_batched_routed(sipe_private
,
3428 sipe_subscribe_presence_batched_routed_free(payload
);
3431 static void process_incoming_notify_rlmi_resub(struct sipe_core_private
*sipe_private
,
3432 const gchar
*data
, unsigned len
)
3435 const sipe_xml
*xn_resource
;
3436 GHashTable
*servers
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
3441 xn_list
= sipe_xml_parse(data
, len
);
3443 for (xn_resource
= sipe_xml_child(xn_list
, "resource");
3445 xn_resource
= sipe_xml_twin(xn_resource
) )
3447 const char *uri
, *state
;
3448 const sipe_xml
*xn_instance
;
3450 xn_instance
= sipe_xml_child(xn_resource
, "instance");
3451 if (!xn_instance
) continue;
3453 uri
= sipe_xml_attribute(xn_resource
, "uri");
3454 state
= sipe_xml_attribute(xn_instance
, "state");
3455 SIPE_DEBUG_INFO("process_incoming_notify_rlmi_resub: uri(%s),state(%s)", uri
, state
);
3457 if (strstr(state
, "resubscribe")) {
3458 const char *poolFqdn
= sipe_xml_attribute(xn_instance
, "poolFqdn");
3460 if (poolFqdn
) { //[MS-PRES] Section 3.4.5.1.3 Processing Details
3461 gchar
*user
= g_strdup(uri
);
3462 host
= g_strdup(poolFqdn
);
3463 server
= g_hash_table_lookup(servers
, host
);
3464 server
= g_slist_append(server
, user
);
3465 g_hash_table_insert(servers
, host
, server
);
3467 sipe_subscribe_presence_single(sipe_private
,
3473 /* Send out any deferred poolFqdn subscriptions */
3474 g_hash_table_foreach(servers
, (GHFunc
) sipe_subscribe_poolfqdn_resource_uri
, sipe_private
);
3475 g_hash_table_destroy(servers
);
3477 sipe_xml_free(xn_list
);
3480 static void process_incoming_notify_pidf(struct sipe_core_private
*sipe_private
,
3481 const gchar
*data
, unsigned len
)
3485 gchar
*activity
= NULL
;
3487 const sipe_xml
*basicstatus
= NULL
, *tuple
, *status
;
3488 gboolean isonline
= FALSE
;
3489 const sipe_xml
*display_name_node
;
3491 pidf
= sipe_xml_parse(data
, len
);
3493 SIPE_DEBUG_INFO("process_incoming_notify_pidf: no parseable pidf:%s", data
);
3497 if ((tuple
= sipe_xml_child(pidf
, "tuple")))
3499 if ((status
= sipe_xml_child(tuple
, "status"))) {
3500 basicstatus
= sipe_xml_child(status
, "basic");
3505 SIPE_DEBUG_INFO_NOFORMAT("process_incoming_notify_pidf: no basic found");
3506 sipe_xml_free(pidf
);
3510 getbasic
= sipe_xml_data(basicstatus
);
3512 SIPE_DEBUG_INFO_NOFORMAT("process_incoming_notify_pidf: no basic data found");
3513 sipe_xml_free(pidf
);
3517 SIPE_DEBUG_INFO("process_incoming_notify_pidf: basic-status(%s)", getbasic
);
3518 if (strstr(getbasic
, "open")) {
3523 uri
= sip_uri(sipe_xml_attribute(pidf
, "entity")); /* with 'sip:' prefix */ /* AOL comes without the prefix */
3525 display_name_node
= sipe_xml_child(pidf
, "display-name");
3526 if (display_name_node
) {
3527 char * display_name
= sipe_xml_data(display_name_node
);
3529 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_DISPLAY_NAME
, display_name
);
3530 g_free(display_name
);
3533 if ((tuple
= sipe_xml_child(pidf
, "tuple"))) {
3534 if ((status
= sipe_xml_child(tuple
, "status"))) {
3535 if ((basicstatus
= sipe_xml_child(status
, "activities"))) {
3536 if ((basicstatus
= sipe_xml_child(basicstatus
, "activity"))) {
3537 activity
= sipe_xml_data(basicstatus
);
3538 SIPE_DEBUG_INFO("process_incoming_notify_pidf: activity(%s)", activity
);
3545 const gchar
* status_id
= NULL
;
3547 if (sipe_strequal(activity
, sipe_activity_map
[SIPE_ACTIVITY_BUSY
].token
)) {
3548 status_id
= SIPE_STATUS_ID_BUSY
;
3549 } else if (sipe_strequal(activity
, sipe_activity_map
[SIPE_ACTIVITY_AWAY
].token
)) {
3550 status_id
= SIPE_STATUS_ID_AWAY
;
3555 status_id
= SIPE_STATUS_ID_AVAILABLE
;
3558 SIPE_DEBUG_INFO("process_incoming_notify_pidf: status_id(%s)", status_id
);
3559 sipe_core_buddy_got_status(SIPE_CORE_PUBLIC
, uri
, status_id
);
3561 sipe_core_buddy_got_status(SIPE_CORE_PUBLIC
, uri
, SIPE_STATUS_ID_OFFLINE
);
3566 sipe_xml_free(pidf
);
3571 sipe_user_info_has_updated(struct sipe_core_private
*sipe_private
,
3572 const sipe_xml
*xn_userinfo
)
3574 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
3575 const sipe_xml
*xn_states
;
3577 g_free(sip
->user_states
);
3578 sip
->user_states
= NULL
;
3579 if ((xn_states
= sipe_xml_child(xn_userinfo
, "states")) != NULL
) {
3580 gchar
*orig
= sip
->user_states
= sipe_xml_stringify(xn_states
);
3582 /* this is a hack-around to remove added newline after inner element,
3583 * state in this case, where it shouldn't be.
3584 * After several use of sipe_xml_stringify, amount of added newlines
3585 * grows significantly.
3588 gchar c
, *stripped
= orig
;
3589 while ((c
= *orig
++)) {
3590 if ((c
!= '\n') /* && (c != '\r') */) {
3598 /* Publish initial state if not yet.
3599 * Assuming this happens on initial responce to self subscription
3600 * so we've already updated our UserInfo.
3602 if (!sip
->initial_state_published
) {
3603 send_presence_soap(sipe_private
, FALSE
);
3605 sipe_schedule_seconds(sipe_private
,
3606 "<+update-calendar>",
3608 UPDATE_CALENDAR_DELAY
,
3609 (sipe_schedule_action
) sipe_core_update_calendar
,
3614 static void process_incoming_notify_msrtc(struct sipe_core_private
*sipe_private
,
3615 const gchar
*data
, unsigned len
)
3617 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
3618 char *activity
= NULL
;
3620 const char *status_id
= NULL
;
3623 char *self_uri
= sip_uri_self(sipe_private
);
3626 const char *device_name
= NULL
;
3627 const char *cal_start_time
= NULL
;
3628 const char *cal_granularity
= NULL
;
3629 char *cal_free_busy_base64
= NULL
;
3630 struct sipe_buddy
*sbuddy
;
3631 const sipe_xml
*node
;
3632 sipe_xml
*xn_presentity
;
3633 const sipe_xml
*xn_availability
;
3634 const sipe_xml
*xn_activity
;
3635 const sipe_xml
*xn_display_name
;
3636 const sipe_xml
*xn_email
;
3637 const sipe_xml
*xn_phone_number
;
3638 const sipe_xml
*xn_userinfo
;
3639 const sipe_xml
*xn_note
;
3640 const sipe_xml
*xn_oof
;
3641 const sipe_xml
*xn_state
;
3642 const sipe_xml
*xn_contact
;
3645 const char *user_avail_nil
;
3647 time_t user_avail_since
= 0;
3648 time_t activity_since
= 0;
3650 /* fix for Reuters environment on Linux */
3651 if (data
&& strstr(data
, "encoding=\"utf-16\"")) {
3653 tmp_data
= replace(data
, "encoding=\"utf-16\"", "encoding=\"utf-8\"");
3654 xn_presentity
= sipe_xml_parse(tmp_data
, strlen(tmp_data
));
3657 xn_presentity
= sipe_xml_parse(data
, len
);
3660 xn_availability
= sipe_xml_child(xn_presentity
, "availability");
3661 xn_activity
= sipe_xml_child(xn_presentity
, "activity");
3662 xn_display_name
= sipe_xml_child(xn_presentity
, "displayName");
3663 xn_email
= sipe_xml_child(xn_presentity
, "email");
3664 xn_phone_number
= sipe_xml_child(xn_presentity
, "phoneNumber");
3665 xn_userinfo
= sipe_xml_child(xn_presentity
, "userInfo");
3666 xn_oof
= xn_userinfo
? sipe_xml_child(xn_userinfo
, "oof") : NULL
;
3667 xn_state
= xn_userinfo
? sipe_xml_child(xn_userinfo
, "states/state"): NULL
;
3668 user_avail
= xn_state
? sipe_xml_int_attribute(xn_state
, "avail", 0) : 0;
3669 user_avail_since
= xn_state
? sipe_utils_str_to_time(sipe_xml_attribute(xn_state
, "since")) : 0;
3670 user_avail_nil
= xn_state
? sipe_xml_attribute(xn_state
, "nil") : NULL
;
3671 xn_contact
= xn_userinfo
? sipe_xml_child(xn_userinfo
, "contact") : NULL
;
3672 xn_note
= xn_userinfo
? sipe_xml_child(xn_userinfo
, "note") : NULL
;
3673 note
= xn_note
? sipe_xml_data(xn_note
) : NULL
;
3675 if (sipe_strequal(user_avail_nil
, "true")) { /* null-ed */
3677 user_avail_since
= 0;
3680 name
= sipe_xml_attribute(xn_presentity
, "uri"); /* without 'sip:' prefix */
3681 uri
= sip_uri_from_name(name
);
3682 avl
= sipe_xml_int_attribute(xn_availability
, "aggregate", 0);
3683 epid
= sipe_xml_attribute(xn_availability
, "epid");
3684 act
= sipe_xml_int_attribute(xn_activity
, "aggregate", 0);
3686 status_id
= sipe_get_status_by_act_avail_2005(act
, avl
, &activity
);
3687 res_avail
= sipe_get_availability_by_status(status_id
, NULL
);
3688 if (user_avail
> res_avail
) {
3689 res_avail
= user_avail
;
3690 status_id
= sipe_get_status_by_availability(user_avail
, NULL
);
3693 if (xn_display_name
) {
3694 char *display_name
= g_strdup(sipe_xml_attribute(xn_display_name
, "displayName"));
3695 char *email
= xn_email
? g_strdup(sipe_xml_attribute(xn_email
, "email")) : NULL
;
3696 char *phone_label
= xn_phone_number
? g_strdup(sipe_xml_attribute(xn_phone_number
, "label")) : NULL
;
3697 char *phone_number
= xn_phone_number
? g_strdup(sipe_xml_attribute(xn_phone_number
, "number")) : NULL
;
3698 char *tel_uri
= sip_to_tel_uri(phone_number
);
3700 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_DISPLAY_NAME
, display_name
);
3701 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_EMAIL
, email
);
3702 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_WORK_PHONE
, tel_uri
);
3703 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY
, !is_empty(phone_label
) ? phone_label
: phone_number
);
3706 g_free(phone_label
);
3707 g_free(phone_number
);
3709 g_free(display_name
);
3714 for (node
= sipe_xml_child(xn_contact
, "tel"); node
; node
= sipe_xml_twin(node
))
3716 /* Ex.: <tel type="work">tel:+3222220000</tel> */
3717 const char *phone_type
= sipe_xml_attribute(node
, "type");
3718 char* phone
= sipe_xml_data(node
);
3720 sipe_update_user_phone(sipe_private
, uri
, phone_type
, phone
, NULL
);
3726 /* devicePresence */
3727 for (node
= sipe_xml_child(xn_presentity
, "devices/devicePresence"); node
; node
= sipe_xml_twin(node
)) {
3728 const sipe_xml
*xn_device_name
;
3729 const sipe_xml
*xn_calendar_info
;
3730 const sipe_xml
*xn_state
;
3734 if (sipe_strequal(sipe_xml_attribute(node
, "epid"), epid
)) {
3735 xn_device_name
= sipe_xml_child(node
, "deviceName");
3736 device_name
= xn_device_name
? sipe_xml_attribute(xn_device_name
, "name") : NULL
;
3740 xn_calendar_info
= sipe_xml_child(node
, "calendarInfo");
3741 if (xn_calendar_info
) {
3742 const char *cal_start_time_tmp
= sipe_xml_attribute(xn_calendar_info
, "startTime");
3744 if (cal_start_time
) {
3745 time_t cal_start_time_t
= sipe_utils_str_to_time(cal_start_time
);
3746 time_t cal_start_time_t_tmp
= sipe_utils_str_to_time(cal_start_time_tmp
);
3748 if (cal_start_time_t_tmp
> cal_start_time_t
) {
3749 cal_start_time
= cal_start_time_tmp
;
3750 cal_granularity
= sipe_xml_attribute(xn_calendar_info
, "granularity");
3751 g_free(cal_free_busy_base64
);
3752 cal_free_busy_base64
= sipe_xml_data(xn_calendar_info
);
3754 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
);
3757 cal_start_time
= cal_start_time_tmp
;
3758 cal_granularity
= sipe_xml_attribute(xn_calendar_info
, "granularity");
3759 g_free(cal_free_busy_base64
);
3760 cal_free_busy_base64
= sipe_xml_data(xn_calendar_info
);
3762 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
);
3767 xn_state
= sipe_xml_child(node
, "states/state");
3769 int dev_avail
= sipe_xml_int_attribute(xn_state
, "avail", 0);
3770 time_t dev_avail_since
= sipe_utils_str_to_time(sipe_xml_attribute(xn_state
, "since"));
3772 state
= sipe_xml_data(xn_state
);
3773 if (dev_avail_since
> user_avail_since
&&
3774 dev_avail
>= res_avail
)
3776 res_avail
= dev_avail
;
3777 if (!is_empty(state
))
3779 if (sipe_strequal(state
, sipe_activity_map
[SIPE_ACTIVITY_ON_PHONE
].token
)) {
3781 activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_ON_PHONE
));
3782 } else if (sipe_strequal(state
, "presenting")) {
3784 activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_IN_CONF
));
3789 activity_since
= dev_avail_since
;
3791 status_id
= sipe_get_status_by_availability(res_avail
, &activity
);
3798 if (xn_oof
&& res_avail
>= 15000) { /* 12000 in 2007 */
3800 activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_OOF
));
3804 sbuddy
= g_hash_table_lookup(sipe_private
->buddies
, uri
);
3807 g_free(sbuddy
->activity
);
3808 sbuddy
->activity
= activity
;
3811 sbuddy
->activity_since
= activity_since
;
3813 sbuddy
->user_avail
= user_avail
;
3814 sbuddy
->user_avail_since
= user_avail_since
;
3816 g_free(sbuddy
->note
);
3817 sbuddy
->note
= NULL
;
3818 if (!is_empty(note
)) { sbuddy
->note
= g_markup_escape_text(note
, -1); }
3820 sbuddy
->is_oof_note
= (xn_oof
!= NULL
);
3822 g_free(sbuddy
->device_name
);
3823 sbuddy
->device_name
= NULL
;
3824 if (!is_empty(device_name
)) { sbuddy
->device_name
= g_strdup(device_name
); }
3826 if (!is_empty(cal_free_busy_base64
)) {
3827 g_free(sbuddy
->cal_start_time
);
3828 sbuddy
->cal_start_time
= g_strdup(cal_start_time
);
3830 sbuddy
->cal_granularity
= sipe_strcase_equal(cal_granularity
, "PT15M") ? 15 : 0;
3832 g_free(sbuddy
->cal_free_busy_base64
);
3833 sbuddy
->cal_free_busy_base64
= cal_free_busy_base64
;
3834 cal_free_busy_base64
= NULL
;
3836 g_free(sbuddy
->cal_free_busy
);
3837 sbuddy
->cal_free_busy
= NULL
;
3840 sbuddy
->last_non_cal_status_id
= status_id
;
3841 g_free(sbuddy
->last_non_cal_activity
);
3842 sbuddy
->last_non_cal_activity
= g_strdup(sbuddy
->activity
);
3844 if (sipe_strcase_equal(sbuddy
->name
, self_uri
)) {
3845 if (!sipe_strequal(sbuddy
->note
, sip
->note
)) /* not same */
3847 sip
->is_oof_note
= sbuddy
->is_oof_note
;
3850 sip
->note
= g_strdup(sbuddy
->note
);
3852 sip
->note_since
= time(NULL
);
3855 g_free(sip
->status
);
3856 sip
->status
= g_strdup(sbuddy
->last_non_cal_status_id
);
3859 g_free(cal_free_busy_base64
);
3862 SIPE_DEBUG_INFO("process_incoming_notify_msrtc: status(%s)", status_id
);
3863 sipe_core_buddy_got_status(SIPE_CORE_PUBLIC
, uri
, status_id
);
3865 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
) && sipe_strcase_equal(self_uri
, uri
)) {
3866 sipe_user_info_has_updated(sipe_private
, xn_userinfo
);
3870 sipe_xml_free(xn_presentity
);
3875 static void sipe_presence_mime_cb(gpointer user_data
, /* sipe_core_private */
3876 const GSList
*fields
,
3880 const gchar
*type
= sipe_utils_nameval_find(fields
, "Content-Type");
3882 if (strstr(type
,"application/rlmi+xml")) {
3883 process_incoming_notify_rlmi_resub(user_data
, body
, length
);
3884 } else if (strstr(type
, "text/xml+msrtc.pidf")) {
3885 process_incoming_notify_msrtc(user_data
, body
, length
);
3887 process_incoming_notify_rlmi(user_data
, body
, length
);
3891 static void sipe_process_presence(struct sipe_core_private
*sipe_private
,
3894 const char *ctype
= sipmsg_find_header(msg
, "Content-Type");
3896 SIPE_DEBUG_INFO("sipe_process_presence: Content-Type: %s", ctype
? ctype
: "");
3899 (strstr(ctype
, "application/rlmi+xml") ||
3900 strstr(ctype
, "application/msrtc-event-categories+xml")))
3902 if (strstr(ctype
, "multipart"))
3904 sipe_mime_parts_foreach(ctype
, msg
->body
, sipe_presence_mime_cb
, sipe_private
);
3906 else if(strstr(ctype
, "application/msrtc-event-categories+xml") )
3908 process_incoming_notify_rlmi(sipe_private
, msg
->body
, msg
->bodylen
);
3910 else if(strstr(ctype
, "application/rlmi+xml"))
3912 process_incoming_notify_rlmi_resub(sipe_private
, msg
->body
, msg
->bodylen
);
3915 else if(ctype
&& strstr(ctype
, "text/xml+msrtc.pidf"))
3917 process_incoming_notify_msrtc(sipe_private
, msg
->body
, msg
->bodylen
);
3921 process_incoming_notify_pidf(sipe_private
, msg
->body
, msg
->bodylen
);
3925 static void sipe_presence_timeout_mime_cb(gpointer user_data
,
3926 SIPE_UNUSED_PARAMETER
const GSList
*fields
,
3930 GSList
**buddies
= user_data
;
3931 sipe_xml
*xml
= sipe_xml_parse(body
, length
);
3933 if (xml
&& !sipe_strequal(sipe_xml_name(xml
), "list")) {
3934 const gchar
*uri
= sipe_xml_attribute(xml
, "uri");
3935 const sipe_xml
*xn_category
;
3938 * automaton: presence is never expected to change
3940 * see: http://msdn.microsoft.com/en-us/library/ee354295(office.13).aspx
3942 for (xn_category
= sipe_xml_child(xml
, "category");
3944 xn_category
= sipe_xml_twin(xn_category
)) {
3945 if (sipe_strequal(sipe_xml_attribute(xn_category
, "name"),
3947 const sipe_xml
*node
= sipe_xml_child(xn_category
, "contactCard/automaton");
3949 char *boolean
= sipe_xml_data(node
);
3950 if (sipe_strequal(boolean
, "true")) {
3951 SIPE_DEBUG_INFO("sipe_process_presence_timeout: %s is an automaton: - not subscribing to presence updates",
3962 *buddies
= g_slist_append(*buddies
, sip_uri(uri
));
3969 static void sipe_process_presence_timeout(struct sipe_core_private
*sipe_private
,
3970 struct sipmsg
*msg
, gchar
*who
,
3973 const char *ctype
= sipmsg_find_header(msg
, "Content-Type");
3974 gchar
*action_name
= sipe_utils_presence_key(who
);
3976 SIPE_DEBUG_INFO("sipe_process_presence_timeout: Content-Type: %s", ctype
? ctype
: "");
3979 strstr(ctype
, "multipart") &&
3980 (strstr(ctype
, "application/rlmi+xml") ||
3981 strstr(ctype
, "application/msrtc-event-categories+xml"))) {
3982 GSList
*buddies
= NULL
;
3984 sipe_mime_parts_foreach(ctype
, msg
->body
, sipe_presence_timeout_mime_cb
, &buddies
);
3987 struct presence_batched_routed
*payload
= g_malloc(sizeof(struct presence_batched_routed
));
3988 payload
->host
= g_strdup(who
);
3989 payload
->buddies
= buddies
;
3990 sipe_schedule_seconds(sipe_private
,
3994 sipe_subscribe_presence_batched_routed
,
3995 sipe_subscribe_presence_batched_routed_free
);
3996 SIPE_DEBUG_INFO("Resubscription multiple contacts with batched support & route(%s) in %d", who
, timeout
);
4000 sipe_schedule_seconds(sipe_private
,
4004 sipe_subscribe_presence_single
,
4006 SIPE_DEBUG_INFO("Resubscription single contact with batched support(%s) in %d", who
, timeout
);
4008 g_free(action_name
);
4012 * Dispatcher for all incoming subscription information
4013 * whether it comes from NOTIFY, BENOTIFY requests or
4014 * piggy-backed to subscription's OK responce.
4016 * @param request whether initiated from BE/NOTIFY request or OK-response message.
4017 * @param benotify whether initiated from NOTIFY or BENOTIFY request.
4019 void process_incoming_notify(struct sipe_core_private
*sipe_private
,
4021 gboolean request
, gboolean benotify
)
4023 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
4024 const gchar
*content_type
= sipmsg_find_header(msg
, "Content-Type");
4025 const gchar
*event
= sipmsg_find_header(msg
, "Event");
4026 const gchar
*subscription_state
= sipmsg_find_header(msg
, "subscription-state");
4028 SIPE_DEBUG_INFO("process_incoming_notify: subscription_state: %s", subscription_state
? subscription_state
: "");
4030 /* implicit subscriptions */
4031 if (content_type
&& g_str_has_prefix(content_type
, "application/ms-imdn+xml")) {
4032 sipe_process_imdn(sipe_private
, msg
);
4036 /* for one off subscriptions (send with Expire: 0) */
4037 if (sipe_strcase_equal(event
, "vnd-microsoft-provisioning-v2"))
4039 sipe_process_provisioning_v2(sipe_private
, msg
);
4041 else if (sipe_strcase_equal(event
, "vnd-microsoft-provisioning"))
4043 sipe_process_provisioning(sipe_private
, msg
);
4045 else if (sipe_strcase_equal(event
, "presence"))
4047 sipe_process_presence(sipe_private
, msg
);
4049 else if (sipe_strcase_equal(event
, "registration-notify"))
4051 sipe_process_registration_notify(sipe_private
, msg
);
4054 if (!subscription_state
|| strstr(subscription_state
, "active"))
4056 if (sipe_strcase_equal(event
, "vnd-microsoft-roaming-contacts"))
4058 sipe_process_roaming_contacts(sipe_private
, msg
);
4060 else if (sipe_strcase_equal(event
, "vnd-microsoft-roaming-self"))
4062 sipe_process_roaming_self(sipe_private
, msg
);
4064 else if (sipe_strcase_equal(event
, "vnd-microsoft-roaming-ACL"))
4066 sipe_process_roaming_acl(sipe_private
, msg
);
4068 else if (sipe_strcase_equal(event
, "presence.wpending"))
4070 sipe_process_presence_wpending(sipe_private
, msg
);
4072 else if (sipe_strcase_equal(event
, "conference"))
4074 sipe_process_conference(sipe_private
, msg
);
4079 /* The server sends status 'terminated' */
4080 if (subscription_state
&& strstr(subscription_state
, "terminated") ) {
4081 gchar
*who
= parse_from(sipmsg_find_header(msg
, request
? "From" : "To"));
4082 gchar
*key
= sipe_utils_subscription_key(event
, who
);
4084 SIPE_DEBUG_INFO("process_incoming_notify: server says that subscription to %s was terminated.", who
);
4087 sipe_subscriptions_remove(sipe_private
, key
);
4091 if (!request
&& event
) {
4092 const gchar
*expires_header
= sipmsg_find_header(msg
, "Expires");
4093 int timeout
= expires_header
? strtol(expires_header
, NULL
, 10) : 0;
4094 SIPE_DEBUG_INFO("process_incoming_notify: subscription expires:%d", timeout
);
4097 /* 2 min ahead of expiration */
4098 timeout
= (timeout
- 120) > 120 ? (timeout
- 120) : timeout
;
4100 if (sipe_strcase_equal(event
, "presence.wpending") &&
4101 g_slist_find_custom(sip
->allow_events
, "presence.wpending", (GCompareFunc
)g_ascii_strcasecmp
))
4103 gchar
*action_name
= g_strdup_printf("<%s>", "presence.wpending");
4104 sipe_schedule_seconds(sipe_private
,
4108 sipe_subscribe_presence_wpending
,
4110 g_free(action_name
);
4112 else if (sipe_strcase_equal(event
, "presence") &&
4113 g_slist_find_custom(sip
->allow_events
, "presence", (GCompareFunc
)g_ascii_strcasecmp
))
4115 gchar
*who
= parse_from(sipmsg_find_header(msg
, "To"));
4116 gchar
*action_name
= sipe_utils_presence_key(who
);
4118 if (sip
->batched_support
) {
4119 sipe_process_presence_timeout(sipe_private
, msg
, who
, timeout
);
4122 sipe_schedule_seconds(sipe_private
,
4126 sipe_subscribe_presence_single
,
4128 SIPE_DEBUG_INFO("Resubscription single contact (%s) in %d", who
, timeout
);
4130 g_free(action_name
);
4136 /* The client responses on received a NOTIFY message */
4137 if (request
&& !benotify
)
4139 sip_transport_response(sipe_private
, msg
, 200, "OK", NULL
);
4144 * Whether user manually changed status or
4145 * it was changed automatically due to user
4146 * became inactive/active again
4149 sipe_is_user_state(struct sipe_core_private
*sipe_private
)
4151 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
4153 time_t now
= time(NULL
);
4155 SIPE_DEBUG_INFO("sipe_is_user_state: sip->idle_switch : %s", asctime(localtime(&(sip
->idle_switch
))));
4156 SIPE_DEBUG_INFO("sipe_is_user_state: now : %s", asctime(localtime(&now
)));
4158 res
= ((now
- SIPE_IDLE_SET_DELAY
* 2) >= sip
->idle_switch
);
4160 SIPE_DEBUG_INFO("sipe_is_user_state: res = %s", res
? "USER" : "MACHINE");
4165 send_presence_soap0(struct sipe_core_private
*sipe_private
,
4166 gboolean do_publish_calendar
,
4167 gboolean do_reset_status
)
4169 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
4170 struct sipe_calendar
* cal
= sip
->cal
;
4171 int availability
= 0;
4176 gchar
*res_note
= NULL
;
4177 gchar
*res_oof
= NULL
;
4178 const gchar
*note_pub
= NULL
;
4179 gchar
*states
= NULL
;
4180 gchar
*calendar_data
= NULL
;
4181 gchar
*epid
= get_epid(sipe_private
);
4182 time_t now
= time(NULL
);
4183 gchar
*since_time_str
= sipe_utils_time_to_str(now
);
4184 const gchar
*oof_note
= cal
? sipe_ews_get_oof_note(cal
) : NULL
;
4185 const char *user_input
;
4186 gboolean pub_oof
= cal
&& oof_note
&& (!sip
->note
|| cal
->updated
> sip
->note_since
);
4188 if (oof_note
&& sip
->note
) {
4189 SIPE_DEBUG_INFO("cal->oof_start : %s", asctime(localtime(&(cal
->oof_start
))));
4190 SIPE_DEBUG_INFO("sip->note_since : %s", asctime(localtime(&(sip
->note_since
))));
4193 SIPE_DEBUG_INFO("sip->note : %s", sip
->note
? sip
->note
: "");
4195 if (!sip
->initial_state_published
||
4198 g_free(sip
->status
);
4199 sip
->status
= g_strdup(SIPE_STATUS_ID_AVAILABLE
);
4202 sipe_get_act_avail_by_status_2005(sip
->status
, &activity
, &availability
);
4206 note_pub
= oof_note
;
4207 res_oof
= SIPE_SOAP_SET_PRESENCE_OOF_XML
;
4208 cal
->published
= TRUE
;
4209 } else if (sip
->note
) {
4210 if (sip
->is_oof_note
&& !oof_note
) { /* stale OOF note, as it's not present in cal already */
4213 sip
->is_oof_note
= FALSE
;
4214 sip
->note_since
= 0;
4216 note_pub
= sip
->note
;
4217 res_oof
= sip
->is_oof_note
? SIPE_SOAP_SET_PRESENCE_OOF_XML
: "";
4223 /* to protocol internal plain text format */
4224 tmp
= sipe_backend_markup_strip_html(note_pub
);
4225 res_note
= g_markup_printf_escaped(SIPE_SOAP_SET_PRESENCE_NOTE_XML
, tmp
);
4230 if (!do_reset_status
) {
4231 if (sipe_is_user_state(sipe_private
) && !do_publish_calendar
&& sip
->initial_state_published
)
4233 gchar
*activity_token
= NULL
;
4234 int avail_2007
= sipe_get_availability_by_status(sip
->status
, &activity_token
);
4236 states
= g_strdup_printf(SIPE_SOAP_SET_PRESENCE_STATES
,
4241 g_free(activity_token
);
4243 else /* preserve existing publication */
4245 if (sip
->user_states
) {
4246 states
= g_strdup(sip
->user_states
);
4250 /* do nothing - then User state will be erased */
4252 sip
->initial_state_published
= TRUE
;
4255 if (cal
&& (!is_empty(cal
->legacy_dn
) || !is_empty(cal
->email
)) && cal
->fb_start
&& !is_empty(cal
->free_busy
))
4257 char *fb_start_str
= sipe_utils_time_to_str(cal
->fb_start
);
4258 char *free_busy_base64
= sipe_cal_get_freebusy_base64(cal
->free_busy
);
4259 calendar_data
= g_strdup_printf(SIPE_SOAP_SET_PRESENCE_CALENDAR
,
4260 !is_empty(cal
->legacy_dn
) ? cal
->legacy_dn
: cal
->email
,
4263 g_free(fb_start_str
);
4264 g_free(free_busy_base64
);
4267 user_input
= (sipe_is_user_state(sipe_private
) ||
4268 sipe_strequal(sip
->status
, SIPE_STATUS_ID_AVAILABLE
)) ?
4271 /* forming resulting XML */
4272 body
= g_strdup_printf(SIPE_SOAP_SET_PRESENCE
,
4273 sipe_private
->username
,
4276 (tmp
= g_ascii_strup(g_get_host_name(), -1)),
4277 res_note
? res_note
: "",
4278 res_oof
? res_oof
: "",
4279 states
? states
: "",
4280 calendar_data
? calendar_data
: "",
4289 g_free(calendar_data
);
4291 send_soap_request(sipe_private
, body
);
4294 g_free(since_time_str
);
4299 send_presence_soap(struct sipe_core_private
*sipe_private
,
4300 gboolean do_publish_calendar
)
4302 return send_presence_soap0(sipe_private
, do_publish_calendar
, FALSE
);
4307 process_send_presence_category_publish_response(struct sipe_core_private
*sipe_private
,
4309 struct transaction
*trans
)
4311 const gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
4313 if (msg
->response
== 409 && g_str_has_prefix(contenttype
, "application/msrtc-fault+xml")) {
4315 const sipe_xml
*node
;
4319 gboolean has_device_publication
= FALSE
;
4321 xml
= sipe_xml_parse(msg
->body
, msg
->bodylen
);
4323 /* test if version mismatch fault */
4324 fault_code
= sipe_xml_data(sipe_xml_child(xml
, "Faultcode"));
4325 if (!sipe_strequal(fault_code
, "Client.BadCall.WrongDelta")) {
4326 SIPE_DEBUG_INFO("process_send_presence_category_publish_response: unsupported fault code:%s returning.", fault_code
);
4333 /* accumulating information about faulty versions */
4334 faults
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, g_free
);
4335 for (node
= sipe_xml_child(xml
, "details/operation");
4337 node
= sipe_xml_twin(node
))
4339 const gchar
*index
= sipe_xml_attribute(node
, "index");
4340 const gchar
*curVersion
= sipe_xml_attribute(node
, "curVersion");
4342 g_hash_table_insert(faults
, g_strdup(index
), g_strdup(curVersion
));
4343 SIPE_DEBUG_INFO("fault added: index:%s curVersion:%s", index
, curVersion
);
4347 /* here we are parsing our own request to figure out what publication
4348 * referenced here only by index went wrong
4350 xml
= sipe_xml_parse(trans
->msg
->body
, trans
->msg
->bodylen
);
4353 for (node
= sipe_xml_child(xml
, "publications/publication"),
4354 index_our
= 1; /* starts with 1 - our first publication */
4356 node
= sipe_xml_twin(node
), index_our
++)
4358 gchar
*idx
= g_strdup_printf("%d", index_our
);
4359 const gchar
*curVersion
= g_hash_table_lookup(faults
, idx
);
4360 const gchar
*categoryName
= sipe_xml_attribute(node
, "categoryName");
4363 if (sipe_strequal("device", categoryName
)) {
4364 has_device_publication
= TRUE
;
4367 if (curVersion
) { /* fault exist on this index */
4368 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
4369 const gchar
*container
= sipe_xml_attribute(node
, "container");
4370 const gchar
*instance
= sipe_xml_attribute(node
, "instance");
4371 /* key is <category><instance><container> */
4372 gchar
*key
= g_strdup_printf("<%s><%s><%s>", categoryName
, instance
, container
);
4373 GHashTable
*category
= g_hash_table_lookup(sip
->our_publications
, categoryName
);
4376 struct sipe_publication
*publication
=
4377 g_hash_table_lookup(category
, key
);
4379 SIPE_DEBUG_INFO("key is %s", key
);
4382 SIPE_DEBUG_INFO("Updating %s with version %s. Was %d before.",
4383 key
, curVersion
, publication
->version
);
4384 /* updating publication's version to the correct one */
4385 publication
->version
= atoi(curVersion
);
4388 /* We somehow lost this category from our publications... */
4389 struct sipe_publication
*publication
= g_new0(struct sipe_publication
, 1);
4390 publication
->category
= g_strdup(categoryName
);
4391 publication
->instance
= atoi(instance
);
4392 publication
->container
= atoi(container
);
4393 publication
->version
= atoi(curVersion
);
4394 category
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
4395 g_free
, (GDestroyNotify
)free_publication
);
4396 g_hash_table_insert(category
, g_strdup(key
), publication
);
4397 g_hash_table_insert(sip
->our_publications
, g_strdup(categoryName
), category
);
4398 SIPE_DEBUG_INFO("added lost category '%s' key '%s'", categoryName
, key
);
4404 g_hash_table_destroy(faults
);
4406 /* rebublishing with right versions */
4407 if (has_device_publication
) {
4408 send_publish_category_initial(sipe_private
);
4410 send_presence_status(sipe_private
, NULL
);
4417 * Returns 'device' XML part for publication.
4418 * Must be g_free'd after use.
4421 sipe_publish_get_category_device(struct sipe_core_private
*sipe_private
)
4423 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
4426 gchar
*epid
= get_epid(sipe_private
);
4427 gchar
*uuid
= generateUUIDfromEPID(epid
);
4428 guint device_instance
= sipe_get_pub_instance(sipe_private
, SIPE_PUB_DEVICE
);
4429 /* key is <category><instance><container> */
4430 gchar
*key
= g_strdup_printf("<%s><%u><%u>", "device", device_instance
, 2);
4431 struct sipe_publication
*publication
=
4432 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "device"), key
);
4437 uri
= sip_uri_self(sipe_private
);
4438 doc
= g_strdup_printf(SIPE_PUB_XML_DEVICE
,
4440 publication
? publication
->version
: 0,
4443 "00:00:00+01:00", /* @TODO make timezone real*/
4454 * A service method - use
4455 * - send_publish_get_category_state_machine and
4456 * - send_publish_get_category_state_user instead.
4457 * Must be g_free'd after use.
4460 sipe_publish_get_category_state(struct sipe_core_private
*sipe_private
,
4461 gboolean is_user_state
)
4463 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
4464 int availability
= sipe_get_availability_by_status(sip
->status
, NULL
);
4465 guint instance
= is_user_state
? sipe_get_pub_instance(sipe_private
, SIPE_PUB_STATE_USER
) :
4466 sipe_get_pub_instance(sipe_private
, SIPE_PUB_STATE_MACHINE
);
4467 /* key is <category><instance><container> */
4468 gchar
*key_2
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 2);
4469 gchar
*key_3
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 3);
4470 struct sipe_publication
*publication_2
=
4471 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "state"), key_2
);
4472 struct sipe_publication
*publication_3
=
4473 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "state"), key_3
);
4478 if (publication_2
&& (publication_2
->availability
== availability
))
4480 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_state: state has NOT changed. Exiting.");
4481 return NULL
; /* nothing to update */
4484 return g_strdup_printf( is_user_state
? SIPE_PUB_XML_STATE_USER
: SIPE_PUB_XML_STATE_MACHINE
,
4486 publication_2
? publication_2
->version
: 0,
4489 publication_3
? publication_3
->version
: 0,
4494 * Only Busy and OOF calendar event are published.
4495 * Different instances are used for that.
4497 * Must be g_free'd after use.
4500 sipe_publish_get_category_state_calendar(struct sipe_core_private
*sipe_private
,
4501 struct sipe_cal_event
*event
,
4505 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
4506 gchar
*start_time_str
;
4507 int availability
= 0;
4510 guint instance
= (cal_satus
== SIPE_CAL_OOF
) ?
4511 sipe_get_pub_instance(sipe_private
, SIPE_PUB_STATE_CALENDAR_OOF
) :
4512 sipe_get_pub_instance(sipe_private
, SIPE_PUB_STATE_CALENDAR
);
4514 /* key is <category><instance><container> */
4515 gchar
*key_2
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 2);
4516 gchar
*key_3
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 3);
4517 struct sipe_publication
*publication_2
=
4518 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "state"), key_2
);
4519 struct sipe_publication
*publication_3
=
4520 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "state"), key_3
);
4525 if (!publication_3
&& !event
) { /* was nothing, have nothing, exiting */
4526 SIPE_DEBUG_INFO("sipe_publish_get_category_state_calendar: "
4527 "Exiting as no publication and no event for cal_satus:%d", cal_satus
);
4533 (publication_3
->availability
== availability
) &&
4534 sipe_strequal(publication_3
->cal_event_hash
, (tmp
= sipe_cal_event_hash(event
))))
4537 SIPE_DEBUG_INFO("sipe_publish_get_category_state_calendar: "
4538 "cal state has NOT changed for cal_satus:%d. Exiting.", cal_satus
);
4539 return NULL
; /* nothing to update */
4544 (event
->cal_status
== SIPE_CAL_BUSY
||
4545 event
->cal_status
== SIPE_CAL_OOF
))
4547 gchar
*availability_xml_str
= NULL
;
4548 gchar
*activity_xml_str
= NULL
;
4549 gchar
*escaped_subject
= event
->subject
? g_markup_escape_text(event
->subject
, -1) : NULL
;
4550 gchar
*escaped_location
= event
->location
? g_markup_escape_text(event
->location
, -1) : NULL
;
4552 if (event
->cal_status
== SIPE_CAL_BUSY
) {
4553 availability_xml_str
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_AVAIL
, 6500);
4556 if (event
->cal_status
== SIPE_CAL_BUSY
&& event
->is_meeting
) {
4557 activity_xml_str
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY
,
4558 sipe_activity_map
[SIPE_ACTIVITY_IN_MEETING
].token
,
4559 "minAvailability=\"6500\"",
4560 "maxAvailability=\"8999\"");
4561 } else if (event
->cal_status
== SIPE_CAL_OOF
) {
4562 activity_xml_str
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY
,
4563 sipe_activity_map
[SIPE_ACTIVITY_OOF
].token
,
4564 "minAvailability=\"12000\"",
4567 start_time_str
= sipe_utils_time_to_str(event
->start_time
);
4569 res
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR
,
4571 publication_2
? publication_2
->version
: 0,
4574 availability_xml_str
? availability_xml_str
: "",
4575 activity_xml_str
? activity_xml_str
: "",
4576 escaped_subject
? escaped_subject
: "",
4577 escaped_location
? escaped_location
: "",
4580 publication_3
? publication_3
->version
: 0,
4583 availability_xml_str
? availability_xml_str
: "",
4584 activity_xml_str
? activity_xml_str
: "",
4585 escaped_subject
? escaped_subject
: "",
4586 escaped_location
? escaped_location
: ""
4588 g_free(escaped_location
);
4589 g_free(escaped_subject
);
4590 g_free(start_time_str
);
4591 g_free(availability_xml_str
);
4592 g_free(activity_xml_str
);
4595 else /* including !event, SIPE_CAL_FREE, SIPE_CAL_TENTATIVE */
4597 res
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_CLEAR
,
4599 publication_2
? publication_2
->version
: 0,
4602 publication_3
? publication_3
->version
: 0
4610 * Returns 'machineState' XML part for publication.
4611 * Must be g_free'd after use.
4614 sipe_publish_get_category_state_machine(struct sipe_core_private
*sipe_private
)
4616 return sipe_publish_get_category_state(sipe_private
, FALSE
);
4620 * Returns 'userState' XML part for publication.
4621 * Must be g_free'd after use.
4624 sipe_publish_get_category_state_user(struct sipe_core_private
*sipe_private
)
4626 return sipe_publish_get_category_state(sipe_private
, TRUE
);
4630 * Returns 'note' XML part for publication.
4631 * Must be g_free'd after use.
4633 * Protocol format for Note is plain text.
4635 * @param note a note in Sipe internal HTML format
4636 * @param note_type either personal or OOF
4639 sipe_publish_get_category_note(struct sipe_core_private
*sipe_private
,
4640 const char *note
, /* html */
4641 const char *note_type
,
4645 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
4646 guint instance
= sipe_strequal("OOF", note_type
) ? sipe_get_pub_instance(sipe_private
, SIPE_PUB_NOTE_OOF
) : 0;
4647 /* key is <category><instance><container> */
4648 gchar
*key_note_200
= g_strdup_printf("<%s><%u><%u>", "note", instance
, 200);
4649 gchar
*key_note_300
= g_strdup_printf("<%s><%u><%u>", "note", instance
, 300);
4650 gchar
*key_note_400
= g_strdup_printf("<%s><%u><%u>", "note", instance
, 400);
4652 struct sipe_publication
*publication_note_200
=
4653 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "note"), key_note_200
);
4654 struct sipe_publication
*publication_note_300
=
4655 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "note"), key_note_300
);
4656 struct sipe_publication
*publication_note_400
=
4657 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "note"), key_note_400
);
4659 char *tmp
= note
? sipe_backend_markup_strip_html(note
) : NULL
;
4660 char *n1
= tmp
? g_markup_escape_text(tmp
, -1) : NULL
;
4661 const char *n2
= publication_note_200
? publication_note_200
->note
: NULL
;
4662 char *res
, *tmp1
, *tmp2
, *tmp3
;
4663 char *start_time_attr
;
4664 char *end_time_attr
;
4668 g_free(key_note_200
);
4669 g_free(key_note_300
);
4670 g_free(key_note_400
);
4672 /* we even need to republish empty note */
4673 if (sipe_strequal(n1
, n2
))
4675 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_note: note has NOT changed. Exiting.");
4677 return NULL
; /* nothing to update */
4680 start_time_attr
= note_start
? g_strdup_printf(" startTime=\"%s\"", (tmp
= sipe_utils_time_to_str(note_start
))) : NULL
;
4683 end_time_attr
= note_end
? g_strdup_printf(" endTime=\"%s\"", (tmp
= sipe_utils_time_to_str(note_end
))) : NULL
;
4687 tmp1
= g_strdup_printf(SIPE_PUB_XML_NOTE
,
4690 publication_note_200
? publication_note_200
->version
: 0,
4692 start_time_attr
? start_time_attr
: "",
4693 end_time_attr
? end_time_attr
: "",
4696 tmp2
= g_strdup_printf(SIPE_PUB_XML_NOTE
,
4699 publication_note_300
? publication_note_300
->version
: 0,
4701 start_time_attr
? start_time_attr
: "",
4702 end_time_attr
? end_time_attr
: "",
4705 tmp3
= g_strdup_printf(SIPE_PUB_XML_NOTE
,
4708 publication_note_400
? publication_note_400
->version
: 0,
4710 start_time_attr
? start_time_attr
: "",
4711 end_time_attr
? end_time_attr
: "",
4714 tmp1
= g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR
,
4718 publication_note_200
? publication_note_200
->version
: 0,
4720 tmp2
= g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR
,
4724 publication_note_200
? publication_note_200
->version
: 0,
4726 tmp3
= g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR
,
4730 publication_note_200
? publication_note_200
->version
: 0,
4733 res
= g_strconcat(tmp1
, tmp2
, tmp3
, NULL
);
4735 g_free(start_time_attr
);
4736 g_free(end_time_attr
);
4746 * Returns 'calendarData' XML part with WorkingHours for publication.
4747 * Must be g_free'd after use.
4750 sipe_publish_get_category_cal_working_hours(struct sipe_core_private
*sipe_private
)
4752 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
4753 struct sipe_calendar
* cal
= sip
->cal
;
4755 /* key is <category><instance><container> */
4756 gchar
*key_cal_1
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 1);
4757 gchar
*key_cal_100
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 100);
4758 gchar
*key_cal_200
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 200);
4759 gchar
*key_cal_300
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 300);
4760 gchar
*key_cal_400
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 400);
4761 gchar
*key_cal_32000
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 32000);
4763 struct sipe_publication
*publication_cal_1
=
4764 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_1
);
4765 struct sipe_publication
*publication_cal_100
=
4766 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_100
);
4767 struct sipe_publication
*publication_cal_200
=
4768 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_200
);
4769 struct sipe_publication
*publication_cal_300
=
4770 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_300
);
4771 struct sipe_publication
*publication_cal_400
=
4772 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_400
);
4773 struct sipe_publication
*publication_cal_32000
=
4774 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_32000
);
4776 const char *n1
= cal
? cal
->working_hours_xml_str
: NULL
;
4777 const char *n2
= publication_cal_300
? publication_cal_300
->working_hours_xml_str
: NULL
;
4780 g_free(key_cal_100
);
4781 g_free(key_cal_200
);
4782 g_free(key_cal_300
);
4783 g_free(key_cal_400
);
4784 g_free(key_cal_32000
);
4786 if (!cal
|| is_empty(cal
->email
) || is_empty(cal
->working_hours_xml_str
)) {
4787 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_working_hours: no data to publish, exiting");
4791 if (sipe_strequal(n1
, n2
))
4793 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_working_hours: WorkingHours has NOT changed. Exiting.");
4794 return NULL
; /* nothing to update */
4797 return g_strdup_printf(SIPE_PUB_XML_WORKING_HOURS
,
4799 publication_cal_1
? publication_cal_1
->version
: 0,
4801 cal
->working_hours_xml_str
,
4803 publication_cal_100
? publication_cal_100
->version
: 0,
4805 publication_cal_200
? publication_cal_200
->version
: 0,
4807 cal
->working_hours_xml_str
,
4809 publication_cal_300
? publication_cal_300
->version
: 0,
4811 cal
->working_hours_xml_str
,
4812 /* 400 - Personal */
4813 publication_cal_400
? publication_cal_400
->version
: 0,
4815 cal
->working_hours_xml_str
,
4816 /* 32000 - Blocked */
4817 publication_cal_32000
? publication_cal_32000
->version
: 0
4822 * Returns 'calendarData' XML part with FreeBusy for publication.
4823 * Must be g_free'd after use.
4826 sipe_publish_get_category_cal_free_busy(struct sipe_core_private
*sipe_private
)
4828 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
4829 struct sipe_calendar
* cal
= sip
->cal
;
4830 guint cal_data_instance
= sipe_get_pub_instance(sipe_private
, SIPE_PUB_CALENDAR_DATA
);
4832 char *free_busy_base64
;
4833 /* const char *st; */
4834 /* const char *fb; */
4837 /* key is <category><instance><container> */
4838 gchar
*key_cal_1
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 1);
4839 gchar
*key_cal_100
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 100);
4840 gchar
*key_cal_200
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 200);
4841 gchar
*key_cal_300
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 300);
4842 gchar
*key_cal_400
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 400);
4843 gchar
*key_cal_32000
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 32000);
4845 struct sipe_publication
*publication_cal_1
=
4846 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_1
);
4847 struct sipe_publication
*publication_cal_100
=
4848 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_100
);
4849 struct sipe_publication
*publication_cal_200
=
4850 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_200
);
4851 struct sipe_publication
*publication_cal_300
=
4852 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_300
);
4853 struct sipe_publication
*publication_cal_400
=
4854 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_400
);
4855 struct sipe_publication
*publication_cal_32000
=
4856 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_32000
);
4859 g_free(key_cal_100
);
4860 g_free(key_cal_200
);
4861 g_free(key_cal_300
);
4862 g_free(key_cal_400
);
4863 g_free(key_cal_32000
);
4865 if (!cal
|| is_empty(cal
->email
) || !cal
->fb_start
|| is_empty(cal
->free_busy
)) {
4866 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_free_busy: no data to publish, exiting");
4870 fb_start_str
= sipe_utils_time_to_str(cal
->fb_start
);
4871 free_busy_base64
= sipe_cal_get_freebusy_base64(cal
->free_busy
);
4873 /* we will rebuplish the same data to refresh publication time,
4874 * so if data from multiple sources, most recent will be choosen
4876 // st = publication_cal_300 ? publication_cal_300->fb_start_str : NULL;
4877 // fb = publication_cal_300 ? publication_cal_300->free_busy_base64 : NULL;
4879 //if (sipe_strequal(st, fb_start_str) && sipe_strequal(fb, free_busy_base64))
4881 // SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_free_busy: FreeBusy has NOT changed. Exiting.");
4882 // g_free(fb_start_str);
4883 // g_free(free_busy_base64);
4884 // return NULL; /* nothing to update */
4887 res
= g_strdup_printf(SIPE_PUB_XML_FREE_BUSY
,
4890 publication_cal_1
? publication_cal_1
->version
: 0,
4893 publication_cal_100
? publication_cal_100
->version
: 0,
4896 publication_cal_200
? publication_cal_200
->version
: 0,
4902 publication_cal_300
? publication_cal_300
->version
: 0,
4906 /* 400 - Personal */
4908 publication_cal_400
? publication_cal_400
->version
: 0,
4912 /* 32000 - Blocked */
4914 publication_cal_32000
? publication_cal_32000
->version
: 0
4917 g_free(fb_start_str
);
4918 g_free(free_busy_base64
);
4922 static void send_presence_publish(struct sipe_core_private
*sipe_private
,
4923 const char *publications
)
4930 uri
= sip_uri_self(sipe_private
);
4931 doc
= g_strdup_printf(SIPE_SEND_PRESENCE
,
4935 tmp
= get_contact(sipe_private
);
4936 hdr
= g_strdup_printf("Contact: %s\r\n"
4937 "Content-Type: application/msrtc-category-publish+xml\r\n", tmp
);
4939 sip_transport_service(sipe_private
,
4943 process_send_presence_category_publish_response
);
4952 send_publish_category_initial(struct sipe_core_private
*sipe_private
)
4954 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
4955 gchar
*pub_device
= sipe_publish_get_category_device(sipe_private
);
4957 gchar
*publications
;
4959 g_free(sip
->status
);
4960 sip
->status
= g_strdup(SIPE_STATUS_ID_AVAILABLE
); /* our initial state */
4962 pub_machine
= sipe_publish_get_category_state_machine(sipe_private
);
4963 publications
= g_strdup_printf("%s%s",
4965 pub_machine
? pub_machine
: "");
4967 g_free(pub_machine
);
4969 send_presence_publish(sipe_private
, publications
);
4970 g_free(publications
);
4974 send_presence_category_publish(struct sipe_core_private
*sipe_private
)
4976 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
4977 gchar
*pub_state
= sipe_is_user_state(sipe_private
) ?
4978 sipe_publish_get_category_state_user(sipe_private
) :
4979 sipe_publish_get_category_state_machine(sipe_private
);
4980 gchar
*pub_note
= sipe_publish_get_category_note(sipe_private
,
4982 sip
->is_oof_note
? "OOF" : "personal",
4985 gchar
*publications
;
4987 if (!pub_state
&& !pub_note
) {
4988 SIPE_DEBUG_INFO_NOFORMAT("send_presence_category_publish: nothing has changed. Exiting.");
4992 publications
= g_strdup_printf("%s%s",
4993 pub_state
? pub_state
: "",
4994 pub_note
? pub_note
: "");
4999 send_presence_publish(sipe_private
, publications
);
5000 g_free(publications
);
5004 * Publishes self status
5005 * based on own calendar information.
5010 publish_calendar_status_self(struct sipe_core_private
*sipe_private
,
5011 SIPE_UNUSED_PARAMETER
void *unused
)
5013 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
5014 struct sipe_cal_event
* event
= NULL
;
5015 gchar
*pub_cal_working_hours
= NULL
;
5016 gchar
*pub_cal_free_busy
= NULL
;
5017 gchar
*pub_calendar
= NULL
;
5018 gchar
*pub_calendar2
= NULL
;
5019 gchar
*pub_oof_note
= NULL
;
5020 const gchar
*oof_note
;
5021 time_t oof_start
= 0;
5025 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self() no calendar data.");
5029 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self() started.");
5030 if (sip
->cal
->cal_events
) {
5031 event
= sipe_cal_get_event(sip
->cal
->cal_events
, time(NULL
));
5035 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self: current event is NULL");
5037 char *desc
= sipe_cal_event_describe(event
);
5038 SIPE_DEBUG_INFO("publish_calendar_status_self: current event is:\n%s", desc
? desc
: "");
5044 OOF publish, Busy clean
5046 OOF clean, Busy publish
5048 OOF clean, Busy clean
5050 if (event
&& event
->cal_status
== SIPE_CAL_OOF
) {
5051 pub_calendar
= sipe_publish_get_category_state_calendar(sipe_private
, event
, sip
->cal
->email
, SIPE_CAL_OOF
);
5052 pub_calendar2
= sipe_publish_get_category_state_calendar(sipe_private
, NULL
, sip
->cal
->email
, SIPE_CAL_BUSY
);
5053 } else if (event
&& event
->cal_status
== SIPE_CAL_BUSY
) {
5054 pub_calendar
= sipe_publish_get_category_state_calendar(sipe_private
, NULL
, sip
->cal
->email
, SIPE_CAL_OOF
);
5055 pub_calendar2
= sipe_publish_get_category_state_calendar(sipe_private
, event
, sip
->cal
->email
, SIPE_CAL_BUSY
);
5057 pub_calendar
= sipe_publish_get_category_state_calendar(sipe_private
, NULL
, sip
->cal
->email
, SIPE_CAL_OOF
);
5058 pub_calendar2
= sipe_publish_get_category_state_calendar(sipe_private
, NULL
, sip
->cal
->email
, SIPE_CAL_BUSY
);
5061 oof_note
= sipe_ews_get_oof_note(sip
->cal
);
5062 if (sipe_strequal("Scheduled", sip
->cal
->oof_state
)) {
5063 oof_start
= sip
->cal
->oof_start
;
5064 oof_end
= sip
->cal
->oof_end
;
5066 pub_oof_note
= sipe_publish_get_category_note(sipe_private
, oof_note
, "OOF", oof_start
, oof_end
);
5068 pub_cal_working_hours
= sipe_publish_get_category_cal_working_hours(sipe_private
);
5069 pub_cal_free_busy
= sipe_publish_get_category_cal_free_busy(sipe_private
);
5071 if (!pub_cal_working_hours
&& !pub_cal_free_busy
&& !pub_calendar
&& !pub_calendar2
&& !pub_oof_note
) {
5072 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self: nothing has changed.");
5074 gchar
*publications
= g_strdup_printf("%s%s%s%s%s",
5075 pub_cal_working_hours
? pub_cal_working_hours
: "",
5076 pub_cal_free_busy
? pub_cal_free_busy
: "",
5077 pub_calendar
? pub_calendar
: "",
5078 pub_calendar2
? pub_calendar2
: "",
5079 pub_oof_note
? pub_oof_note
: "");
5081 send_presence_publish(sipe_private
, publications
);
5082 g_free(publications
);
5085 g_free(pub_cal_working_hours
);
5086 g_free(pub_cal_free_busy
);
5087 g_free(pub_calendar
);
5088 g_free(pub_calendar2
);
5089 g_free(pub_oof_note
);
5091 /* repeat scheduling */
5092 sipe_sched_calendar_status_self_publish(sipe_private
, time(NULL
));
5095 static void send_presence_status(struct sipe_core_private
*sipe_private
,
5096 SIPE_UNUSED_PARAMETER
void *unused
)
5098 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
5099 PurpleStatus
* status
= purple_account_get_active_status(sip
->account
);
5101 if (!status
) return;
5103 SIPE_DEBUG_INFO("send_presence_status: status: %s (%s)",
5104 purple_status_get_id(status
) ? purple_status_get_id(status
) : "",
5105 sipe_is_user_state(sipe_private
) ? "USER" : "MACHINE");
5107 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
5108 send_presence_category_publish(sipe_private
);
5110 send_presence_soap(sipe_private
, FALSE
);
5114 static guint
sipe_ht_hash_nick(const char *nick
)
5116 char *lc
= g_utf8_strdown(nick
, -1);
5117 guint bucket
= g_str_hash(lc
);
5123 static gboolean
sipe_ht_equals_nick(const char *nick1
, const char *nick2
)
5125 char *nick1_norm
= NULL
;
5126 char *nick2_norm
= NULL
;
5129 if (nick1
== NULL
&& nick2
== NULL
) return TRUE
;
5130 if (nick1
== NULL
|| nick2
== NULL
||
5131 !g_utf8_validate(nick1
, -1, NULL
) ||
5132 !g_utf8_validate(nick2
, -1, NULL
)) return FALSE
;
5134 nick1_norm
= g_utf8_casefold(nick1
, -1);
5135 nick2_norm
= g_utf8_casefold(nick2
, -1);
5136 equal
= g_utf8_collate(nick1_norm
, nick2_norm
) == 0;
5143 /* temporary function */
5144 void sipe_purple_setup(struct sipe_core_public
*sipe_public
,
5145 PurpleConnection
*gc
)
5147 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA
;
5149 sip
->account
= purple_connection_get_account(gc
);
5152 struct sipe_core_public
*sipe_core_allocate(const gchar
*signin_name
,
5153 const gchar
*login_domain
,
5154 const gchar
*login_account
,
5155 const gchar
*password
,
5157 const gchar
*email_url
,
5158 const gchar
**errmsg
)
5160 struct sipe_core_private
*sipe_private
;
5161 struct sipe_account_data
*sip
;
5162 gchar
**user_domain
;
5164 SIPE_DEBUG_INFO("sipe_core_allocate: signin_name '%s'", signin_name
);
5166 /* ensure that sign-in name doesn't contain invalid characters */
5167 if (strpbrk(signin_name
, "\t\v\r\n") != NULL
) {
5168 *errmsg
= _("SIP Exchange user name contains invalid characters");
5172 /* ensure that sign-in name format is name@domain */
5173 if (!strchr(signin_name
, '@') ||
5174 g_str_has_prefix(signin_name
, "@") ||
5175 g_str_has_suffix(signin_name
, "@")) {
5176 *errmsg
= _("User name should be a valid SIP URI\nExample: user@company.com");
5180 /* ensure that email format is name@domain (if provided) */
5181 if (!is_empty(email
) &&
5182 (!strchr(email
, '@') ||
5183 g_str_has_prefix(email
, "@") ||
5184 g_str_has_suffix(email
, "@")))
5186 *errmsg
= _("Email address should be valid if provided\nExample: user@company.com");
5190 /* ensure that user name doesn't contain spaces */
5191 user_domain
= g_strsplit(signin_name
, "@", 2);
5192 SIPE_DEBUG_INFO("sipe_core_allocate: user '%s' domain '%s'", user_domain
[0], user_domain
[1]);
5193 if (strchr(user_domain
[0], ' ') != NULL
) {
5194 g_strfreev(user_domain
);
5195 *errmsg
= _("SIP Exchange user name contains whitespace");
5199 /* ensure that email_url is in proper format if enabled (if provided).
5200 * Example (Exchange): https://server.company.com/EWS/Exchange.asmx
5201 * Example (Domino) : https://[domino_server]/[mail_database_name].nsf
5203 if (!is_empty(email_url
)) {
5204 char *tmp
= g_ascii_strdown(email_url
, -1);
5205 if (!g_str_has_prefix(tmp
, "https://"))
5208 g_strfreev(user_domain
);
5209 *errmsg
= _("Email services URL should be valid if provided\n"
5210 "Example: https://exchange.corp.com/EWS/Exchange.asmx\n"
5211 "Example: https://domino.corp.com/maildatabase.nsf");
5217 sipe_private
= g_new0(struct sipe_core_private
, 1);
5218 sipe_private
->temporary
= sip
= g_new0(struct sipe_account_data
, 1);
5219 sip
->subscribed_buddies
= FALSE
;
5220 sip
->initial_state_published
= FALSE
;
5221 sipe_private
->username
= g_strdup(signin_name
);
5222 sip
->email
= is_empty(email
) ? g_strdup(signin_name
) : g_strdup(email
);
5223 sip
->authdomain
= is_empty(login_domain
) ? NULL
: g_strdup(login_domain
);
5224 sip
->authuser
= is_empty(login_account
) ? NULL
: g_strdup(login_account
);
5225 sip
->password
= g_strdup(password
);
5226 sipe_private
->public.sip_name
= g_strdup(user_domain
[0]);
5227 sipe_private
->public.sip_domain
= g_strdup(user_domain
[1]);
5228 g_strfreev(user_domain
);
5230 sipe_private
->buddies
= g_hash_table_new((GHashFunc
)sipe_ht_hash_nick
, (GEqualFunc
)sipe_ht_equals_nick
);
5231 sip
->our_publications
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
5232 g_free
, (GDestroyNotify
)g_hash_table_destroy
);
5233 sipe_subscriptions_init(sipe_private
);
5234 sip
->status
= g_strdup(SIPE_STATUS_ID_UNKNOWN
);
5236 return((struct sipe_core_public
*)sipe_private
);
5240 sipe_blist_menu_free_containers(struct sipe_core_private
*sipe_private
);
5242 void sipe_connection_cleanup(struct sipe_core_private
*sipe_private
)
5244 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
5246 g_free(sipe_private
->epid
);
5247 sipe_private
->epid
= NULL
;
5249 sip_transport_disconnect(sipe_private
);
5251 sipe_schedule_cancel_all(sipe_private
);
5253 if (sip
->allow_events
) {
5254 GSList
*entry
= sip
->allow_events
;
5256 g_free(entry
->data
);
5257 entry
= entry
->next
;
5260 g_slist_free(sip
->allow_events
);
5262 if (sip
->containers
) {
5263 GSList
*entry
= sip
->containers
;
5265 free_container((struct sipe_container
*)entry
->data
);
5266 entry
= entry
->next
;
5269 g_slist_free(sip
->containers
);
5271 /* libpurple memory leak workaround */
5272 sipe_blist_menu_free_containers(sipe_private
);
5274 if (sipe_private
->contact
)
5275 g_free(sipe_private
->contact
);
5276 sipe_private
->contact
= NULL
;
5278 g_free(sip
->regcallid
);
5279 sip
->regcallid
= NULL
;
5281 if (sipe_private
->focus_factory_uri
)
5282 g_free(sipe_private
->focus_factory_uri
);
5283 sipe_private
->focus_factory_uri
= NULL
;
5286 sipe_cal_calendar_free(sip
->cal
);
5290 sipe_groupchat_free(sipe_private
);
5294 * A callback for g_hash_table_foreach_remove
5296 static gboolean
sipe_buddy_remove(SIPE_UNUSED_PARAMETER gpointer key
, gpointer buddy
,
5297 SIPE_UNUSED_PARAMETER gpointer user_data
)
5299 sipe_free_buddy((struct sipe_buddy
*) buddy
);
5301 /* We must return TRUE as the key/value have already been deleted */
5305 void sipe_buddy_free_all(struct sipe_core_private
*sipe_private
)
5307 g_hash_table_foreach_steal(sipe_private
->buddies
, sipe_buddy_remove
, NULL
);
5310 static void sipe_searchresults_im_buddy(PurpleConnection
*gc
, GList
*row
,
5311 SIPE_UNUSED_PARAMETER
void *user_data
)
5313 PurpleAccount
*acct
= purple_connection_get_account(gc
);
5314 char *id
= sip_uri_from_name((gchar
*)g_list_nth_data(row
, 0));
5315 PurpleConversation
*conv
= purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM
, id
, acct
);
5317 conv
= purple_conversation_new(PURPLE_CONV_TYPE_IM
, acct
, id
);
5318 purple_conversation_present(conv
);
5322 static void sipe_searchresults_add_buddy(PurpleConnection
*gc
, GList
*row
,
5323 SIPE_UNUSED_PARAMETER
void *user_data
)
5326 purple_blist_request_add_buddy(purple_connection_get_account(gc
),
5327 g_list_nth_data(row
, 0), _("Other Contacts"), g_list_nth_data(row
, 1));
5330 static gboolean
process_search_contact_response(struct sipe_core_private
*sipe_private
,
5332 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
5334 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
5335 PurpleNotifySearchResults
*results
;
5336 PurpleNotifySearchColumn
*column
;
5337 sipe_xml
*searchResults
;
5338 const sipe_xml
*mrow
;
5339 int match_count
= 0;
5340 gboolean more
= FALSE
;
5343 SIPE_DEBUG_INFO("process_search_contact_response: body:\n%s", msg
->body
? msg
->body
: "");
5345 searchResults
= sipe_xml_parse(msg
->body
, msg
->bodylen
);
5346 if (!searchResults
) {
5347 SIPE_DEBUG_INFO_NOFORMAT("process_search_contact_response: no parseable searchResults");
5351 results
= purple_notify_searchresults_new();
5353 if (results
== NULL
) {
5354 SIPE_DEBUG_ERROR_NOFORMAT("purple_parse_searchreply: Unable to display the search results.");
5355 purple_notify_error(sip
->gc
, NULL
, _("Unable to display the search results"), NULL
);
5357 sipe_xml_free(searchResults
);
5361 column
= purple_notify_searchresults_column_new(_("User name"));
5362 purple_notify_searchresults_column_add(results
, column
);
5364 column
= purple_notify_searchresults_column_new(_("Name"));
5365 purple_notify_searchresults_column_add(results
, column
);
5367 column
= purple_notify_searchresults_column_new(_("Company"));
5368 purple_notify_searchresults_column_add(results
, column
);
5370 column
= purple_notify_searchresults_column_new(_("Country"));
5371 purple_notify_searchresults_column_add(results
, column
);
5373 column
= purple_notify_searchresults_column_new(_("Email"));
5374 purple_notify_searchresults_column_add(results
, column
);
5376 for (mrow
= sipe_xml_child(searchResults
, "Body/Array/row"); mrow
; mrow
= sipe_xml_twin(mrow
)) {
5379 gchar
**uri_parts
= g_strsplit(sipe_xml_attribute(mrow
, "uri"), ":", 2);
5380 row
= g_list_append(row
, g_strdup(uri_parts
[1]));
5381 g_strfreev(uri_parts
);
5383 row
= g_list_append(row
, g_strdup(sipe_xml_attribute(mrow
, "displayName")));
5384 row
= g_list_append(row
, g_strdup(sipe_xml_attribute(mrow
, "company")));
5385 row
= g_list_append(row
, g_strdup(sipe_xml_attribute(mrow
, "country")));
5386 row
= g_list_append(row
, g_strdup(sipe_xml_attribute(mrow
, "email")));
5388 purple_notify_searchresults_row_add(results
, row
);
5392 if ((mrow
= sipe_xml_child(searchResults
, "Body/directorySearch/moreAvailable")) != NULL
) {
5393 char *data
= sipe_xml_data(mrow
);
5394 more
= (g_strcasecmp(data
, "true") == 0);
5398 secondary
= g_strdup_printf(
5399 dngettext(PACKAGE_NAME
,
5400 "Found %d contact%s:",
5401 "Found %d contacts%s:", match_count
),
5402 match_count
, more
? _(" (more matched your query)") : "");
5404 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_IM
, sipe_searchresults_im_buddy
);
5405 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_ADD
, sipe_searchresults_add_buddy
);
5406 purple_notify_searchresults(sip
->gc
, NULL
, NULL
, secondary
, results
, NULL
, NULL
);
5409 sipe_xml_free(searchResults
);
5413 void sipe_search_contact_with_cb(PurpleConnection
*gc
, PurpleRequestFields
*fields
)
5415 GList
*entries
= purple_request_field_group_get_fields(purple_request_fields_get_groups(fields
)->data
);
5416 gchar
**attrs
= g_new(gchar
*, g_list_length(entries
) + 1);
5422 PurpleRequestField
*field
= entries
->data
;
5423 const char *id
= purple_request_field_get_id(field
);
5424 const char *value
= purple_request_field_string_get_value(field
);
5426 SIPE_DEBUG_INFO("sipe_search_contact_with_cb: %s = '%s'", id
, value
? value
: "");
5428 if (value
!= NULL
) attrs
[i
++] = g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW
, id
, value
);
5429 } while ((entries
= g_list_next(entries
)) != NULL
);
5433 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
5434 gchar
*domain_uri
= sip_uri_from_name(sipe_private
->public.sip_domain
);
5435 gchar
*query
= g_strjoinv(NULL
, attrs
);
5436 gchar
*body
= g_strdup_printf(SIPE_SOAP_SEARCH_CONTACT
, 100, query
);
5437 SIPE_DEBUG_INFO("sipe_search_contact_with_cb: body:\n%s", body
? body
: "");
5438 send_soap_request_with_cb(sipe_private
, domain_uri
, body
,
5439 process_search_contact_response
, NULL
);
5448 static void sipe_publish_get_cat_state_user_to_clear(SIPE_UNUSED_PARAMETER
const char *name
,
5452 struct sipe_publication
*publication
= value
;
5454 g_string_append_printf( str
,
5455 SIPE_PUB_XML_PUBLICATION_CLEAR
,
5456 publication
->category
,
5457 publication
->instance
,
5458 publication
->container
,
5459 publication
->version
,
5463 void sipe_core_reset_status(struct sipe_core_public
*sipe_public
)
5465 struct sipe_core_private
*sipe_private
= SIPE_CORE_PRIVATE
;
5466 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA
;
5467 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) /* 2007+ */
5469 GString
* str
= g_string_new(NULL
);
5470 gchar
*publications
;
5472 if (!sip
->user_state_publications
|| g_hash_table_size(sip
->user_state_publications
) == 0) {
5473 SIPE_DEBUG_INFO_NOFORMAT("sipe_reset_status: no userState publications, exiting.");
5477 g_hash_table_foreach(sip
->user_state_publications
, (GHFunc
)sipe_publish_get_cat_state_user_to_clear
, str
);
5478 publications
= g_string_free(str
, FALSE
);
5480 send_presence_publish(sipe_private
, publications
);
5481 g_free(publications
);
5485 send_presence_soap0(sipe_private
, FALSE
, TRUE
);
5489 /** for Access levels menu */
5490 #define INDENT_FMT " %s"
5492 /** Member is directly placed to access level container.
5493 * For example SIP URI of user is in the container.
5495 #define INDENT_MARKED_FMT "* %s"
5497 /** Member is indirectly belong to access level container.
5498 * For example 'sameEnterprise' is in the container and user
5499 * belongs to that same enterprise.
5501 #define INDENT_MARKED_INHERITED_FMT "= %s"
5503 GSList
*sipe_core_buddy_info(struct sipe_core_public
*sipe_public
,
5505 const gchar
*status_name
,
5508 struct sipe_core_private
*sipe_private
= SIPE_CORE_PRIVATE
;
5510 gboolean is_oof_note
= FALSE
;
5511 const gchar
*activity
= NULL
;
5512 gchar
*calendar
= NULL
;
5513 const gchar
*meeting_subject
= NULL
;
5514 const gchar
*meeting_location
= NULL
;
5515 gchar
*access_text
= NULL
;
5516 GSList
*info
= NULL
;
5518 #define SIPE_ADD_BUDDY_INFO_COMMON(l, t) \
5520 struct sipe_buddy_info *sbi = g_malloc(sizeof(struct sipe_buddy_info)); \
5523 info = g_slist_append(info, sbi); \
5525 #define SIPE_ADD_BUDDY_INFO(l, t) SIPE_ADD_BUDDY_INFO_COMMON((l), g_markup_escape_text((t), -1))
5526 #define SIPE_ADD_BUDDY_INFO_NOESCAPE(l, t) SIPE_ADD_BUDDY_INFO_COMMON((l), (t))
5528 if (sipe_public
) { //happens on pidgin exit
5529 struct sipe_buddy
*sbuddy
= g_hash_table_lookup(sipe_private
->buddies
, name
);
5531 note
= sbuddy
->note
;
5532 is_oof_note
= sbuddy
->is_oof_note
;
5533 activity
= sbuddy
->activity
;
5534 calendar
= sipe_cal_get_description(sbuddy
);
5535 meeting_subject
= sbuddy
->meeting_subject
;
5536 meeting_location
= sbuddy
->meeting_location
;
5538 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
5539 gboolean is_group_access
= FALSE
;
5540 const int container_id
= sipe_find_access_level(sipe_private
, "user", sipe_get_no_sip_uri(name
), &is_group_access
);
5541 const char *access_level
= sipe_get_access_level_name(container_id
);
5542 access_text
= is_group_access
?
5543 g_strdup(access_level
) :
5544 g_strdup_printf(INDENT_MARKED_FMT
, access_level
);
5551 const gchar
*status_str
= activity
? activity
: status_name
;
5553 SIPE_ADD_BUDDY_INFO(_("Status"), status_str
);
5555 if (is_online
&& !is_empty(calendar
))
5557 SIPE_ADD_BUDDY_INFO(_("Calendar"), calendar
);
5560 if (!is_empty(meeting_location
))
5562 SIPE_DEBUG_INFO("sipe_tooltip_text: %s meeting location: '%s'", name
, meeting_location
);
5563 SIPE_ADD_BUDDY_INFO(_("Meeting in"), meeting_location
);
5565 if (!is_empty(meeting_subject
))
5567 SIPE_DEBUG_INFO("sipe_tooltip_text: %s meeting subject: '%s'", name
, meeting_subject
);
5568 SIPE_ADD_BUDDY_INFO(_("Meeting about"), meeting_subject
);
5572 SIPE_DEBUG_INFO("sipe_tooltip_text: %s note: '%s'", name
, note
);
5573 SIPE_ADD_BUDDY_INFO_NOESCAPE(is_oof_note
? _("Out of office note") : _("Note"),
5574 g_strdup_printf("<i>%s</i>", note
));
5577 SIPE_ADD_BUDDY_INFO(_("Access level"), access_text
);
5578 g_free(access_text
);
5584 static PurpleBuddy
*
5585 purple_blist_add_buddy_clone(PurpleGroup
* group
, PurpleBuddy
* buddy
)
5588 const gchar
*server_alias
, *email
;
5589 const PurpleStatus
*status
= purple_presence_get_active_status(purple_buddy_get_presence(buddy
));
5591 clone
= purple_buddy_new(buddy
->account
, buddy
->name
, buddy
->alias
);
5593 purple_blist_add_buddy(clone
, NULL
, group
, NULL
);
5595 server_alias
= purple_buddy_get_server_alias(buddy
);
5597 purple_blist_server_alias_buddy(clone
, server_alias
);
5600 email
= purple_blist_node_get_string(&buddy
->node
, EMAIL_PROP
);
5602 purple_blist_node_set_string(&clone
->node
, EMAIL_PROP
, email
);
5605 purple_presence_set_status_active(purple_buddy_get_presence(clone
), purple_status_get_id(status
), TRUE
);
5607 purple_prpl_got_user_status(clone
->account
, clone
->name
, purple_status_get_id(status
), NULL
);
5612 sipe_buddy_menu_copy_to_cb(PurpleBlistNode
*node
, const char *group_name
)
5614 PurpleBuddy
*buddy
, *b
;
5615 PurpleConnection
*gc
;
5616 PurpleGroup
* group
= purple_find_group(group_name
);
5618 g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node
));
5620 buddy
= (PurpleBuddy
*)node
;
5622 SIPE_DEBUG_INFO("sipe_buddy_menu_copy_to_cb: copying %s to %s", buddy
->name
, group_name
);
5623 gc
= purple_account_get_connection(buddy
->account
);
5625 b
= purple_find_buddy_in_group(buddy
->account
, buddy
->name
, group
);
5627 b
= purple_blist_add_buddy_clone(group
, buddy
);
5630 sipe_add_buddy(gc
, b
, group
);
5634 sipe_buddy_menu_chat_new_cb(PurpleBuddy
*buddy
)
5636 struct sipe_core_private
*sipe_private
= PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE
;
5638 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_new_cb: buddy->name=%s", buddy
->name
);
5640 /* 2007+ conference */
5641 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
))
5643 sipe_conf_add(sipe_private
, buddy
->name
);
5645 else /* 2005- multiparty chat */
5647 gchar
*self
= sip_uri_self(sipe_private
);
5648 struct sip_session
*session
;
5650 session
= sipe_session_add_chat(sipe_private
,
5654 session
->chat_session
->backend
= sipe_backend_chat_create(SIPE_CORE_PUBLIC
,
5655 session
->chat_session
,
5656 session
->chat_session
->title
,
5660 sipe_im_invite(sipe_private
, session
, buddy
->name
, NULL
, NULL
, NULL
, FALSE
);
5665 * For 2007+ conference only.
5668 sipe_buddy_menu_chat_make_leader_cb(PurpleBuddy
*buddy
,
5669 struct sipe_chat_session
*chat_session
)
5671 struct sipe_core_private
*sipe_private
= PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE
;
5672 struct sip_session
*session
;
5674 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_make_leader_cb: buddy->name=%s", buddy
->name
);
5675 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_make_leader_cb: chat_title=%s", chat_session
->title
);
5677 session
= sipe_session_find_chat(sipe_private
, chat_session
);
5679 sipe_conf_modify_user_role(sipe_private
, session
, buddy
->name
);
5683 * For 2007+ conference only.
5686 sipe_buddy_menu_chat_remove_cb(PurpleBuddy
*buddy
,
5687 struct sipe_chat_session
*chat_session
)
5689 struct sipe_core_private
*sipe_private
= PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE
;
5690 struct sip_session
*session
;
5692 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_remove_cb: buddy->name=%s", buddy
->name
);
5693 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_remove_cb: chat_title=%s", chat_session
->title
);
5695 session
= sipe_session_find_chat(sipe_private
, chat_session
);
5697 sipe_conf_delete_user(sipe_private
, session
, buddy
->name
);
5701 sipe_buddy_menu_chat_invite_cb(PurpleBuddy
*buddy
,
5702 struct sipe_chat_session
*chat_session
)
5704 struct sipe_core_private
*sipe_private
= PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE
;
5706 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_invite_cb: buddy->name=%s", buddy
->name
);
5707 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_invite_cb: chat_title=%s", chat_session
->title
);
5709 sipe_core_chat_invite(SIPE_CORE_PUBLIC
, chat_session
, buddy
->name
);
5713 sipe_buddy_menu_make_call_cb(PurpleBuddy
*buddy
, const char *phone
)
5715 struct sipe_core_private
*sipe_private
= PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE
;
5717 SIPE_DEBUG_INFO("sipe_buddy_menu_make_call_cb: buddy->name=%s", buddy
->name
);
5719 char *tel_uri
= sip_to_tel_uri(phone
);
5721 SIPE_DEBUG_INFO("sipe_buddy_menu_make_call_cb: going to call number: %s", tel_uri
? tel_uri
: "");
5722 sip_csta_make_call(sipe_private
, tel_uri
);
5729 sipe_buddy_menu_access_level_help_cb(PurpleBuddy
*buddy
)
5731 /** Translators: replace with URL to localized page
5732 * If it doesn't exist copy the original URL */
5733 purple_notify_uri(buddy
->account
->gc
, _("https://sourceforge.net/apps/mediawiki/sipe/index.php?title=Access_Levels"));
5737 sipe_buddy_menu_send_email_cb(PurpleBuddy
*buddy
)
5740 SIPE_DEBUG_INFO("sipe_buddy_menu_send_email_cb: buddy->name=%s", buddy
->name
);
5742 email
= purple_blist_node_get_string(&buddy
->node
, EMAIL_PROP
);
5745 char *command_line
= g_strdup_printf(
5751 " mailto:%s", email
);
5752 SIPE_DEBUG_INFO("sipe_buddy_menu_send_email_cb: going to call email client: %s", command_line
);
5754 g_spawn_command_line_async(command_line
, NULL
);
5755 g_free(command_line
);
5759 SIPE_DEBUG_INFO("sipe_buddy_menu_send_email_cb: no email address stored for buddy=%s", buddy
->name
);
5764 sipe_buddy_menu_access_level_cb(PurpleBuddy
*buddy
,
5765 struct sipe_container
*container
)
5767 struct sipe_core_private
*sipe_private
= PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE
;
5768 struct sipe_container_member
*member
;
5770 if (!container
|| !container
->members
) return;
5772 member
= ((struct sipe_container_member
*)container
->members
->data
);
5774 if (!member
->type
) return;
5776 SIPE_DEBUG_INFO("sipe_buddy_menu_access_level_cb: container->id=%d, member->type=%s, member->value=%s",
5777 container
->id
, member
->type
, member
->value
? member
->value
: "");
5779 sipe_change_access_level(sipe_private
, container
->id
, member
->type
, member
->value
);
5783 sipe_get_access_control_menu(struct sipe_core_private
*sipe_private
,
5787 * A menu which appear when right-clicking on buddy in contact list.
5790 sipe_buddy_menu(PurpleBuddy
*buddy
)
5792 PurpleBlistNode
*g_node
;
5793 PurpleGroup
*gr_parent
;
5794 PurpleMenuAction
*act
;
5796 GList
*menu_groups
= NULL
;
5797 struct sipe_core_private
*sipe_private
= PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE
;
5798 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
5800 gchar
*self
= sip_uri_self(sipe_private
);
5802 SIPE_SESSION_FOREACH
{
5803 if (!sipe_strcase_equal(self
, buddy
->name
) && session
->chat_session
)
5805 struct sipe_chat_session
*chat_session
= session
->chat_session
;
5806 gboolean is_conf
= (chat_session
->type
== SIPE_CHAT_TYPE_CONFERENCE
);
5808 if (sipe_backend_chat_find(chat_session
->backend
, buddy
->name
))
5810 gboolean conf_op
= sipe_backend_chat_is_operator(chat_session
->backend
, self
);
5813 && !sipe_backend_chat_is_operator(chat_session
->backend
, buddy
->name
) /* Not conf OP */
5814 && conf_op
) /* We are a conf OP */
5816 gchar
*label
= g_strdup_printf(_("Make leader of '%s'"),
5817 chat_session
->title
);
5818 act
= purple_menu_action_new(label
,
5819 PURPLE_CALLBACK(sipe_buddy_menu_chat_make_leader_cb
),
5820 chat_session
, NULL
);
5822 menu
= g_list_prepend(menu
, act
);
5826 && conf_op
) /* We are a conf OP */
5828 gchar
*label
= g_strdup_printf(_("Remove from '%s'"),
5829 chat_session
->title
);
5830 act
= purple_menu_action_new(label
,
5831 PURPLE_CALLBACK(sipe_buddy_menu_chat_remove_cb
),
5832 chat_session
, NULL
);
5834 menu
= g_list_prepend(menu
, act
);
5840 || (is_conf
&& !session
->locked
))
5842 gchar
*label
= g_strdup_printf(_("Invite to '%s'"),
5843 chat_session
->title
);
5844 act
= purple_menu_action_new(label
,
5845 PURPLE_CALLBACK(sipe_buddy_menu_chat_invite_cb
),
5846 chat_session
, NULL
);
5848 menu
= g_list_prepend(menu
, act
);
5852 } SIPE_SESSION_FOREACH_END
;
5854 act
= purple_menu_action_new(_("New chat"),
5855 PURPLE_CALLBACK(sipe_buddy_menu_chat_new_cb
),
5857 menu
= g_list_prepend(menu
, act
);
5859 if (sip
->csta
&& !sip
->csta
->line_status
) {
5861 const char *phone_disp_str
;
5864 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_PROP
);
5865 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_DISPLAY_PROP
);
5867 gchar
*label
= g_strdup_printf(_("Work %s"),
5868 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
5869 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
5873 menu
= g_list_prepend(menu
, act
);
5877 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_MOBILE_PROP
);
5878 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_MOBILE_DISPLAY_PROP
);
5880 gchar
*label
= g_strdup_printf(_("Mobile %s"),
5881 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
5882 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
5886 menu
= g_list_prepend(menu
, act
);
5890 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_HOME_PROP
);
5891 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_HOME_DISPLAY_PROP
);
5893 gchar
*label
= g_strdup_printf(_("Home %s"),
5894 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
5895 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
5899 menu
= g_list_prepend(menu
, act
);
5903 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_OTHER_PROP
);
5904 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_OTHER_DISPLAY_PROP
);
5906 gchar
*label
= g_strdup_printf(_("Other %s"),
5907 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
5908 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
5912 menu
= g_list_prepend(menu
, act
);
5916 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_CUSTOM1_PROP
);
5917 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_CUSTOM1_DISPLAY_PROP
);
5919 gchar
*label
= g_strdup_printf(_("Custom1 %s"),
5920 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
5921 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
5925 menu
= g_list_prepend(menu
, act
);
5929 email
= purple_blist_node_get_string(&buddy
->node
, EMAIL_PROP
);
5931 act
= purple_menu_action_new(_("Send email..."),
5932 PURPLE_CALLBACK(sipe_buddy_menu_send_email_cb
),
5934 menu
= g_list_prepend(menu
, act
);
5938 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
5939 GList
*menu_access_levels
= sipe_get_access_control_menu(sipe_private
, buddy
->name
);
5941 act
= purple_menu_action_new(_("Access level"),
5943 NULL
, menu_access_levels
);
5944 menu
= g_list_prepend(menu
, act
);
5948 gr_parent
= purple_buddy_get_group(buddy
);
5949 for (g_node
= purple_blist_get_root(); g_node
; g_node
= g_node
->next
) {
5952 if (g_node
->type
!= PURPLE_BLIST_GROUP_NODE
)
5955 group
= (PurpleGroup
*)g_node
;
5956 if (group
== gr_parent
)
5959 if (purple_find_buddy_in_group(buddy
->account
, buddy
->name
, group
))
5962 act
= purple_menu_action_new(purple_group_get_name(group
),
5963 PURPLE_CALLBACK(sipe_buddy_menu_copy_to_cb
),
5965 menu_groups
= g_list_prepend(menu_groups
, act
);
5967 menu_groups
= g_list_reverse(menu_groups
);
5969 act
= purple_menu_action_new(_("Copy to"),
5972 menu
= g_list_prepend(menu
, act
);
5974 menu
= g_list_reverse(menu
);
5981 sipe_ask_access_domain_cb(PurpleConnection
*gc
, PurpleRequestFields
*fields
)
5983 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
5984 const char *domain
= purple_request_fields_get_string(fields
, "access_domain");
5985 int index
= purple_request_fields_get_choice(fields
, "container_id");
5986 /* move Blocked first */
5987 int i
= (index
== 4) ? 0 : index
+ 1;
5988 int container_id
= containers
[i
];
5990 SIPE_DEBUG_INFO("sipe_ask_access_domain_cb: domain=%s, container_id=(%d)%d", domain
? domain
: "", index
, container_id
);
5992 sipe_change_access_level(sipe_private
, container_id
, "domain", domain
);
5996 sipe_ask_access_domain(struct sipe_core_private
*sipe_private
)
5998 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
5999 PurpleAccount
*account
= sip
->account
;
6000 PurpleConnection
*gc
= sip
->gc
;
6001 PurpleRequestFields
*fields
;
6002 PurpleRequestFieldGroup
*g
;
6003 PurpleRequestField
*f
;
6005 fields
= purple_request_fields_new();
6007 g
= purple_request_field_group_new(NULL
);
6008 f
= purple_request_field_string_new("access_domain", _("Domain"), "partner-company.com", FALSE
);
6009 purple_request_field_set_required(f
, TRUE
);
6010 purple_request_field_group_add_field(g
, f
);
6012 f
= purple_request_field_choice_new("container_id", _("Access level"), 0);
6013 purple_request_field_choice_add(f
, _("Personal")); /* index 0 */
6014 purple_request_field_choice_add(f
, _("Team"));
6015 purple_request_field_choice_add(f
, _("Company"));
6016 purple_request_field_choice_add(f
, _("Public"));
6017 purple_request_field_choice_add(f
, _("Blocked")); /* index 4 */
6018 purple_request_field_choice_set_default_value(f
, 3); /* index */
6019 purple_request_field_set_required(f
, TRUE
);
6020 purple_request_field_group_add_field(g
, f
);
6022 purple_request_fields_add_group(fields
, g
);
6024 purple_request_fields(gc
, _("Add new domain"),
6025 _("Add new domain"), NULL
, fields
,
6026 _("Add"), G_CALLBACK(sipe_ask_access_domain_cb
),
6028 account
, NULL
, NULL
, gc
);
6032 sipe_buddy_menu_access_level_add_domain_cb(PurpleBuddy
*buddy
)
6034 sipe_ask_access_domain(PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE
);
6038 * Workaround for missing libpurple API to release resources allocated
6039 * during blist_node_menu() callback. See also:
6041 * <http://developer.pidgin.im/ticket/12597>
6043 * We remember all memory blocks in a list and deallocate them when
6045 * - the next time we enter the callback, or
6046 * - the account is disconnected
6048 * That means that after the buddy menu has been closed we have unused
6049 * resources but at least we don't leak them anymore...
6052 sipe_blist_menu_free_containers(struct sipe_core_private
*sipe_private
)
6054 GSList
*entry
= sipe_private
->blist_menu_containers
;
6056 free_container(entry
->data
);
6057 entry
= entry
->next
;
6059 g_slist_free(sipe_private
->blist_menu_containers
);
6060 sipe_private
->blist_menu_containers
= NULL
;
6064 sipe_blist_menu_remember_container(struct sipe_core_private
*sipe_private
,
6065 struct sipe_container
*container
)
6067 sipe_private
->blist_menu_containers
= g_slist_prepend(sipe_private
->blist_menu_containers
,
6072 sipe_get_access_levels_menu(struct sipe_core_private
*sipe_private
,
6073 const char* member_type
,
6074 const char* member_value
,
6075 const gboolean extra_menu
)
6077 GList
*menu_access_levels
= NULL
;
6080 PurpleMenuAction
*act
;
6081 struct sipe_container
*container
;
6082 struct sipe_container_member
*member
;
6083 gboolean is_group_access
= FALSE
;
6084 int container_id
= sipe_find_access_level(sipe_private
, member_type
, member_value
, &is_group_access
);
6086 for (i
= 1; i
<= CONTAINERS_LEN
; i
++) {
6087 /* to put Blocked level last in menu list.
6088 * Blocked should remaim in the first place in the containers[] array.
6090 unsigned int j
= (i
== CONTAINERS_LEN
) ? 0 : i
;
6091 const char *acc_level_name
= sipe_get_access_level_name(containers
[j
]);
6093 container
= g_new0(struct sipe_container
, 1);
6094 member
= g_new0(struct sipe_container_member
, 1);
6095 container
->id
= containers
[j
];
6096 container
->members
= g_slist_append(container
->members
, member
);
6097 member
->type
= g_strdup(member_type
);
6098 member
->value
= g_strdup(member_value
);
6100 /* libpurple memory leak workaround */
6101 sipe_blist_menu_remember_container(sipe_private
, container
);
6103 /* current container/access level */
6104 if (((int)containers
[j
]) == container_id
) {
6105 menu_name
= is_group_access
?
6106 g_strdup_printf(INDENT_MARKED_INHERITED_FMT
, acc_level_name
) :
6107 g_strdup_printf(INDENT_MARKED_FMT
, acc_level_name
);
6109 menu_name
= g_strdup_printf(INDENT_FMT
, acc_level_name
);
6112 act
= purple_menu_action_new(menu_name
,
6113 PURPLE_CALLBACK(sipe_buddy_menu_access_level_cb
),
6116 menu_access_levels
= g_list_prepend(menu_access_levels
, act
);
6119 if (extra_menu
&& (container_id
>= 0)) {
6121 act
= purple_menu_action_new(" --------------", NULL
, NULL
, NULL
);
6122 menu_access_levels
= g_list_prepend(menu_access_levels
, act
);
6124 if (!is_group_access
) {
6125 container
= g_new0(struct sipe_container
, 1);
6126 member
= g_new0(struct sipe_container_member
, 1);
6128 container
->members
= g_slist_append(container
->members
, member
);
6129 member
->type
= g_strdup(member_type
);
6130 member
->value
= g_strdup(member_value
);
6132 /* libpurple memory leak workaround */
6133 sipe_blist_menu_remember_container(sipe_private
, container
);
6135 /* Translators: remove (clear) previously assigned access level */
6136 menu_name
= g_strdup_printf(INDENT_FMT
, _("Unspecify"));
6137 act
= purple_menu_action_new(menu_name
,
6138 PURPLE_CALLBACK(sipe_buddy_menu_access_level_cb
),
6141 menu_access_levels
= g_list_prepend(menu_access_levels
, act
);
6145 menu_access_levels
= g_list_reverse(menu_access_levels
);
6146 return menu_access_levels
;
6150 sipe_get_access_groups_menu(struct sipe_core_private
*sipe_private
)
6152 GList
*menu_access_groups
= NULL
;
6153 PurpleMenuAction
*act
;
6154 GSList
*access_domains
= NULL
;
6159 act
= purple_menu_action_new(_("People in my company"),
6161 NULL
, sipe_get_access_levels_menu(sipe_private
, "sameEnterprise", NULL
, FALSE
));
6162 menu_access_groups
= g_list_prepend(menu_access_groups
, act
);
6164 /* this is original name, don't edit */
6165 act
= purple_menu_action_new(_("People in domains connected with my company"),
6167 NULL
, sipe_get_access_levels_menu(sipe_private
, "federated", NULL
, FALSE
));
6168 menu_access_groups
= g_list_prepend(menu_access_groups
, act
);
6170 act
= purple_menu_action_new(_("People in public domains"),
6172 NULL
, sipe_get_access_levels_menu(sipe_private
, "publicCloud", NULL
, TRUE
));
6173 menu_access_groups
= g_list_prepend(menu_access_groups
, act
);
6175 access_domains
= sipe_get_access_domains(sipe_private
);
6176 entry
= access_domains
;
6178 domain
= entry
->data
;
6180 menu_name
= g_strdup_printf(_("People at %s"), domain
);
6181 act
= purple_menu_action_new(menu_name
,
6183 NULL
, sipe_get_access_levels_menu(sipe_private
, "domain", g_strdup(domain
), TRUE
));
6184 menu_access_groups
= g_list_prepend(menu_access_groups
, act
);
6187 entry
= entry
->next
;
6191 /* People in domains connected with my company */
6192 act
= purple_menu_action_new("-------------------------------------------", NULL
, NULL
, NULL
);
6193 menu_access_groups
= g_list_prepend(menu_access_groups
, act
);
6195 act
= purple_menu_action_new(_("Add new domain..."),
6196 PURPLE_CALLBACK(sipe_buddy_menu_access_level_add_domain_cb
),
6198 menu_access_groups
= g_list_prepend(menu_access_groups
, act
);
6200 menu_access_groups
= g_list_reverse(menu_access_groups
);
6202 return menu_access_groups
;
6206 sipe_get_access_control_menu(struct sipe_core_private
*sipe_private
,
6209 GList
*menu_access_levels
= NULL
;
6210 GList
*menu_access_groups
= NULL
;
6212 PurpleMenuAction
*act
;
6214 /* libpurple memory leak workaround */
6215 sipe_blist_menu_free_containers(sipe_private
);
6217 menu_access_levels
= sipe_get_access_levels_menu(sipe_private
, "user", sipe_get_no_sip_uri(uri
), TRUE
);
6219 menu_access_groups
= sipe_get_access_groups_menu(sipe_private
);
6221 menu_name
= g_strdup_printf(INDENT_FMT
, _("Access groups"));
6222 act
= purple_menu_action_new(menu_name
,
6224 NULL
, menu_access_groups
);
6226 menu_access_levels
= g_list_append(menu_access_levels
, act
);
6228 menu_name
= g_strdup_printf(INDENT_FMT
, _("Online help..."));
6229 act
= purple_menu_action_new(menu_name
,
6230 PURPLE_CALLBACK(sipe_buddy_menu_access_level_help_cb
),
6233 menu_access_levels
= g_list_append(menu_access_levels
, act
);
6235 return menu_access_levels
;
6239 process_get_info_response(struct sipe_core_private
*sipe_private
,
6240 struct sipmsg
*msg
, struct transaction
*trans
)
6242 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
6243 char *uri
= trans
->payload
->data
;
6245 PurpleNotifyUserInfo
*info
;
6246 PurpleBuddy
*pbuddy
= NULL
;
6247 struct sipe_buddy
*sbuddy
;
6248 const char *alias
= NULL
;
6249 char *device_name
= NULL
;
6250 char *server_alias
= NULL
;
6251 char *phone_number
= NULL
;
6254 char *first_name
= NULL
;
6255 char *last_name
= NULL
;
6257 if (!sip
) return FALSE
;
6259 SIPE_DEBUG_INFO("Fetching %s's user info for %s", uri
, sipe_private
->username
);
6261 pbuddy
= purple_find_buddy((PurpleAccount
*)sip
->account
, uri
);
6262 alias
= purple_buddy_get_local_alias(pbuddy
);
6264 //will query buddy UA's capabilities and send answer to log
6265 sipe_options_request(sipe_private
, uri
);
6267 sbuddy
= g_hash_table_lookup(sipe_private
->buddies
, uri
);
6269 device_name
= sbuddy
->device_name
? g_strdup(sbuddy
->device_name
) : NULL
;
6272 info
= purple_notify_user_info_new();
6274 if (msg
->response
!= 200) {
6275 SIPE_DEBUG_INFO("process_get_info_response: SERVICE response is %d", msg
->response
);
6277 sipe_xml
*searchResults
;
6278 const sipe_xml
*mrow
;
6280 SIPE_DEBUG_INFO("process_get_info_response: body:\n%s", msg
->body
? msg
->body
: "");
6281 searchResults
= sipe_xml_parse(msg
->body
, msg
->bodylen
);
6282 if (!searchResults
) {
6283 SIPE_DEBUG_INFO_NOFORMAT("process_get_info_response: no parseable searchResults");
6284 } else if ((mrow
= sipe_xml_child(searchResults
, "Body/Array/row"))) {
6286 server_alias
= g_strdup(sipe_xml_attribute(mrow
, "displayName"));
6287 email
= g_strdup(sipe_xml_attribute(mrow
, "email"));
6288 phone_number
= g_strdup(sipe_xml_attribute(mrow
, "phone"));
6290 /* For 2007 system we will take this from ContactCard -
6291 * it has cleaner tel: URIs at least
6293 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
6294 char *tel_uri
= sip_to_tel_uri(phone_number
);
6295 /* trims its parameters, so call first */
6296 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_DISPLAY_NAME
, server_alias
);
6297 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_EMAIL
, email
);
6298 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_WORK_PHONE
, tel_uri
);
6299 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY
, phone_number
);
6303 if (server_alias
&& strlen(server_alias
) > 0) {
6304 purple_notify_user_info_add_pair(info
, _("Display name"), server_alias
);
6306 if ((value
= sipe_xml_attribute(mrow
, "title")) && strlen(value
) > 0) {
6307 purple_notify_user_info_add_pair(info
, _("Job title"), value
);
6309 if ((value
= sipe_xml_attribute(mrow
, "office")) && strlen(value
) > 0) {
6310 purple_notify_user_info_add_pair(info
, _("Office"), value
);
6312 if (phone_number
&& strlen(phone_number
) > 0) {
6313 purple_notify_user_info_add_pair(info
, _("Business phone"), phone_number
);
6315 if ((value
= sipe_xml_attribute(mrow
, "company")) && strlen(value
) > 0) {
6316 purple_notify_user_info_add_pair(info
, _("Company"), value
);
6318 if ((value
= sipe_xml_attribute(mrow
, "city")) && strlen(value
) > 0) {
6319 purple_notify_user_info_add_pair(info
, _("City"), value
);
6321 if ((value
= sipe_xml_attribute(mrow
, "state")) && strlen(value
) > 0) {
6322 purple_notify_user_info_add_pair(info
, _("State"), value
);
6324 if ((value
= sipe_xml_attribute(mrow
, "country")) && strlen(value
) > 0) {
6325 purple_notify_user_info_add_pair(info
, _("Country"), value
);
6327 if (email
&& strlen(email
) > 0) {
6328 purple_notify_user_info_add_pair(info
, _("Email address"), email
);
6332 sipe_xml_free(searchResults
);
6335 purple_notify_user_info_add_section_break(info
);
6337 if (is_empty(server_alias
)) {
6338 g_free(server_alias
);
6339 server_alias
= g_strdup(purple_buddy_get_server_alias(pbuddy
));
6341 purple_notify_user_info_add_pair(info
, _("Display name"), server_alias
);
6345 /* present alias if it differs from server alias */
6346 if (alias
&& !sipe_strequal(alias
, server_alias
))
6348 purple_notify_user_info_add_pair(info
, _("Alias"), alias
);
6351 if (is_empty(email
)) {
6353 email
= g_strdup(purple_blist_node_get_string(&pbuddy
->node
, EMAIL_PROP
));
6355 purple_notify_user_info_add_pair(info
, _("Email address"), email
);
6359 site
= purple_blist_node_get_string(&pbuddy
->node
, SITE_PROP
);
6361 purple_notify_user_info_add_pair(info
, _("Site"), site
);
6364 sipe_get_first_last_names(sipe_private
, uri
, &first_name
, &last_name
);
6365 if (first_name
&& last_name
) {
6366 char *link
= g_strconcat("http://www.linkedin.com/pub/dir/", first_name
, "/", last_name
, NULL
);
6368 purple_notify_user_info_add_pair(info
, _("Find on LinkedIn"), link
);
6375 purple_notify_user_info_add_pair(info
, _("Device"), device_name
);
6378 /* show a buddy's user info in a nice dialog box */
6379 purple_notify_userinfo(sip
->gc
, /* connection the buddy info came through */
6380 uri
, /* buddy's URI */
6382 NULL
, /* callback called when dialog closed */
6383 NULL
); /* userdata for callback */
6385 g_free(phone_number
);
6386 g_free(server_alias
);
6388 g_free(device_name
);
6394 * AD search first, LDAP based
6396 void sipe_get_info(PurpleConnection
*gc
, const char *username
)
6398 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
6399 gchar
*domain_uri
= sip_uri_from_name(sipe_private
->public.sip_domain
);
6400 char *row
= g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW
, "msRTCSIP-PrimaryUserAddress", username
);
6401 gchar
*body
= g_strdup_printf(SIPE_SOAP_SEARCH_CONTACT
, 1, row
);
6402 struct transaction_payload
*payload
= g_new0(struct transaction_payload
, 1);
6404 payload
->destroy
= g_free
;
6405 payload
->data
= g_strdup(username
);
6407 SIPE_DEBUG_INFO("sipe_get_contact_data: body:\n%s", body
? body
: "");
6408 send_soap_request_with_cb(sipe_private
, domain_uri
, body
,
6409 process_get_info_response
, payload
);