6 * Copyright (C) 2010 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
49 #include "sipe-common.h"
53 #include "connection.h"
54 #include "conversation.h"
60 #include "savedstatuses.h"
62 #include "core-depurple.h" /* Temporary for the core de-purple transition */
64 #include "http-conn.h"
67 #include "sip-transport.h"
68 #include "sipe-backend.h"
69 #include "sipe-buddy.h"
71 #include "sipe-chat.h"
72 #include "sipe-conf.h"
73 #include "sipe-core.h"
74 #include "sipe-core-private.h"
75 #include "sipe-dialog.h"
77 #include "sipe-domino.h"
79 #include "sipe-mime.h"
81 #include "sipe-schedule.h"
82 #include "sipe-session.h"
83 #include "sipe-subscriptions.h"
84 #include "sipe-media.h"
85 #include "sipe-utils.h"
90 #define SIPE_IDLE_SET_DELAY 1 /* 1 sec */
92 #define UPDATE_CALENDAR_DELAY 1*60 /* 1 min */
93 #define UPDATE_CALENDAR_INTERVAL 30*60 /* 30 min */
95 /* Status identifiers (see also: sipe_status_types()) */
96 #define SIPE_STATUS_ID_UNKNOWN purple_primitive_get_id_from_type(PURPLE_STATUS_UNSET) /* Unset (primitive) */
97 #define SIPE_STATUS_ID_OFFLINE purple_primitive_get_id_from_type(PURPLE_STATUS_OFFLINE) /* Offline (primitive) */
98 #define SIPE_STATUS_ID_AVAILABLE purple_primitive_get_id_from_type(PURPLE_STATUS_AVAILABLE) /* Online */
99 /* PURPLE_STATUS_UNAVAILABLE: */
100 #define SIPE_STATUS_ID_BUSY "busy" /* Busy */
101 #define SIPE_STATUS_ID_BUSYIDLE "busyidle" /* BusyIdle */
102 #define SIPE_STATUS_ID_DND "do-not-disturb" /* Do Not Disturb */
103 #define SIPE_STATUS_ID_IN_MEETING "in-a-meeting" /* In a meeting */
104 #define SIPE_STATUS_ID_IN_CONF "in-a-conference" /* In a conference */
105 #define SIPE_STATUS_ID_ON_PHONE "on-the-phone" /* On the phone */
106 #define SIPE_STATUS_ID_INVISIBLE purple_primitive_get_id_from_type(PURPLE_STATUS_INVISIBLE) /* Appear Offline */
107 /* PURPLE_STATUS_AWAY: */
108 #define SIPE_STATUS_ID_IDLE "idle" /* Idle/Inactive */
109 #define SIPE_STATUS_ID_BRB "be-right-back" /* Be Right Back */
110 #define SIPE_STATUS_ID_AWAY purple_primitive_get_id_from_type(PURPLE_STATUS_AWAY) /* Away (primitive) */
111 /** Reuters status (user settable) */
112 #define SIPE_STATUS_ID_LUNCH "out-to-lunch" /* Out To Lunch */
113 /* ??? PURPLE_STATUS_EXTENDED_AWAY */
114 /* ??? PURPLE_STATUS_MOBILE */
115 /* ??? PURPLE_STATUS_TUNE */
117 /* Status attributes (see also sipe_status_types() */
118 #define SIPE_STATUS_ATTR_ID_MESSAGE "message"
120 static struct sipe_activity_map_struct
125 const char *status_id
;
127 } const sipe_activity_map
[] =
129 /* This has nothing to do with Availability numbers, like 3500 (online).
130 * Just a mapping of Communicator Activities to Purple statuses to be able display them in Pidgin.
132 { SIPE_ACTIVITY_UNSET
, "unset", NULL
, NULL
},
133 { SIPE_ACTIVITY_ONLINE
, "online", NULL
, NULL
},
134 { SIPE_ACTIVITY_INACTIVE
, SIPE_STATUS_ID_IDLE
, N_("Inactive") , NULL
},
135 { SIPE_ACTIVITY_BUSY
, SIPE_STATUS_ID_BUSY
, N_("Busy") , SIPE_STATUS_ID_BUSY
},
136 { SIPE_ACTIVITY_BUSYIDLE
, SIPE_STATUS_ID_BUSYIDLE
, N_("Busy-Idle") , NULL
},
137 { SIPE_ACTIVITY_DND
, SIPE_STATUS_ID_DND
, NULL
, SIPE_STATUS_ID_DND
},
138 { SIPE_ACTIVITY_BRB
, SIPE_STATUS_ID_BRB
, N_("Be right back") , SIPE_STATUS_ID_BRB
},
139 { SIPE_ACTIVITY_AWAY
, "away", NULL
, NULL
},
140 { SIPE_ACTIVITY_LUNCH
, SIPE_STATUS_ID_LUNCH
, N_("Out to lunch") , NULL
},
141 { SIPE_ACTIVITY_OFFLINE
, "offline", NULL
, NULL
},
142 { SIPE_ACTIVITY_ON_PHONE
, SIPE_STATUS_ID_ON_PHONE
, N_("In a call") , NULL
},
143 { SIPE_ACTIVITY_IN_CONF
, SIPE_STATUS_ID_IN_CONF
, N_("In a conference") , NULL
},
144 { SIPE_ACTIVITY_IN_MEETING
, SIPE_STATUS_ID_IN_MEETING
, N_("In a meeting") , NULL
},
145 { SIPE_ACTIVITY_OOF
, "out-of-office", N_("Out of office") , NULL
},
146 { SIPE_ACTIVITY_URGENT_ONLY
, "urgent-interruptions-only", N_("Urgent interruptions only") , NULL
}
148 /** @param x is sipe_activity */
149 #define SIPE_ACTIVITY_I18N(x) gettext(sipe_activity_map[x].desc)
152 sipe_get_activity_by_token(const char *token
)
156 for (i
= 0; i
< SIPE_ACTIVITY_NUM_TYPES
; i
++)
158 if (sipe_strequal(token
, sipe_activity_map
[i
].token
))
159 return sipe_activity_map
[i
].type
;
162 return sipe_activity_map
[0].type
;
166 sipe_get_activity_desc_by_token(const char *token
)
168 if (!token
) return NULL
;
170 return SIPE_ACTIVITY_I18N(sipe_get_activity_by_token(token
));
173 static void send_presence_status(struct sipe_core_private
*sipe_private
,
177 * @param from0 from URI (with 'sip:' prefix). Will be filled with self-URI if NULL passed.
180 send_soap_request_with_cb(struct sipe_core_private
*sipe_private
,
183 TransCallback callback
,
184 struct transaction_payload
*payload
)
186 gchar
*from
= from0
? g_strdup(from0
) : sip_uri_self(sipe_private
);
187 gchar
*contact
= get_contact(sipe_private
);
188 gchar
*hdr
= g_strdup_printf("Contact: %s\r\n"
189 "Content-Type: application/SOAP+xml\r\n",contact
);
191 struct transaction
*trans
= sip_transport_service(sipe_private
,
196 trans
->payload
= payload
;
203 static void send_soap_request(struct sipe_core_private
*sipe_private
,
206 send_soap_request_with_cb(sipe_private
, NULL
, body
, NULL
, NULL
);
210 * Returns pointer to URI without sip: prefix if any
212 * @param sip_uri SIP URI possibly with sip: prefix. Example: sip:first.last@hq.company.com
213 * @return pointer to URL without sip: prefix. Coresponding example: first.last@hq.company.com
215 * Doesn't allocate memory
218 sipe_get_no_sip_uri(const char *sip_uri
)
220 const char *prefix
= "sip:";
221 if (!sip_uri
) return NULL
;
223 if (g_str_has_prefix(sip_uri
, prefix
)) {
224 return (sip_uri
+strlen(prefix
));
231 sipe_contact_set_acl (struct sipe_core_private
*sipe_private
,
235 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
236 gchar
* body
= g_strdup_printf(SIPE_SOAP_ALLOW_DENY
, who
, rights
, sip
->acl_delta
++);
237 send_soap_request(sipe_private
, body
);
242 sipe_change_access_level(struct sipe_core_private
*sipe_private
,
243 const int container_id
,
248 sipe_core_contact_allow_deny (struct sipe_core_public
*sipe_public
,
249 const gchar
* who
, gboolean allow
)
251 struct sipe_core_private
*sipe_private
= SIPE_CORE_PRIVATE
;
254 SIPE_DEBUG_INFO("Authorizing contact %s", who
);
256 SIPE_DEBUG_INFO("Blocking contact %s", who
);
259 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
260 sipe_change_access_level(sipe_private
, (allow
? -1 : 32000), "user", sipe_get_no_sip_uri(who
));
262 sipe_contact_set_acl(sipe_private
, who
, allow
? "AA" : "BD");
267 void sipe_auth_user_cb(void * data
)
269 struct sipe_auth_job
* job
= (struct sipe_auth_job
*) data
;
272 sipe_core_contact_allow_deny((struct sipe_core_public
*)job
->sipe_private
, job
->who
, TRUE
);
277 void sipe_deny_user_cb(void * data
)
279 struct sipe_auth_job
* job
= (struct sipe_auth_job
*) data
;
282 sipe_core_contact_allow_deny((struct sipe_core_public
*)job
->sipe_private
, job
->who
, FALSE
);
286 /** @applicable: 2005-
289 sipe_process_presence_wpending (struct sipe_core_private
*sipe_private
,
292 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
294 const sipe_xml
*watcher
;
295 // Ensure it's either not a response (eg it's a BENOTIFY) or that it's a 200 OK response
296 if (msg
->response
!= 0 && msg
->response
!= 200) return;
298 if (msg
->bodylen
== 0 || msg
->body
== NULL
|| sipe_strequal(sipmsg_find_header(msg
, "Event"), "msrtc.wpending")) return;
300 watchers
= sipe_xml_parse(msg
->body
, msg
->bodylen
);
301 if (!watchers
) return;
303 for (watcher
= sipe_xml_child(watchers
, "watcher"); watcher
; watcher
= sipe_xml_twin(watcher
)) {
304 gchar
* remote_user
= g_strdup(sipe_xml_attribute(watcher
, "uri"));
305 gchar
* alias
= g_strdup(sipe_xml_attribute(watcher
, "displayName"));
306 gboolean on_list
= g_hash_table_lookup(sipe_private
->buddies
, remote_user
) != NULL
;
308 // TODO pull out optional displayName to pass as alias
310 struct sipe_auth_job
* job
= g_new0(struct sipe_auth_job
, 1);
311 job
->who
= remote_user
;
312 job
->sipe_private
= sipe_private
;
313 purple_account_request_authorization(
327 sipe_xml_free(watchers
);
332 sipe_group_add(struct sipe_core_private
*sipe_private
,
333 struct sipe_group
* group
)
335 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
336 PurpleGroup
* purple_group
= purple_find_group(group
->name
);
338 purple_group
= purple_group_new(group
->name
);
339 purple_blist_add_group(purple_group
, NULL
);
343 group
->purple_group
= purple_group
;
344 sip
->groups
= g_slist_append(sip
->groups
, group
);
345 SIPE_DEBUG_INFO("added group %s (id %d)", group
->name
, group
->id
);
347 SIPE_DEBUG_INFO("did not add group %s", group
->name
? group
->name
: "");
351 static struct sipe_group
*sipe_group_find_by_id(struct sipe_core_private
*sipe_private
,
354 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
355 struct sipe_group
*group
;
364 if (group
->id
== id
) {
372 static struct sipe_group
*sipe_group_find_by_name(struct sipe_core_private
*sipe_private
,
375 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
376 struct sipe_group
*group
;
385 if (sipe_strequal(group
->name
, name
)) {
394 sipe_group_rename(struct sipe_core_private
*sipe_private
,
395 struct sipe_group
*group
,
398 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
400 SIPE_DEBUG_INFO("Renaming group %s to %s", group
->name
, name
);
401 body
= g_markup_printf_escaped(SIPE_SOAP_MOD_GROUP
, group
->id
, name
, sip
->contacts_delta
++);
402 send_soap_request(sipe_private
, body
);
405 group
->name
= g_strdup(name
);
409 * Only appends if no such value already stored.
412 GSList
* slist_insert_unique_sorted(GSList
*list
, gpointer data
, GCompareFunc func
) {
414 if (!g_slist_find_custom(list
, data
, func
)) {
415 res
= g_slist_insert_sorted(list
, data
, func
);
421 sipe_group_compare(struct sipe_group
*group1
, struct sipe_group
*group2
) {
422 return group1
->id
- group2
->id
;
426 * Returns string like "2 4 7 8" - group ids buddy belong to.
429 sipe_get_buddy_groups_string (struct sipe_buddy
*buddy
) {
432 //creating array from GList, converting int to gchar*
433 gchar
**ids_arr
= g_new(gchar
*, g_slist_length(buddy
->groups
) + 1);
434 GSList
*entry
= buddy
->groups
;
436 if (!ids_arr
) return NULL
;
439 struct sipe_group
* group
= entry
->data
;
440 ids_arr
[i
] = g_strdup_printf("%d", group
->id
);
445 res
= g_strjoinv(" ", ids_arr
);
451 * Sends buddy update to server
454 sipe_core_group_set_user(struct sipe_core_public
*sipe_public
, const gchar
* who
)
456 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA
;
457 struct sipe_buddy
*buddy
= g_hash_table_lookup(SIPE_CORE_PRIVATE
->buddies
, who
);
458 PurpleBuddy
*purple_buddy
= purple_find_buddy (sip
->account
, who
);
460 if (buddy
&& purple_buddy
) {
461 const char *alias
= purple_buddy_get_alias(purple_buddy
);
462 gchar
*groups
= sipe_get_buddy_groups_string(buddy
);
465 SIPE_DEBUG_INFO("Saving buddy %s with alias %s and groups %s", who
, alias
, groups
);
467 body
= g_markup_printf_escaped(SIPE_SOAP_SET_CONTACT
,
468 alias
, groups
, "true", buddy
->name
, sip
->contacts_delta
++
470 send_soap_request(SIPE_CORE_PRIVATE
, body
);
477 static gboolean
process_add_group_response(struct sipe_core_private
*sipe_private
,
479 struct transaction
*trans
)
481 if (msg
->response
== 200) {
482 struct sipe_group
*group
;
483 struct group_user_context
*ctx
= trans
->payload
->data
;
485 const sipe_xml
*node
;
487 struct sipe_buddy
*buddy
;
489 xml
= sipe_xml_parse(msg
->body
, msg
->bodylen
);
494 node
= sipe_xml_child(xml
, "Body/addGroup/groupID");
500 group_id
= sipe_xml_data(node
);
506 group
= g_new0(struct sipe_group
, 1);
507 group
->id
= (int)g_ascii_strtod(group_id
, NULL
);
509 group
->name
= g_strdup(ctx
->group_name
);
511 sipe_group_add(sipe_private
, group
);
513 if (ctx
->user_name
) {
514 buddy
= g_hash_table_lookup(sipe_private
->buddies
, ctx
->user_name
);
516 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, group
, (GCompareFunc
)sipe_group_compare
);
519 sipe_core_group_set_user(SIPE_CORE_PUBLIC
, ctx
->user_name
);
528 static void sipe_group_context_destroy(gpointer data
)
530 struct group_user_context
*ctx
= data
;
531 g_free(ctx
->group_name
);
532 g_free(ctx
->user_name
);
536 static void sipe_group_create (struct sipe_core_private
*sipe_private
,
537 const gchar
*name
, const gchar
* who
)
539 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
540 struct transaction_payload
*payload
= g_new0(struct transaction_payload
, 1);
541 struct group_user_context
*ctx
= g_new0(struct group_user_context
, 1);
542 const gchar
*soap_name
= sipe_strequal(name
, _("Other Contacts")) ? "~" : name
;
544 ctx
->group_name
= g_strdup(name
);
545 ctx
->user_name
= g_strdup(who
);
546 payload
->destroy
= sipe_group_context_destroy
;
549 body
= g_markup_printf_escaped(SIPE_SOAP_ADD_GROUP
, soap_name
, sip
->contacts_delta
++);
550 send_soap_request_with_cb(sipe_private
, NULL
, body
, process_add_group_response
, payload
);
555 sipe_sched_calendar_status_update(struct sipe_core_private
*sipe_private
,
556 time_t calculate_from
);
559 sipe_get_availability_by_status(const char* sipe_status_id
, char** activity_token
);
562 sipe_get_status_by_availability(int avail
,
566 sipe_set_purple_account_status_and_note(const PurpleAccount
*account
,
567 const char *status_id
,
569 time_t do_not_publish
[]);
572 sipe_apply_calendar_status(struct sipe_core_private
*sipe_private
,
573 struct sipe_buddy
*sbuddy
,
574 const char *status_id
)
576 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
577 time_t cal_avail_since
;
578 int cal_status
= sipe_cal_get_status(sbuddy
, time(NULL
), &cal_avail_since
);
584 if (cal_status
< SIPE_CAL_NO_DATA
) {
585 SIPE_DEBUG_INFO("sipe_apply_calendar_status: cal_status : %d for %s", cal_status
, sbuddy
->name
);
586 SIPE_DEBUG_INFO("sipe_apply_calendar_status: cal_avail_since : %s", asctime(localtime(&cal_avail_since
)));
589 /* scheduled Cal update call */
591 status_id
= sbuddy
->last_non_cal_status_id
;
592 g_free(sbuddy
->activity
);
593 sbuddy
->activity
= g_strdup(sbuddy
->last_non_cal_activity
);
597 SIPE_DEBUG_INFO("sipe_apply_calendar_status: status_id is NULL for %s, exiting.",
598 sbuddy
->name
? sbuddy
->name
: "" );
602 /* adjust to calendar status */
603 if (cal_status
!= SIPE_CAL_NO_DATA
) {
604 SIPE_DEBUG_INFO("sipe_apply_calendar_status: user_avail_since: %s", asctime(localtime(&sbuddy
->user_avail_since
)));
606 if (cal_status
== SIPE_CAL_BUSY
607 && cal_avail_since
> sbuddy
->user_avail_since
608 && 6500 >= sipe_get_availability_by_status(status_id
, NULL
))
610 status_id
= SIPE_STATUS_ID_BUSY
;
611 g_free(sbuddy
->activity
);
612 sbuddy
->activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_IN_MEETING
));
614 avail
= sipe_get_availability_by_status(status_id
, NULL
);
616 SIPE_DEBUG_INFO("sipe_apply_calendar_status: activity_since : %s", asctime(localtime(&sbuddy
->activity_since
)));
617 if (cal_avail_since
> sbuddy
->activity_since
) {
618 if (cal_status
== SIPE_CAL_OOF
619 && avail
>= 15000) /* 12000 in 2007 */
621 g_free(sbuddy
->activity
);
622 sbuddy
->activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_OOF
));
627 /* then set status_id actually */
628 SIPE_DEBUG_INFO("sipe_apply_calendar_status: to %s for %s", status_id
, sbuddy
->name
? sbuddy
->name
: "" );
629 purple_prpl_got_user_status(sip
->account
, sbuddy
->name
, status_id
, NULL
);
631 /* set our account state to the one in roaming (including calendar info) */
632 self_uri
= sip_uri_self(sipe_private
);
633 if (sip
->initial_state_published
&& sipe_strcase_equal(sbuddy
->name
, self_uri
)) {
634 if (sipe_strequal(status_id
, SIPE_STATUS_ID_OFFLINE
)) {
635 status_id
= g_strdup(SIPE_STATUS_ID_INVISIBLE
); /* not not let offline status switch us off */
638 SIPE_DEBUG_INFO("sipe_apply_calendar_status: switch to '%s' for the account", sip
->status
);
639 sipe_set_purple_account_status_and_note(sip
->account
, status_id
, sip
->note
, sip
->do_not_publish
);
645 sipe_got_user_status(struct sipe_core_private
*sipe_private
,
647 const char *status_id
)
649 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
650 struct sipe_buddy
*sbuddy
= g_hash_table_lookup(sipe_private
->buddies
, uri
);
654 /* Check if on 2005 system contact's calendar,
655 * then set/preserve it.
657 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
658 sipe_apply_calendar_status(sipe_private
, sbuddy
, status_id
);
660 purple_prpl_got_user_status(sip
->account
, uri
, status_id
, NULL
);
665 update_calendar_status_cb(SIPE_UNUSED_PARAMETER
char *name
,
666 struct sipe_buddy
*sbuddy
,
667 struct sipe_core_private
*sipe_private
)
669 sipe_apply_calendar_status(sipe_private
, sbuddy
, NULL
);
673 * Updates contact's status
674 * based on their calendar information.
676 * Applicability: 2005 systems
679 update_calendar_status(struct sipe_core_private
*sipe_private
,
680 SIPE_UNUSED_PARAMETER
void *unused
)
682 SIPE_DEBUG_INFO_NOFORMAT("update_calendar_status() started.");
683 g_hash_table_foreach(sipe_private
->buddies
, (GHFunc
)update_calendar_status_cb
, sipe_private
);
685 /* repeat scheduling */
686 sipe_sched_calendar_status_update(sipe_private
, time(NULL
) + 3*60 /* 3 min */);
690 * Schedules process of contacts' status update
691 * based on their calendar information.
692 * Should be scheduled to the beginning of every
693 * 15 min interval, like:
694 * 13:00, 13:15, 13:30, 13:45, etc.
696 * Applicability: 2005 systems
699 sipe_sched_calendar_status_update(struct sipe_core_private
*sipe_private
,
700 time_t calculate_from
)
702 int interval
= 15*60;
703 /** start of the beginning of closest 15 min interval. */
704 time_t next_start
= ((time_t)((int)((int)calculate_from
)/interval
+ 1)*interval
);
706 SIPE_DEBUG_INFO("sipe_sched_calendar_status_update: calculate_from time: %s",
707 asctime(localtime(&calculate_from
)));
708 SIPE_DEBUG_INFO("sipe_sched_calendar_status_update: next start time : %s",
709 asctime(localtime(&next_start
)));
711 sipe_schedule_seconds(sipe_private
,
712 "<+2005-cal-status>",
714 next_start
- time(NULL
),
715 update_calendar_status
,
720 * Schedules process of self status publish
721 * based on own calendar information.
722 * Should be scheduled to the beginning of every
723 * 15 min interval, like:
724 * 13:00, 13:15, 13:30, 13:45, etc.
726 * Applicability: 2007+ systems
729 sipe_sched_calendar_status_self_publish(struct sipe_core_private
*sipe_private
,
730 time_t calculate_from
)
733 /** start of the beginning of closest 5 min interval. */
734 time_t next_start
= ((time_t)((int)((int)calculate_from
)/interval
+ 1)*interval
);
736 SIPE_DEBUG_INFO("sipe_sched_calendar_status_self_publish: calculate_from time: %s",
737 asctime(localtime(&calculate_from
)));
738 SIPE_DEBUG_INFO("sipe_sched_calendar_status_self_publish: next start time : %s",
739 asctime(localtime(&next_start
)));
741 sipe_schedule_seconds(sipe_private
,
742 "<+2007-cal-status>",
744 next_start
- time(NULL
),
745 publish_calendar_status_self
,
749 static void sipe_subscribe_resource_uri(const char *name
,
750 SIPE_UNUSED_PARAMETER gpointer value
,
751 gchar
**resources_uri
)
753 gchar
*tmp
= *resources_uri
;
754 *resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"/>\n", tmp
, name
);
758 static void sipe_subscribe_resource_uri_with_context(const char *name
, gpointer value
, gchar
**resources_uri
)
760 struct sipe_buddy
*sbuddy
= (struct sipe_buddy
*)value
;
761 gchar
*context
= sbuddy
&& sbuddy
->just_added
? "><context/></resource>" : "/>";
762 gchar
*tmp
= *resources_uri
;
764 if (sbuddy
) sbuddy
->just_added
= FALSE
; /* should be enought to include context one time */
766 *resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"%s\n", tmp
, name
, context
);
771 * Support for Batch Category SUBSCRIBE [MS-PRES] - msrtc-event-categories+xml OCS 2007
772 * Support for Batch Category SUBSCRIBE [MS-SIP] - adrl+xml LCS 2005
773 * The user sends an initial batched category SUBSCRIBE request against all contacts on his roaming list in only a request
774 * A batch category SUBSCRIBE request MUST have the same To-URI and From-URI.
775 * This header will be send only if adhoclist there is a "Supported: adhoclist" in REGISTER answer else will be send a Single Category SUBSCRIBE
778 static void sipe_subscribe_presence_batched_to(struct sipe_core_private
*sipe_private
,
779 gchar
*resources_uri
,
782 gchar
*contact
= get_contact(sipe_private
);
787 gchar
*autoextend
= "";
790 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
791 require
= ", categoryList";
792 accept
= ", application/msrtc-event-categories+xml, application/xpidf+xml, application/pidf+xml";
793 content_type
= "application/msrtc-adrl-categorylist+xml";
794 content
= g_strdup_printf(
795 "<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
796 "<action name=\"subscribe\" id=\"63792024\">\n"
797 "<adhocList>\n%s</adhocList>\n"
798 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
799 "<category name=\"calendarData\"/>\n"
800 "<category name=\"contactCard\"/>\n"
801 "<category name=\"note\"/>\n"
802 "<category name=\"state\"/>\n"
805 "</batchSub>", sipe_private
->username
, resources_uri
);
807 autoextend
= "Supported: com.microsoft.autoextend\r\n";
808 content_type
= "application/adrl+xml";
809 content
= g_strdup_printf(
810 "<adhoclist xmlns=\"urn:ietf:params:xml:ns:adrl\" uri=\"sip:%s\" name=\"sip:%s\">\n"
811 "<create xmlns=\"\">\n%s</create>\n"
812 "</adhoclist>\n", sipe_private
->username
, sipe_private
->username
, resources_uri
);
814 g_free(resources_uri
);
816 request
= g_strdup_printf(
817 "Require: adhoclist%s\r\n"
818 "Supported: eventlist\r\n"
819 "Accept: application/rlmi+xml, multipart/related, text/xml+msrtc.pidf%s\r\n"
820 "Supported: ms-piggyback-first-notify\r\n"
821 "%sSupported: ms-benotify\r\n"
822 "Proxy-Require: ms-benotify\r\n"
823 "Event: presence\r\n"
824 "Content-Type: %s\r\n"
825 "Contact: %s\r\n", require
, accept
, autoextend
, content_type
, contact
);
828 sipe_subscribe_presence_buddy(sipe_private
, to
, request
, content
);
835 static void sipe_subscribe_presence_batched(struct sipe_core_private
*sipe_private
,
836 SIPE_UNUSED_PARAMETER
void *unused
)
838 gchar
*to
= sip_uri_self(sipe_private
);
839 gchar
*resources_uri
= g_strdup("");
840 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
841 g_hash_table_foreach(sipe_private
->buddies
, (GHFunc
) sipe_subscribe_resource_uri_with_context
, &resources_uri
);
843 g_hash_table_foreach(sipe_private
->buddies
, (GHFunc
) sipe_subscribe_resource_uri
, &resources_uri
);
846 sipe_subscribe_presence_batched_to(sipe_private
, resources_uri
, to
);
849 struct presence_batched_routed
{
854 static void sipe_subscribe_presence_batched_routed_free(void *payload
)
856 struct presence_batched_routed
*data
= payload
;
857 GSList
*buddies
= data
->buddies
;
859 g_free(buddies
->data
);
860 buddies
= buddies
->next
;
862 g_slist_free(data
->buddies
);
867 static void sipe_subscribe_presence_batched_routed(struct sipe_core_private
*sipe_private
,
870 struct presence_batched_routed
*data
= payload
;
871 GSList
*buddies
= data
->buddies
;
872 gchar
*resources_uri
= g_strdup("");
874 gchar
*tmp
= resources_uri
;
875 resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"/>\n", tmp
, (char *) buddies
->data
);
877 buddies
= buddies
->next
;
879 sipe_subscribe_presence_batched_to(sipe_private
, resources_uri
,
880 g_strdup(data
->host
));
884 * Single Category SUBSCRIBE [MS-PRES] ; To send when the server returns a 200 OK message with state="resubscribe" in response.
885 * The user sends a single SUBSCRIBE request to the subscribed contact.
886 * The To-URI and the URI listed in the resource list MUST be the same for a single category SUBSCRIBE request.
890 static void sipe_subscribe_presence_single(struct sipe_core_private
*sipe_private
,
893 gchar
*to
= sip_uri((char *)buddy_name
);
894 gchar
*tmp
= get_contact(sipe_private
);
896 gchar
*content
= NULL
;
897 gchar
*autoextend
= "";
898 gchar
*content_type
= "";
899 struct sipe_buddy
*sbuddy
= g_hash_table_lookup(sipe_private
->buddies
, to
);
900 gchar
*context
= sbuddy
&& sbuddy
->just_added
? "><context/></resource>" : "/>";
902 if (sbuddy
) sbuddy
->just_added
= FALSE
;
904 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
905 content_type
= "Content-Type: application/msrtc-adrl-categorylist+xml\r\n";
907 autoextend
= "Supported: com.microsoft.autoextend\r\n";
910 request
= g_strdup_printf(
911 "Accept: application/msrtc-event-categories+xml, text/xml+msrtc.pidf, application/xpidf+xml, application/pidf+xml, application/rlmi+xml, multipart/related\r\n"
912 "Supported: ms-piggyback-first-notify\r\n"
913 "%s%sSupported: ms-benotify\r\n"
914 "Proxy-Require: ms-benotify\r\n"
915 "Event: presence\r\n"
916 "Contact: %s\r\n", autoextend
, content_type
, tmp
);
918 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
919 content
= g_strdup_printf(
920 "<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
921 "<action name=\"subscribe\" id=\"63792024\"><adhocList>\n"
922 "<resource uri=\"%s\"%s\n"
924 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
925 "<category name=\"calendarData\"/>\n"
926 "<category name=\"contactCard\"/>\n"
927 "<category name=\"note\"/>\n"
928 "<category name=\"state\"/>\n"
931 "</batchSub>", sipe_private
->username
, to
, context
);
936 sipe_subscribe_presence_buddy(sipe_private
, to
, request
, content
);
943 void sipe_set_status(PurpleAccount
*account
, PurpleStatus
*status
)
945 SIPE_DEBUG_INFO("sipe_set_status: status=%s", purple_status_get_id(status
));
947 if (!purple_status_is_active(status
))
951 struct sipe_core_private
*sipe_private
= PURPLE_ACCOUNT_TO_SIPE_CORE_PRIVATE
;
952 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
957 time_t now
= time(NULL
);
958 const char *status_id
= purple_status_get_id(status
);
959 const char *note
= purple_status_get_attr_string(status
, SIPE_STATUS_ATTR_ID_MESSAGE
);
960 sipe_activity activity
= sipe_get_activity_by_token(status_id
);
961 gboolean do_not_publish
= ((now
- sip
->do_not_publish
[activity
]) <= 2);
963 /* when other point of presence clears note, but we are keeping
966 if (do_not_publish
&& !note
&& sip
->cal
&& sip
->cal
->oof_note
) {
967 SIPE_DEBUG_INFO_NOFORMAT("sipe_set_status: enabling publication as OOF note keepers.");
968 do_not_publish
= FALSE
;
971 SIPE_DEBUG_INFO("sipe_set_status: was: sip->do_not_publish[%s]=%d [?] now(time)=%d",
972 status_id
, (int)sip
->do_not_publish
[activity
], (int)now
);
974 sip
->do_not_publish
[activity
] = 0;
975 SIPE_DEBUG_INFO("sipe_set_status: set: sip->do_not_publish[%s]=%d [0]",
976 status_id
, (int)sip
->do_not_publish
[activity
]);
980 SIPE_DEBUG_INFO_NOFORMAT("sipe_set_status: publication was switched off, exiting.");
985 sip
->status
= g_strdup(status_id
);
987 /* hack to escape apostrof before comparison */
988 tmp
= note
? sipe_utils_str_replace(note
, "'", "'") : NULL
;
990 /* this will preserve OOF flag as well */
991 if (!sipe_strequal(tmp
, sip
->note
)) {
992 sip
->is_oof_note
= FALSE
;
994 sip
->note
= g_strdup(note
);
995 sip
->note_since
= time(NULL
);
999 /* schedule 2 sec to capture idle flag */
1000 action_name
= g_strdup_printf("<%s>", "+set-status");
1001 sipe_schedule_seconds(sipe_private
,
1004 SIPE_IDLE_SET_DELAY
,
1005 send_presence_status
,
1007 g_free(action_name
);
1013 sipe_set_idle(PurpleConnection
* gc
,
1016 SIPE_DEBUG_INFO("sipe_set_idle: interval=%d", interval
);
1019 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
1020 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
1023 sip
->idle_switch
= time(NULL
);
1024 SIPE_DEBUG_INFO("sipe_set_idle: sip->idle_switch : %s", asctime(localtime(&(sip
->idle_switch
))));
1030 sipe_group_buddy(PurpleConnection
*gc
,
1032 const char *old_group_name
,
1033 const char *new_group_name
)
1035 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
1036 struct sipe_buddy
* buddy
= g_hash_table_lookup(sipe_private
->buddies
, who
);
1037 struct sipe_group
* old_group
= NULL
;
1038 struct sipe_group
* new_group
;
1040 SIPE_DEBUG_INFO("sipe_group_buddy[CB]: who:%s old_group_name:%s new_group_name:%s",
1041 who
? who
: "", old_group_name
? old_group_name
: "", new_group_name
? new_group_name
: "");
1043 if(!buddy
) { // buddy not in roaming list
1047 if (old_group_name
) {
1048 old_group
= sipe_group_find_by_name(sipe_private
, old_group_name
);
1050 new_group
= sipe_group_find_by_name(sipe_private
, new_group_name
);
1053 buddy
->groups
= g_slist_remove(buddy
->groups
, old_group
);
1054 SIPE_DEBUG_INFO("buddy %s removed from old group %s", who
, old_group_name
);
1058 sipe_group_create(sipe_private
, new_group_name
, who
);
1060 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, new_group
, (GCompareFunc
)sipe_group_compare
);
1061 sipe_core_group_set_user(SIPE_CORE_PUBLIC
, who
);
1065 void sipe_add_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
1067 SIPE_DEBUG_INFO("sipe_add_buddy[CB]: buddy:%s group:%s", buddy
? buddy
->name
: "", group
? group
->name
: "");
1069 /* libpurple can call us with undefined buddy or group */
1070 if (buddy
&& group
) {
1071 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
1073 /* Buddy name must be lower case as we use purple_normalize_nocase() to compare */
1074 gchar
*buddy_name
= g_ascii_strdown(buddy
->name
, -1);
1075 purple_blist_rename_buddy(buddy
, buddy_name
);
1078 /* Prepend sip: if needed */
1079 if (!g_str_has_prefix(buddy
->name
, "sip:")) {
1080 gchar
*buf
= sip_uri_from_name(buddy
->name
);
1081 purple_blist_rename_buddy(buddy
, buf
);
1085 if (!g_hash_table_lookup(sipe_private
->buddies
, buddy
->name
)) {
1086 struct sipe_buddy
*b
= g_new0(struct sipe_buddy
, 1);
1087 SIPE_DEBUG_INFO("sipe_add_buddy: adding %s", buddy
->name
);
1088 b
->name
= g_strdup(buddy
->name
);
1089 b
->just_added
= TRUE
;
1090 g_hash_table_insert(sipe_private
->buddies
, b
->name
, b
);
1091 sipe_group_buddy(gc
, b
->name
, NULL
, group
->name
);
1092 /* @TODO should go to callback */
1093 sipe_subscribe_presence_single(sipe_private
,
1096 SIPE_DEBUG_INFO("sipe_add_buddy: buddy %s already in internal list", buddy
->name
);
1101 static void sipe_free_buddy(struct sipe_buddy
*buddy
)
1105 * We are calling g_hash_table_foreach_steal(). That means that no
1106 * key/value deallocation functions are called. Therefore the glib
1107 * hash code does not touch the key (buddy->name) or value (buddy)
1108 * of the to-be-deleted hash node at all. It follows that we
1110 * - MUST free the memory for the key ourselves and
1111 * - ARE allowed to do it in this function
1113 * Conclusion: glib must be broken on the Windows platform if sipe
1114 * crashes with SIGTRAP when closing. You'll have to live
1115 * with the memory leak until this is fixed.
1117 g_free(buddy
->name
);
1119 g_free(buddy
->activity
);
1120 g_free(buddy
->meeting_subject
);
1121 g_free(buddy
->meeting_location
);
1122 g_free(buddy
->note
);
1124 g_free(buddy
->cal_start_time
);
1125 g_free(buddy
->cal_free_busy_base64
);
1126 g_free(buddy
->cal_free_busy
);
1127 g_free(buddy
->last_non_cal_activity
);
1129 sipe_cal_free_working_hours(buddy
->cal_working_hours
);
1131 g_free(buddy
->device_name
);
1132 g_slist_free(buddy
->groups
);
1137 * Unassociates buddy from group first.
1138 * Then see if no groups left, removes buddy completely.
1139 * Otherwise updates buddy groups on server.
1141 void sipe_remove_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
1143 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
1144 struct sipe_buddy
*b
;
1145 struct sipe_group
*g
= NULL
;
1147 SIPE_DEBUG_INFO("sipe_remove_buddy[CB]: buddy:%s group:%s", buddy
? buddy
->name
: "", group
? group
->name
: "");
1150 b
= g_hash_table_lookup(sipe_private
->buddies
, buddy
->name
);
1154 g
= sipe_group_find_by_name(sipe_private
, group
->name
);
1158 b
->groups
= g_slist_remove(b
->groups
, g
);
1159 SIPE_DEBUG_INFO("buddy %s removed from group %s", buddy
->name
, g
->name
);
1162 if (g_slist_length(b
->groups
) < 1) {
1163 gchar
*action_name
= sipe_utils_presence_key(buddy
->name
);
1164 sipe_schedule_cancel(sipe_private
, action_name
);
1165 g_free(action_name
);
1167 g_hash_table_remove(sipe_private
->buddies
, buddy
->name
);
1170 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
1171 gchar
* body
= g_strdup_printf(SIPE_SOAP_DEL_CONTACT
, b
->name
, sip
->contacts_delta
++);
1172 send_soap_request(sipe_private
, body
);
1178 //updates groups on server
1179 sipe_core_group_set_user(SIPE_CORE_PUBLIC
, b
->name
);
1185 sipe_rename_group(PurpleConnection
*gc
,
1186 const char *old_name
,
1188 SIPE_UNUSED_PARAMETER GList
*moved_buddies
)
1190 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
1191 struct sipe_group
* s_group
= sipe_group_find_by_name(sipe_private
, old_name
);
1193 sipe_group_rename(sipe_private
, s_group
, group
->name
);
1195 SIPE_DEBUG_INFO("Cannot find group %s to rename", old_name
);
1200 sipe_remove_group(PurpleConnection
*gc
, PurpleGroup
*group
)
1202 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
1203 struct sipe_group
* s_group
= sipe_group_find_by_name(sipe_private
, group
->name
);
1205 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
1207 SIPE_DEBUG_INFO("Deleting group %s", group
->name
);
1208 body
= g_strdup_printf(SIPE_SOAP_DEL_GROUP
, s_group
->id
, sip
->contacts_delta
++);
1209 send_soap_request(sipe_private
, body
);
1212 sip
->groups
= g_slist_remove(sip
->groups
, s_group
);
1213 g_free(s_group
->name
);
1216 SIPE_DEBUG_INFO("Cannot find group %s to delete", group
->name
);
1221 * A callback for g_hash_table_foreach
1224 sipe_buddy_subscribe_cb(char *buddy_name
,
1225 SIPE_UNUSED_PARAMETER
struct sipe_buddy
*buddy
,
1226 struct sipe_core_private
*sipe_private
)
1228 gchar
*action_name
= sipe_utils_presence_key(buddy_name
);
1229 /* g_hash_table_size() can never return 0, otherwise this function wouldn't be called :-) */
1230 guint time_range
= (g_hash_table_size(sipe_private
->buddies
) * 1000) / 25; /* time interval for 25 requests per sec. In msec. */
1231 guint timeout
= ((guint
) rand()) / (RAND_MAX
/ time_range
) + 1; /* random period within the range but never 0! */
1233 sipe_schedule_mseconds(sipe_private
,
1235 g_strdup(buddy_name
),
1237 sipe_subscribe_presence_single
,
1239 g_free(action_name
);
1243 * Removes entries from purple buddy list
1244 * that does not correspond ones in the roaming contact list.
1246 static void sipe_cleanup_local_blist(struct sipe_core_private
*sipe_private
) {
1247 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
1248 GSList
*buddies
= purple_find_buddies(sip
->account
, NULL
);
1249 GSList
*entry
= buddies
;
1250 struct sipe_buddy
*buddy
;
1254 SIPE_DEBUG_INFO("sipe_cleanup_local_blist: overall %d Purple buddies (including clones)", g_slist_length(buddies
));
1255 SIPE_DEBUG_INFO("sipe_cleanup_local_blist: %d sipe buddies (unique)", g_hash_table_size(sipe_private
->buddies
));
1258 g
= purple_buddy_get_group(b
);
1259 buddy
= g_hash_table_lookup(sipe_private
->buddies
, b
->name
);
1261 gboolean in_sipe_groups
= FALSE
;
1262 GSList
*entry2
= buddy
->groups
;
1264 struct sipe_group
*group
= entry2
->data
;
1265 if (sipe_strequal(group
->name
, g
->name
)) {
1266 in_sipe_groups
= TRUE
;
1269 entry2
= entry2
->next
;
1271 if(!in_sipe_groups
) {
1272 SIPE_DEBUG_INFO("*** REMOVING %s from Purple group: %s as not having this group in roaming list", b
->name
, g
->name
);
1273 purple_blist_remove_buddy(b
);
1276 SIPE_DEBUG_INFO("*** REMOVING %s from Purple group: %s as this buddy not in roaming list", b
->name
, g
->name
);
1277 purple_blist_remove_buddy(b
);
1279 entry
= entry
->next
;
1281 g_slist_free(buddies
);
1285 sipe_find_access_level(struct sipe_core_private
*sipe_private
,
1288 gboolean
*is_group_access
);
1291 sipe_refresh_blocked_status_cb(char *buddy_name
,
1292 SIPE_UNUSED_PARAMETER
struct sipe_buddy
*buddy
,
1293 struct sipe_core_private
*sipe_private
)
1295 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
1296 int container_id
= sipe_find_access_level(sipe_private
, "user", buddy_name
, NULL
);
1297 gboolean blocked
= (container_id
== 32000);
1298 gboolean blocked_in_blist
= !purple_privacy_check(sip
->account
, buddy_name
);
1300 /* SIPE_DEBUG_INFO("sipe_refresh_blocked_status_cb: buddy_name=%s, blocked=%s, blocked_in_blist=%s",
1301 buddy_name, blocked ? "T" : "F", blocked_in_blist ? "T" : "F"); */
1303 if (blocked
!= blocked_in_blist
) {
1305 purple_privacy_deny_add(sip
->account
, buddy_name
, TRUE
);
1307 purple_privacy_deny_remove(sip
->account
, buddy_name
, TRUE
);
1310 /* stupid workaround to make pidgin re-render screen to reflect our changes */
1312 PurpleBuddy
*pbuddy
= purple_find_buddy(sip
->account
, buddy_name
);
1313 const PurplePresence
*presence
= purple_buddy_get_presence(pbuddy
);
1314 const PurpleStatus
*pstatus
= purple_presence_get_active_status(presence
);
1316 SIPE_DEBUG_INFO_NOFORMAT("sipe_refresh_blocked_status_cb: forcefully refreshing screen.");
1317 sipe_got_user_status(sipe_private
, buddy_name
, purple_status_get_id(pstatus
));
1324 sipe_refresh_blocked_status(struct sipe_core_private
*sipe_private
)
1326 g_hash_table_foreach(sipe_private
->buddies
,
1327 (GHFunc
) sipe_refresh_blocked_status_cb
,
1331 static gboolean
sipe_process_roaming_contacts(struct sipe_core_private
*sipe_private
,
1334 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
1335 int len
= msg
->bodylen
;
1337 const gchar
*tmp
= sipmsg_find_header(msg
, "Event");
1338 const sipe_xml
*item
;
1340 const gchar
*contacts_delta
;
1341 const sipe_xml
*group_node
;
1342 if (!g_str_has_prefix(tmp
, "vnd-microsoft-roaming-contacts")) {
1346 /* Convert the contact from XML to Purple Buddies */
1347 isc
= sipe_xml_parse(msg
->body
, len
);
1352 contacts_delta
= sipe_xml_attribute(isc
, "deltaNum");
1353 if (contacts_delta
) {
1354 sip
->contacts_delta
= (int)g_ascii_strtod(contacts_delta
, NULL
);
1357 if (sipe_strequal(sipe_xml_name(isc
), "contactList")) {
1360 for (group_node
= sipe_xml_child(isc
, "group"); group_node
; group_node
= sipe_xml_twin(group_node
)) {
1361 struct sipe_group
* group
= g_new0(struct sipe_group
, 1);
1362 const char *name
= sipe_xml_attribute(group_node
, "name");
1364 if (g_str_has_prefix(name
, "~")) {
1365 name
= _("Other Contacts");
1367 group
->name
= g_strdup(name
);
1368 group
->id
= (int)g_ascii_strtod(sipe_xml_attribute(group_node
, "id"), NULL
);
1370 sipe_group_add(sipe_private
, group
);
1373 // Make sure we have at least one group
1374 if (g_slist_length(sip
->groups
) == 0) {
1375 sipe_group_create(sipe_private
, _("Other Contacts"), NULL
);
1378 /* Parse contacts */
1379 for (item
= sipe_xml_child(isc
, "contact"); item
; item
= sipe_xml_twin(item
)) {
1380 const gchar
*uri
= sipe_xml_attribute(item
, "uri");
1381 const gchar
*name
= sipe_xml_attribute(item
, "name");
1383 struct sipe_buddy
*buddy
= NULL
;
1385 gchar
**item_groups
;
1388 /* Buddy name must be lower case as we use purple_normalize_nocase() to compare */
1389 tmp
= sip_uri_from_name(uri
);
1390 buddy_name
= g_ascii_strdown(tmp
, -1);
1393 /* assign to group Other Contacts if nothing else received */
1394 tmp
= g_strdup(sipe_xml_attribute(item
, "groups"));
1396 struct sipe_group
*group
= sipe_group_find_by_name(sipe_private
, _("Other Contacts"));
1398 tmp
= group
? g_strdup_printf("%d", group
->id
) : g_strdup("1");
1400 item_groups
= g_strsplit(tmp
, " ", 0);
1403 while (item_groups
[i
]) {
1404 struct sipe_group
*group
= sipe_group_find_by_id(sipe_private
, g_ascii_strtod(item_groups
[i
], NULL
));
1406 // If couldn't find the right group for this contact, just put them in the first group we have
1407 if (group
== NULL
&& g_slist_length(sip
->groups
) > 0) {
1408 group
= sip
->groups
->data
;
1411 if (group
!= NULL
) {
1412 PurpleBuddy
*b
= purple_find_buddy_in_group(sip
->account
, buddy_name
, group
->purple_group
);
1414 b
= purple_buddy_new(sip
->account
, buddy_name
, uri
);
1415 purple_blist_add_buddy(b
, NULL
, group
->purple_group
, NULL
);
1417 SIPE_DEBUG_INFO("Created new buddy %s with alias %s", buddy_name
, uri
);
1420 if (sipe_strcase_equal(uri
, purple_buddy_get_alias(b
))) {
1421 if (name
!= NULL
&& strlen(name
) != 0) {
1422 purple_blist_alias_buddy(b
, name
);
1424 SIPE_DEBUG_INFO("Replaced buddy %s alias with %s", buddy_name
, name
);
1429 buddy
= g_new0(struct sipe_buddy
, 1);
1430 buddy
->name
= g_strdup(b
->name
);
1431 g_hash_table_insert(sipe_private
->buddies
, buddy
->name
, buddy
);
1433 SIPE_DEBUG_INFO("Added SIPE buddy %s", buddy
->name
);
1436 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, group
, (GCompareFunc
)sipe_group_compare
);
1438 SIPE_DEBUG_INFO("Added buddy %s to group %s", b
->name
, group
->name
);
1440 SIPE_DEBUG_INFO("No group found for contact %s! Unable to add to buddy list",
1445 } // while, contact groups
1446 g_strfreev(item_groups
);
1451 sipe_cleanup_local_blist(sipe_private
);
1453 /* Add self-contact if not there yet. 2005 systems. */
1454 /* This will resemble subscription to roaming_self in 2007 systems */
1455 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
1456 gchar
*self_uri
= sip_uri_self(sipe_private
);
1457 struct sipe_buddy
*buddy
= g_hash_table_lookup(sipe_private
->buddies
, self_uri
);
1460 buddy
= g_new0(struct sipe_buddy
, 1);
1461 buddy
->name
= g_strdup(self_uri
);
1462 g_hash_table_insert(sipe_private
->buddies
, buddy
->name
, buddy
);
1469 /* subscribe to buddies */
1470 if (!sip
->subscribed_buddies
) { //do it once, then count Expire field to schedule resubscribe.
1471 if (sip
->batched_support
) {
1472 sipe_subscribe_presence_batched(sipe_private
, NULL
);
1474 g_hash_table_foreach(sipe_private
->buddies
,
1475 (GHFunc
)sipe_buddy_subscribe_cb
,
1478 sip
->subscribed_buddies
= TRUE
;
1480 /* for 2005 systems schedule contacts' status update
1481 * based on their calendar information
1483 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
1484 sipe_sched_calendar_status_update(sipe_private
, time(NULL
));
1491 * Fires on deregistration event initiated by server.
1492 * [MS-SIPREGE] SIP extension.
1497 // Content-Type: text/registration-event
1498 // subscription-state: terminated;expires=0
1499 // ms-diagnostics-public: 4141;reason="User disabled"
1501 // deregistered;event=rejected
1503 static void sipe_process_registration_notify(struct sipe_core_private
*sipe_private
,
1506 const gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
1507 gchar
*event
= NULL
;
1508 gchar
*reason
= NULL
;
1509 const gchar
*diagnostics
= sipmsg_find_header(msg
, "ms-diagnostics");
1512 diagnostics
= diagnostics
? diagnostics
: sipmsg_find_header(msg
, "ms-diagnostics-public");
1513 SIPE_DEBUG_INFO_NOFORMAT("sipe_process_registration_notify: deregistration received.");
1515 if (!g_ascii_strncasecmp(contenttype
, "text/registration-event", 23)) {
1516 event
= sipmsg_find_part_of_header(msg
->body
, "event=", NULL
, NULL
);
1517 //@TODO have proper parameter extraction _by_name_ func, case insesitive.
1518 event
= event
? event
: sipmsg_find_part_of_header(msg
->body
, "event=", ";", NULL
);
1520 SIPE_DEBUG_INFO_NOFORMAT("sipe_process_registration_notify: unknown content type, exiting.");
1524 if (diagnostics
!= NULL
) {
1525 reason
= sipmsg_find_part_of_header(diagnostics
, "reason=\"", "\"", NULL
);
1526 } else { // for LCS2005
1528 if (event
&& sipe_strcase_equal(event
, "unregistered")) {
1529 error_id
= 4140; // [MS-SIPREGE]
1530 //reason = g_strdup(_("User logged out")); // [MS-OCER]
1531 reason
= g_strdup(_("you are already signed in at another location"));
1532 } else if (event
&& sipe_strcase_equal(event
, "rejected")) {
1534 reason
= g_strdup(_("user disabled")); // [MS-OCER]
1535 } else if (event
&& sipe_strcase_equal(event
, "deactivated")) {
1537 reason
= g_strdup(_("user moved")); // [MS-OCER]
1541 warning
= g_strdup_printf(_("You have been rejected by the server: %s"), reason
? reason
: _("no reason given"));
1544 sipe_backend_connection_error(SIPE_CORE_PUBLIC
,
1545 SIPE_CONNECTION_ERROR_INVALID_USERNAME
,
1551 static void sipe_process_provisioning_v2(struct sipe_core_private
*sipe_private
,
1554 sipe_xml
*xn_provision_group_list
;
1555 const sipe_xml
*node
;
1557 xn_provision_group_list
= sipe_xml_parse(msg
->body
, msg
->bodylen
);
1559 /* provisionGroup */
1560 for (node
= sipe_xml_child(xn_provision_group_list
, "provisionGroup"); node
; node
= sipe_xml_twin(node
)) {
1561 if (sipe_strequal("ServerConfiguration", sipe_xml_attribute(node
, "name"))) {
1562 g_free(sipe_private
->focus_factory_uri
);
1563 sipe_private
->focus_factory_uri
= sipe_xml_data(sipe_xml_child(node
, "focusFactoryUri"));
1564 SIPE_DEBUG_INFO("sipe_process_provisioning_v2: sipe_private->focus_factory_uri=%s",
1565 sipe_private
->focus_factory_uri
? sipe_private
->focus_factory_uri
: "");
1569 sipe_xml_free(xn_provision_group_list
);
1572 /** for 2005 system */
1574 sipe_process_provisioning(struct sipe_core_private
*sipe_private
,
1577 sipe_xml
*xn_provision
;
1578 const sipe_xml
*node
;
1580 xn_provision
= sipe_xml_parse(msg
->body
, msg
->bodylen
);
1581 if ((node
= sipe_xml_child(xn_provision
, "user"))) {
1582 SIPE_DEBUG_INFO("sipe_process_provisioning: uri=%s", sipe_xml_attribute(node
, "uri"));
1583 if ((node
= sipe_xml_child(node
, "line"))) {
1584 const gchar
*line_uri
= sipe_xml_attribute(node
, "uri");
1585 const gchar
*server
= sipe_xml_attribute(node
, "server");
1586 SIPE_DEBUG_INFO("sipe_process_provisioning: line_uri=%s server=%s", line_uri
, server
);
1587 sip_csta_open(sipe_private
, line_uri
, server
);
1590 sipe_xml_free(xn_provision
);
1593 static void sipe_process_roaming_acl(struct sipe_core_private
*sipe_private
,
1596 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
1597 const gchar
*contacts_delta
;
1600 xml
= sipe_xml_parse(msg
->body
, msg
->bodylen
);
1606 contacts_delta
= sipe_xml_attribute(xml
, "deltaNum");
1609 sip
->acl_delta
= (int)g_ascii_strtod(contacts_delta
, NULL
);
1615 /** MS-PRES container */
1616 struct sipe_container
{
1621 /** MS-PRES container member */
1622 struct sipe_container_member
{
1623 /** user, domain, sameEnterprise, federated, publicCloud; everyone */
1629 free_container_member(struct sipe_container_member
*member
)
1631 if (!member
) return;
1633 g_free(member
->type
);
1634 g_free(member
->value
);
1639 free_container(struct sipe_container
*container
)
1643 if (!container
) return;
1645 entry
= container
->members
;
1647 void *data
= entry
->data
;
1648 entry
= g_slist_remove(entry
, data
);
1649 free_container_member((struct sipe_container_member
*)data
);
1655 sipe_send_container_members_prepare(const guint container_id
,
1656 const guint container_version
,
1657 const gchar
*action
,
1660 char **container_xmls
)
1662 gchar
*value_str
= value
? g_strdup_printf(" value=\"%s\"", value
) : g_strdup("");
1665 if (!container_xmls
) return;
1667 body
= g_strdup_printf(
1668 "<container id=\"%d\" version=\"%d\"><member action=\"%s\" type=\"%s\"%s/></container>",
1676 if ((*container_xmls
) == NULL
) {
1677 *container_xmls
= body
;
1679 char *tmp
= *container_xmls
;
1681 *container_xmls
= g_strconcat(*container_xmls
, body
, NULL
);
1688 sipe_send_set_container_members(struct sipe_core_private
*sipe_private
,
1689 char *container_xmls
)
1696 if (!container_xmls
) return;
1698 self
= sip_uri_self(sipe_private
);
1699 body
= g_strdup_printf(
1700 "<setContainerMembers xmlns=\"http://schemas.microsoft.com/2006/09/sip/container-management\">"
1702 "</setContainerMembers>",
1705 contact
= get_contact(sipe_private
);
1706 hdr
= g_strdup_printf("Contact: %s\r\n"
1707 "Content-Type: application/msrtc-setcontainermembers+xml\r\n", contact
);
1710 sip_transport_service(sipe_private
,
1722 * Finds locally stored MS-PRES container member
1724 static struct sipe_container_member
*
1725 sipe_find_container_member(struct sipe_container
*container
,
1729 struct sipe_container_member
*member
;
1732 if (container
== NULL
|| type
== NULL
) {
1736 entry
= container
->members
;
1738 member
= entry
->data
;
1739 if (sipe_strcase_equal(member
->type
, type
) &&
1740 sipe_strcase_equal(member
->value
, value
))
1744 entry
= entry
->next
;
1750 * Finds locally stored MS-PRES container by id
1752 static struct sipe_container
*
1753 sipe_find_container(struct sipe_core_private
*sipe_private
,
1756 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
1757 struct sipe_container
*container
;
1764 entry
= sip
->containers
;
1766 container
= entry
->data
;
1767 if (id
== container
->id
) {
1770 entry
= entry
->next
;
1776 sipe_get_access_domains(struct sipe_core_private
*sipe_private
)
1778 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
1779 struct sipe_container
*container
;
1780 struct sipe_container_member
*member
;
1785 if (!sip
) return NULL
;
1787 entry
= sip
->containers
;
1789 container
= entry
->data
;
1791 entry2
= container
->members
;
1793 member
= entry2
->data
;
1794 if (sipe_strcase_equal(member
->type
, "domain"))
1796 res
= slist_insert_unique_sorted(res
, g_strdup(member
->value
), (GCompareFunc
)g_ascii_strcasecmp
);
1798 entry2
= entry2
->next
;
1800 entry
= entry
->next
;
1806 * Returns pointer to domain part in provided Email URL
1808 * @param email an email URL. Example: first.last@hq.company.com
1809 * @return pointer to domain part of email URL. Coresponding example: hq.company.com
1811 * Doesn't allocate memory
1814 sipe_get_domain(const char *email
)
1818 if (!email
) return NULL
;
1820 tmp
= strstr(email
, "@");
1822 if (tmp
&& ((tmp
+1) < (email
+ strlen(email
)))) {
1830 /* @TODO: replace with binary search for faster access? */
1831 /** source: http://support.microsoft.com/kb/897567 */
1832 static const char * const public_domains
[] = {
1833 "aol.com", "icq.com", "love.com", "mac.com", "br.live.com",
1834 "hotmail.co.il", "hotmail.co.jp", "hotmail.co.th", "hotmail.co.uk",
1835 "hotmail.com", "hotmail.com.ar", "hotmail.com.tr", "hotmail.es",
1836 "hotmail.de", "hotmail.fr", "hotmail.it", "live.at", "live.be",
1837 "live.ca", "live.cl", "live.cn", "live.co.in", "live.co.kr",
1838 "live.co.uk", "live.co.za", "live.com", "live.com.ar", "live.com.au",
1839 "live.com.co", "live.com.mx", "live.com.my", "live.com.pe",
1840 "live.com.ph", "live.com.pk", "live.com.pt", "live.com.sg",
1841 "live.com.ve", "live.de", "live.dk", "live.fr", "live.hk", "live.ie",
1842 "live.in", "live.it", "live.jp", "live.nl", "live.no", "live.ph",
1843 "live.ru", "live.se", "livemail.com.br", "livemail.tw",
1844 "messengeruser.com", "msn.com", "passport.com", "sympatico.ca",
1845 "tw.live.com", "webtv.net", "windowslive.com", "windowslive.es",
1850 sipe_is_public_domain(const char *domain
)
1853 while (public_domains
[i
]) {
1854 if (sipe_strcase_equal(public_domains
[i
], domain
)) {
1871 sipe_get_access_level_name(int container_id
)
1873 switch(container_id
) {
1874 case 32000: return _("Blocked");
1875 case 400: return _("Personal");
1876 case 300: return _("Team");
1877 case 200: return _("Company");
1878 case 100: return _("Public");
1880 return _("Unknown");
1883 static const guint containers
[] = {32000, 400, 300, 200, 100};
1884 #define CONTAINERS_LEN (sizeof(containers) / sizeof(guint))
1888 sipe_find_member_access_level(struct sipe_core_private
*sipe_private
,
1893 const gchar
*value_mod
= value
;
1895 if (!type
) return -1;
1897 if (sipe_strequal("user", type
)) {
1898 value_mod
= sipe_get_no_sip_uri(value
);
1901 for (i
= 0; i
< CONTAINERS_LEN
; i
++) {
1902 struct sipe_container_member
*member
;
1903 struct sipe_container
*container
= sipe_find_container(sipe_private
, containers
[i
]);
1904 if (!container
) continue;
1906 member
= sipe_find_container_member(container
, type
, value_mod
);
1907 if (member
) return containers
[i
];
1913 /** Member type: user, domain, sameEnterprise, federated, publicCloud; everyone */
1915 sipe_find_access_level(struct sipe_core_private
*sipe_private
,
1918 gboolean
*is_group_access
)
1920 int container_id
= -1;
1922 if (sipe_strequal("user", type
)) {
1924 const char *no_sip_uri
= sipe_get_no_sip_uri(value
);
1926 container_id
= sipe_find_member_access_level(sipe_private
, "user", no_sip_uri
);
1927 if (container_id
>= 0) {
1928 if (is_group_access
) *is_group_access
= FALSE
;
1929 return container_id
;
1932 domain
= sipe_get_domain(no_sip_uri
);
1933 container_id
= sipe_find_member_access_level(sipe_private
, "domain", domain
);
1934 if (container_id
>= 0) {
1935 if (is_group_access
) *is_group_access
= TRUE
;
1936 return container_id
;
1939 container_id
= sipe_find_member_access_level(sipe_private
, "sameEnterprise", NULL
);
1940 if ((container_id
>= 0) && sipe_strcase_equal(sipe_private
->public.sip_domain
, domain
)) {
1941 if (is_group_access
) *is_group_access
= TRUE
;
1942 return container_id
;
1945 container_id
= sipe_find_member_access_level(sipe_private
, "publicCloud", NULL
);
1946 if ((container_id
>= 0) && sipe_is_public_domain(domain
)) {
1947 if (is_group_access
) *is_group_access
= TRUE
;
1948 return container_id
;
1951 container_id
= sipe_find_member_access_level(sipe_private
, "everyone", NULL
);
1952 if ((container_id
>= 0)) {
1953 if (is_group_access
) *is_group_access
= TRUE
;
1954 return container_id
;
1957 container_id
= sipe_find_member_access_level(sipe_private
, type
, value
);
1958 if (is_group_access
) *is_group_access
= FALSE
;
1961 return container_id
;
1965 * @param container_id a new access level. If -1 then current access level
1966 * is just removed (I.e. the member is removed from all containers).
1967 * @param type a type of member. E.g. "user", "sameEnterprise", etc.
1968 * @param value a value for member. E.g. SIP URI for "user" member type.
1971 sipe_change_access_level(struct sipe_core_private
*sipe_private
,
1972 const int container_id
,
1977 int current_container_id
= -1;
1978 char *container_xmls
= NULL
;
1980 /* for each container: find/delete */
1981 for (i
= 0; i
< CONTAINERS_LEN
; i
++) {
1982 struct sipe_container_member
*member
;
1983 struct sipe_container
*container
= sipe_find_container(sipe_private
, containers
[i
]);
1985 if (!container
) continue;
1987 member
= sipe_find_container_member(container
, type
, value
);
1989 current_container_id
= containers
[i
];
1990 /* delete/publish current access level */
1991 if (container_id
< 0 || container_id
!= current_container_id
) {
1992 sipe_send_container_members_prepare(current_container_id
, container
->version
, "remove", type
, value
, &container_xmls
);
1993 /* remove member from our cache, to be able to recalculate AL below */
1994 container
->members
= g_slist_remove(container
->members
, member
);
1995 current_container_id
= -1;
2000 /* recalculate AL below */
2001 current_container_id
= sipe_find_access_level(sipe_private
, type
, value
, NULL
);
2003 /* assign/publish new access level */
2004 if (container_id
!= current_container_id
&& container_id
>= 0) {
2005 struct sipe_container
*container
= sipe_find_container(sipe_private
, container_id
);
2006 guint version
= container
? container
->version
: 0;
2008 sipe_send_container_members_prepare(container_id
, version
, "add", type
, value
, &container_xmls
);
2011 if (container_xmls
) {
2012 sipe_send_set_container_members(sipe_private
, container_xmls
);
2014 g_free(container_xmls
);
2018 free_publication(struct sipe_publication
*publication
)
2020 g_free(publication
->category
);
2021 g_free(publication
->cal_event_hash
);
2022 g_free(publication
->note
);
2024 g_free(publication
->working_hours_xml_str
);
2025 g_free(publication
->fb_start_str
);
2026 g_free(publication
->free_busy_base64
);
2028 g_free(publication
);
2031 /* key is <category><instance><container> */
2033 sipe_is_our_publication(struct sipe_core_private
*sipe_private
,
2036 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
2039 /* filling keys for our publications if not yet cached */
2040 if (!sip
->our_publication_keys
) {
2041 guint device_instance
= sipe_get_pub_instance(sipe_private
, SIPE_PUB_DEVICE
);
2042 guint machine_instance
= sipe_get_pub_instance(sipe_private
, SIPE_PUB_STATE_MACHINE
);
2043 guint user_instance
= sipe_get_pub_instance(sipe_private
, SIPE_PUB_STATE_USER
);
2044 guint calendar_instance
= sipe_get_pub_instance(sipe_private
, SIPE_PUB_STATE_CALENDAR
);
2045 guint cal_oof_instance
= sipe_get_pub_instance(sipe_private
, SIPE_PUB_STATE_CALENDAR_OOF
);
2046 guint cal_data_instance
= sipe_get_pub_instance(sipe_private
, SIPE_PUB_CALENDAR_DATA
);
2047 guint note_oof_instance
= sipe_get_pub_instance(sipe_private
, SIPE_PUB_NOTE_OOF
);
2049 SIPE_DEBUG_INFO_NOFORMAT("* Our Publication Instances *");
2050 SIPE_DEBUG_INFO("\tDevice : %u\t0x%08X", device_instance
, device_instance
);
2051 SIPE_DEBUG_INFO("\tMachine State : %u\t0x%08X", machine_instance
, machine_instance
);
2052 SIPE_DEBUG_INFO("\tUser Stare : %u\t0x%08X", user_instance
, user_instance
);
2053 SIPE_DEBUG_INFO("\tCalendar State : %u\t0x%08X", calendar_instance
, calendar_instance
);
2054 SIPE_DEBUG_INFO("\tCalendar OOF State : %u\t0x%08X", cal_oof_instance
, cal_oof_instance
);
2055 SIPE_DEBUG_INFO("\tCalendar FreeBusy : %u\t0x%08X", cal_data_instance
, cal_data_instance
);
2056 SIPE_DEBUG_INFO("\tOOF Note : %u\t0x%08X", note_oof_instance
, note_oof_instance
);
2057 SIPE_DEBUG_INFO("\tNote : %u", 0);
2058 SIPE_DEBUG_INFO("\tCalendar WorkingHours: %u", 0);
2061 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2062 g_strdup_printf("<%s><%u><%u>", "device", device_instance
, 2));
2064 /* state:machineState */
2065 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2066 g_strdup_printf("<%s><%u><%u>", "state", machine_instance
, 2));
2067 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2068 g_strdup_printf("<%s><%u><%u>", "state", machine_instance
, 3));
2070 /* state:userState */
2071 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2072 g_strdup_printf("<%s><%u><%u>", "state", user_instance
, 2));
2073 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2074 g_strdup_printf("<%s><%u><%u>", "state", user_instance
, 3));
2076 /* state:calendarState */
2077 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2078 g_strdup_printf("<%s><%u><%u>", "state", calendar_instance
, 2));
2079 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2080 g_strdup_printf("<%s><%u><%u>", "state", calendar_instance
, 3));
2082 /* state:calendarState OOF */
2083 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2084 g_strdup_printf("<%s><%u><%u>", "state", cal_oof_instance
, 2));
2085 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2086 g_strdup_printf("<%s><%u><%u>", "state", cal_oof_instance
, 3));
2089 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2090 g_strdup_printf("<%s><%u><%u>", "note", 0, 200));
2091 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2092 g_strdup_printf("<%s><%u><%u>", "note", 0, 300));
2093 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2094 g_strdup_printf("<%s><%u><%u>", "note", 0, 400));
2097 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2098 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance
, 200));
2099 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2100 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance
, 300));
2101 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2102 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance
, 400));
2104 /* calendarData:WorkingHours */
2105 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2106 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 1));
2107 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2108 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 100));
2109 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2110 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 200));
2111 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2112 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 300));
2113 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2114 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 400));
2115 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2116 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 32000));
2118 /* calendarData:FreeBusy */
2119 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2120 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 1));
2121 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2122 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 100));
2123 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2124 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 200));
2125 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2126 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 300));
2127 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2128 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 400));
2129 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2130 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 32000));
2132 //SIPE_DEBUG_INFO("sipe_is_our_publication: sip->our_publication_keys length=%d",
2133 // sip->our_publication_keys ? (int) g_slist_length(sip->our_publication_keys) : -1);
2136 //SIPE_DEBUG_INFO("sipe_is_our_publication: key=%s", key);
2138 entry
= sip
->our_publication_keys
;
2140 //SIPE_DEBUG_INFO(" sipe_is_our_publication: entry->data=%s", entry->data);
2141 if (sipe_strequal(entry
->data
, key
)) {
2144 entry
= entry
->next
;
2149 /** Property names to store in blist.xml */
2150 #define ALIAS_PROP "alias"
2151 #define EMAIL_PROP "email"
2152 #define PHONE_PROP "phone"
2153 #define PHONE_DISPLAY_PROP "phone-display"
2154 #define PHONE_MOBILE_PROP "phone-mobile"
2155 #define PHONE_MOBILE_DISPLAY_PROP "phone-mobile-display"
2156 #define PHONE_HOME_PROP "phone-home"
2157 #define PHONE_HOME_DISPLAY_PROP "phone-home-display"
2158 #define PHONE_OTHER_PROP "phone-other"
2159 #define PHONE_OTHER_DISPLAY_PROP "phone-other-display"
2160 #define PHONE_CUSTOM1_PROP "phone-custom1"
2161 #define PHONE_CUSTOM1_DISPLAY_PROP "phone-custom1-display"
2162 #define SITE_PROP "site"
2163 #define COMPANY_PROP "company"
2164 #define DEPARTMENT_PROP "department"
2165 #define TITLE_PROP "title"
2166 #define OFFICE_PROP "office"
2167 /** implies work address */
2168 #define ADDRESS_STREET_PROP "address-street"
2169 #define ADDRESS_CITY_PROP "address-city"
2170 #define ADDRESS_STATE_PROP "address-state"
2171 #define ADDRESS_ZIPCODE_PROP "address-zipcode"
2172 #define ADDRESS_COUNTRYCODE_PROP "address-country-code"
2175 * Tries to figure out user first and last name
2176 * based on Display Name and email properties.
2178 * Allocates memory - must be g_free()'d
2180 * Examples to parse:
2182 * First Last - Company Name
2185 * Last, First (C)(STP) (Company)
2186 * first.last@company.com (preprocessed as "first last")
2187 * first.last.company.com@reuters.net (preprocessed as "first last company com")
2189 * Unusable examples:
2190 * user@company.com (preprocessed as "user")
2191 * first.m.last@company.com (preprocessed as "first m last")
2192 * user.company.com@reuters.net (preprocessed as "user company com")
2195 sipe_get_first_last_names(struct sipe_core_private
*sipe_private
,
2200 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
2201 PurpleBuddy
*p_buddy
;
2204 const char *first
, *last
;
2207 gboolean has_comma
= FALSE
;
2209 if (!sip
|| !uri
) return;
2211 p_buddy
= purple_find_buddy(sip
->account
, uri
);
2213 if (!p_buddy
) return;
2215 display_name
= g_strdup(purple_buddy_get_alias(p_buddy
));
2216 email
= purple_blist_node_get_string(&p_buddy
->node
, EMAIL_PROP
);
2218 if (!display_name
&& !email
) return;
2220 /* if no display name, make "first last anything_else" out of email */
2221 if (email
&& !display_name
) {
2222 display_name
= g_strndup(email
, strstr(email
, "@") - email
);
2223 display_name
= sipe_utils_str_replace((tmp
= display_name
), ".", " ");
2228 has_comma
= (strstr(display_name
, ",") != NULL
);
2229 display_name
= sipe_utils_str_replace((tmp
= display_name
), ", ", " ");
2231 display_name
= sipe_utils_str_replace((tmp
= display_name
), ",", " ");
2235 parts
= g_strsplit(display_name
, " ", 0);
2237 if (!parts
[0] || !parts
[1]) {
2238 g_free(display_name
);
2252 *first_name
= g_strstrip(g_strdup(first
));
2256 *last_name
= g_strstrip(g_strdup(last
));
2259 g_free(display_name
);
2264 * Update user information
2266 * @param uri buddy SIP URI with 'sip:' prefix whose info we want to change.
2267 * @param property_name
2268 * @param property_value may be modified to strip white space
2271 sipe_update_user_info(struct sipe_core_private
*sipe_private
,
2273 const char *property_name
,
2274 char *property_value
)
2276 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
2277 GSList
*buddies
, *entry
;
2279 if (!property_name
|| strlen(property_name
) == 0) return;
2282 property_value
= g_strstrip(property_value
);
2284 entry
= buddies
= purple_find_buddies(sip
->account
, uri
); /* all buddies in different groups */
2286 const char *prop_str
;
2287 const char *server_alias
;
2288 PurpleBuddy
*p_buddy
= entry
->data
;
2290 /* for Display Name */
2291 if (sipe_strequal(property_name
, ALIAS_PROP
)) {
2292 if (property_value
&& sipe_is_bad_alias(uri
, purple_buddy_get_alias(p_buddy
))) {
2293 SIPE_DEBUG_INFO("Replacing alias for %s with %s", uri
, property_value
);
2294 purple_blist_alias_buddy(p_buddy
, property_value
);
2297 server_alias
= purple_buddy_get_server_alias(p_buddy
);
2298 if (!is_empty(property_value
) &&
2299 (!sipe_strequal(property_value
, server_alias
) || is_empty(server_alias
)) )
2301 purple_blist_server_alias_buddy(p_buddy
, property_value
);
2304 /* for other properties */
2306 if (!is_empty(property_value
)) {
2307 prop_str
= purple_blist_node_get_string(&p_buddy
->node
, property_name
);
2308 if (!prop_str
|| !sipe_strcase_equal(prop_str
, property_value
)) {
2309 purple_blist_node_set_string(&p_buddy
->node
, property_name
, property_value
);
2314 entry
= entry
->next
;
2316 g_slist_free(buddies
);
2321 * Suitable for both 2005 and 2007 systems.
2323 * @param uri buddy SIP URI with 'sip:' prefix whose info we want to change.
2325 * @param phone may be modified to strip white space
2326 * @param phone_display_string may be modified to strip white space
2329 sipe_update_user_phone(struct sipe_core_private
*sipe_private
,
2331 const gchar
*phone_type
,
2333 gchar
*phone_display_string
)
2335 const char *phone_node
= PHONE_PROP
; /* work phone by default */
2336 const char *phone_display_node
= PHONE_DISPLAY_PROP
; /* work phone by default */
2338 if(!phone
|| strlen(phone
) == 0) return;
2340 if ((sipe_strequal(phone_type
, "mobile") || sipe_strequal(phone_type
, "cell"))) {
2341 phone_node
= PHONE_MOBILE_PROP
;
2342 phone_display_node
= PHONE_MOBILE_DISPLAY_PROP
;
2343 } else if (sipe_strequal(phone_type
, "home")) {
2344 phone_node
= PHONE_HOME_PROP
;
2345 phone_display_node
= PHONE_HOME_DISPLAY_PROP
;
2346 } else if (sipe_strequal(phone_type
, "other")) {
2347 phone_node
= PHONE_OTHER_PROP
;
2348 phone_display_node
= PHONE_OTHER_DISPLAY_PROP
;
2349 } else if (sipe_strequal(phone_type
, "custom1")) {
2350 phone_node
= PHONE_CUSTOM1_PROP
;
2351 phone_display_node
= PHONE_CUSTOM1_DISPLAY_PROP
;
2354 sipe_update_user_info(sipe_private
, uri
, phone_node
, phone
);
2355 if (phone_display_string
) {
2356 sipe_update_user_info(sipe_private
, uri
, phone_display_node
, phone_display_string
);
2361 sipe_core_update_calendar(struct sipe_core_public
*sipe_public
)
2363 SIPE_DEBUG_INFO_NOFORMAT("sipe_core_update_calendar: started.");
2366 * If failed, the branch will be disabled for subsequent calls.
2367 * Can't rely that user turned the functionality on in account settings.
2369 sipe_ews_update_calendar(SIPE_CORE_PRIVATE
);
2370 sipe_domino_update_calendar(SIPE_CORE_PRIVATE
);
2372 /* schedule repeat */
2373 sipe_schedule_seconds(SIPE_CORE_PRIVATE
,
2374 "<+update-calendar>",
2376 UPDATE_CALENDAR_INTERVAL
,
2377 (sipe_schedule_action
)sipe_core_update_calendar
,
2380 SIPE_DEBUG_INFO_NOFORMAT("sipe_core_update_calendar: finished.");
2384 * This method motivates Purple's Host (e.g. Pidgin) to update its UI
2385 * by using standard Purple's means of signals and saved statuses.
2387 * Thus all UI elements get updated: Status Button with Note, docklet.
2388 * This is ablolutely important as both our status and note can come
2389 * inbound (roaming) or be updated programmatically (e.g. based on our
2393 sipe_set_purple_account_status_and_note(const PurpleAccount
*account
,
2394 const char *status_id
,
2395 const char *message
,
2396 time_t do_not_publish
[])
2398 PurpleStatus
*status
= purple_account_get_active_status(account
);
2399 gboolean changed
= TRUE
;
2401 if (g_str_equal(status_id
, purple_status_get_id(status
)) &&
2402 sipe_strequal(message
, purple_status_get_attr_string(status
, SIPE_STATUS_ATTR_ID_MESSAGE
)))
2407 if (purple_savedstatus_is_idleaway()) {
2412 PurpleSavedStatus
*saved_status
;
2413 const PurpleStatusType
*acct_status_type
=
2414 purple_status_type_find_with_id(account
->status_types
, status_id
);
2415 PurpleStatusPrimitive primitive
= purple_status_type_get_primitive(acct_status_type
);
2416 sipe_activity activity
= sipe_get_activity_by_token(status_id
);
2418 saved_status
= purple_savedstatus_find_transient_by_type_and_message(primitive
, message
);
2420 purple_savedstatus_set_substatus(saved_status
, account
, acct_status_type
, message
);
2423 /* If this type+message is unique then create a new transient saved status
2424 * Ref: gtkstatusbox.c
2426 if (!saved_status
) {
2428 GList
*active_accts
= purple_accounts_get_all_active();
2430 saved_status
= purple_savedstatus_new(NULL
, primitive
);
2431 purple_savedstatus_set_message(saved_status
, message
);
2433 for (tmp
= active_accts
; tmp
!= NULL
; tmp
= tmp
->next
) {
2434 purple_savedstatus_set_substatus(saved_status
,
2435 (PurpleAccount
*)tmp
->data
, acct_status_type
, message
);
2437 g_list_free(active_accts
);
2440 do_not_publish
[activity
] = time(NULL
);
2441 SIPE_DEBUG_INFO("sipe_set_purple_account_status_and_note: do_not_publish[%s]=%d [now]",
2442 status_id
, (int)do_not_publish
[activity
]);
2444 /* Set the status for each account */
2445 purple_savedstatus_activate(saved_status
);
2449 struct hash_table_delete_payload
{
2450 GHashTable
*hash_table
;
2455 sipe_remove_category_container_publications_cb(const char *name
,
2456 struct sipe_publication
*publication
,
2457 struct hash_table_delete_payload
*payload
)
2459 if (publication
->container
== payload
->container
) {
2460 g_hash_table_remove(payload
->hash_table
, name
);
2464 sipe_remove_category_container_publications(GHashTable
*our_publications
,
2465 const char *category
,
2468 struct hash_table_delete_payload payload
;
2469 payload
.hash_table
= g_hash_table_lookup(our_publications
, category
);
2471 if (!payload
.hash_table
) return;
2473 payload
.container
= container
;
2474 g_hash_table_foreach(payload
.hash_table
, (GHFunc
)sipe_remove_category_container_publications_cb
, &payload
);
2478 send_publish_category_initial(struct sipe_core_private
*sipe_private
);
2481 * When we receive some self (BE) NOTIFY with a new subscriber
2482 * we sends a setSubscribers request to him [SIP-PRES] 4.8
2485 static void sipe_process_roaming_self(struct sipe_core_private
*sipe_private
,
2488 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
2492 const sipe_xml
*node
;
2493 const sipe_xml
*node2
;
2494 char *display_name
= NULL
;
2496 GSList
*category_names
= NULL
;
2497 int aggreg_avail
= 0;
2498 gboolean do_update_status
= FALSE
;
2499 gboolean has_note_cleaned
= FALSE
;
2501 SIPE_DEBUG_INFO_NOFORMAT("sipe_process_roaming_self");
2503 xml
= sipe_xml_parse(msg
->body
, msg
->bodylen
);
2506 contact
= get_contact(sipe_private
);
2507 to
= sip_uri_self(sipe_private
);
2511 /* set list of categories participating in this XML */
2512 for (node
= sipe_xml_child(xml
, "categories/category"); node
; node
= sipe_xml_twin(node
)) {
2513 const gchar
*name
= sipe_xml_attribute(node
, "name");
2514 category_names
= slist_insert_unique_sorted(category_names
, (gchar
*)name
, (GCompareFunc
)strcmp
);
2516 SIPE_DEBUG_INFO("sipe_process_roaming_self: category_names length=%d",
2517 category_names
? (int) g_slist_length(category_names
) : -1);
2518 /* drop category information */
2519 if (category_names
) {
2520 GSList
*entry
= category_names
;
2522 GHashTable
*cat_publications
;
2523 const gchar
*category
= entry
->data
;
2524 entry
= entry
->next
;
2525 SIPE_DEBUG_INFO("sipe_process_roaming_self: dropping category: %s", category
);
2526 cat_publications
= g_hash_table_lookup(sip
->our_publications
, category
);
2527 if (cat_publications
) {
2528 g_hash_table_remove(sip
->our_publications
, category
);
2529 SIPE_DEBUG_INFO("sipe_process_roaming_self: dropped category: %s", category
);
2533 g_slist_free(category_names
);
2534 /* filling our categories reflected in roaming data */
2535 for (node
= sipe_xml_child(xml
, "categories/category"); node
; node
= sipe_xml_twin(node
)) {
2537 const gchar
*name
= sipe_xml_attribute(node
, "name");
2538 guint container
= sipe_xml_int_attribute(node
, "container", -1);
2539 guint instance
= sipe_xml_int_attribute(node
, "instance", -1);
2540 guint version
= sipe_xml_int_attribute(node
, "version", 0);
2541 time_t publish_time
= (tmp
= sipe_xml_attribute(node
, "publishTime")) ?
2542 sipe_utils_str_to_time(tmp
) : 0;
2544 GHashTable
*cat_publications
= g_hash_table_lookup(sip
->our_publications
, name
);
2546 /* Ex. clear note: <category name="note"/> */
2547 if (container
== (guint
)-1) {
2550 do_update_status
= TRUE
;
2554 /* Ex. clear note: <category name="note" container="200"/> */
2555 if (instance
== (guint
)-1) {
2556 if (container
== 200) {
2559 do_update_status
= TRUE
;
2561 SIPE_DEBUG_INFO("sipe_process_roaming_self: removing publications for: %s/%u", name
, container
);
2562 sipe_remove_category_container_publications(
2563 sip
->our_publications
, name
, container
);
2567 /* key is <category><instance><container> */
2568 key
= g_strdup_printf("<%s><%u><%u>", name
, instance
, container
);
2569 SIPE_DEBUG_INFO("sipe_process_roaming_self: key=%s version=%d", key
, version
);
2571 /* capture all userState publication for later clean up if required */
2572 if (sipe_strequal(name
, "state") && (container
== 2 || container
== 3)) {
2573 const sipe_xml
*xn_state
= sipe_xml_child(node
, "state");
2575 if (xn_state
&& sipe_strequal(sipe_xml_attribute(xn_state
, "type"), "userState")) {
2576 struct sipe_publication
*publication
= g_new0(struct sipe_publication
, 1);
2577 publication
->category
= g_strdup(name
);
2578 publication
->instance
= instance
;
2579 publication
->container
= container
;
2580 publication
->version
= version
;
2582 if (!sip
->user_state_publications
) {
2583 sip
->user_state_publications
= g_hash_table_new_full(
2584 g_str_hash
, g_str_equal
,
2585 g_free
, (GDestroyNotify
)free_publication
);
2587 g_hash_table_insert(sip
->user_state_publications
, g_strdup(key
), publication
);
2588 SIPE_DEBUG_INFO("sipe_process_roaming_self: added to user_state_publications key=%s version=%d",
2593 if (sipe_is_our_publication(sipe_private
, key
)) {
2594 struct sipe_publication
*publication
= g_new0(struct sipe_publication
, 1);
2596 publication
->category
= g_strdup(name
);
2597 publication
->instance
= instance
;
2598 publication
->container
= container
;
2599 publication
->version
= version
;
2601 /* filling publication->availability */
2602 if (sipe_strequal(name
, "state")) {
2603 const sipe_xml
*xn_state
= sipe_xml_child(node
, "state");
2604 const sipe_xml
*xn_avail
= sipe_xml_child(xn_state
, "availability");
2607 gchar
*avail_str
= sipe_xml_data(xn_avail
);
2609 publication
->availability
= atoi(avail_str
);
2613 /* for calendarState */
2614 if (xn_state
&& sipe_strequal(sipe_xml_attribute(xn_state
, "type"), "calendarState")) {
2615 const sipe_xml
*xn_activity
= sipe_xml_child(xn_state
, "activity");
2616 struct sipe_cal_event
*event
= g_new0(struct sipe_cal_event
, 1);
2618 event
->start_time
= sipe_utils_str_to_time(sipe_xml_attribute(xn_state
, "startTime"));
2620 if (sipe_strequal(sipe_xml_attribute(xn_activity
, "token"),
2621 sipe_activity_map
[SIPE_ACTIVITY_IN_MEETING
].token
))
2623 event
->is_meeting
= TRUE
;
2626 event
->subject
= sipe_xml_data(sipe_xml_child(xn_state
, "meetingSubject"));
2627 event
->location
= sipe_xml_data(sipe_xml_child(xn_state
, "meetingLocation"));
2629 publication
->cal_event_hash
= sipe_cal_event_hash(event
);
2630 SIPE_DEBUG_INFO("sipe_process_roaming_self: hash=%s",
2631 publication
->cal_event_hash
);
2632 sipe_cal_event_free(event
);
2635 /* filling publication->note */
2636 if (sipe_strequal(name
, "note")) {
2637 const sipe_xml
*xn_body
= sipe_xml_child(node
, "note/body");
2639 if (!has_note_cleaned
) {
2640 has_note_cleaned
= TRUE
;
2644 sip
->note_since
= publish_time
;
2646 do_update_status
= TRUE
;
2649 g_free(publication
->note
);
2650 publication
->note
= NULL
;
2654 publication
->note
= g_markup_escape_text((tmp
= sipe_xml_data(xn_body
)), -1);
2656 if (publish_time
>= sip
->note_since
) {
2658 sip
->note
= g_strdup(publication
->note
);
2659 sip
->note_since
= publish_time
;
2660 sip
->is_oof_note
= sipe_strequal(sipe_xml_attribute(xn_body
, "type"), "OOF");
2662 do_update_status
= TRUE
;
2667 /* filling publication->fb_start_str, free_busy_base64, working_hours_xml_str */
2668 if (sipe_strequal(name
, "calendarData") && (publication
->container
== 300)) {
2669 const sipe_xml
*xn_free_busy
= sipe_xml_child(node
, "calendarData/freeBusy");
2670 const sipe_xml
*xn_working_hours
= sipe_xml_child(node
, "calendarData/WorkingHours");
2672 publication
->fb_start_str
= g_strdup(sipe_xml_attribute(xn_free_busy
, "startTime"));
2673 publication
->free_busy_base64
= sipe_xml_data(xn_free_busy
);
2675 if (xn_working_hours
) {
2676 publication
->working_hours_xml_str
= sipe_xml_stringify(xn_working_hours
);
2680 if (!cat_publications
) {
2681 cat_publications
= g_hash_table_new_full(
2682 g_str_hash
, g_str_equal
,
2683 g_free
, (GDestroyNotify
)free_publication
);
2684 g_hash_table_insert(sip
->our_publications
, g_strdup(name
), cat_publications
);
2685 SIPE_DEBUG_INFO("sipe_process_roaming_self: added GHashTable cat=%s", name
);
2687 g_hash_table_insert(cat_publications
, g_strdup(key
), publication
);
2688 SIPE_DEBUG_INFO("sipe_process_roaming_self: added key=%s version=%d", key
, version
);
2692 /* aggregateState (not an our publication) from 2-nd container */
2693 if (sipe_strequal(name
, "state") && container
== 2) {
2694 const sipe_xml
*xn_state
= sipe_xml_child(node
, "state");
2696 if (xn_state
&& sipe_strequal(sipe_xml_attribute(xn_state
, "type"), "aggregateState")) {
2697 const sipe_xml
*xn_avail
= sipe_xml_child(xn_state
, "availability");
2700 gchar
*avail_str
= sipe_xml_data(xn_avail
);
2702 aggreg_avail
= atoi(avail_str
);
2707 do_update_status
= TRUE
;
2711 /* userProperties published by server from AD */
2712 if (!sip
->csta
&& sipe_strequal(name
, "userProperties")) {
2713 const sipe_xml
*line
;
2714 /* line, for Remote Call Control (RCC) */
2715 for (line
= sipe_xml_child(node
, "userProperties/lines/line"); line
; line
= sipe_xml_twin(line
)) {
2716 const gchar
*line_server
= sipe_xml_attribute(line
, "lineServer");
2717 const gchar
*line_type
= sipe_xml_attribute(line
, "lineType");
2720 if (!line_server
|| !(sipe_strequal(line_type
, "Rcc") || sipe_strequal(line_type
, "Dual"))) continue;
2722 line_uri
= sipe_xml_data(line
);
2724 SIPE_DEBUG_INFO("sipe_process_roaming_self: line_uri=%s server=%s", line_uri
, line_server
);
2725 sip_csta_open(sipe_private
, line_uri
, line_server
);
2733 SIPE_DEBUG_INFO("sipe_process_roaming_self: sip->our_publications size=%d",
2734 sip
->our_publications
? (int) g_hash_table_size(sip
->our_publications
) : -1);
2737 for (node
= sipe_xml_child(xml
, "containers/container"); node
; node
= sipe_xml_twin(node
)) {
2738 guint id
= sipe_xml_int_attribute(node
, "id", 0);
2739 struct sipe_container
*container
= sipe_find_container(sipe_private
, id
);
2742 sip
->containers
= g_slist_remove(sip
->containers
, container
);
2743 SIPE_DEBUG_INFO("sipe_process_roaming_self: removed existing container id=%d v%d", container
->id
, container
->version
);
2744 free_container(container
);
2746 container
= g_new0(struct sipe_container
, 1);
2748 container
->version
= sipe_xml_int_attribute(node
, "version", 0);
2749 sip
->containers
= g_slist_append(sip
->containers
, container
);
2750 SIPE_DEBUG_INFO("sipe_process_roaming_self: added container id=%d v%d", container
->id
, container
->version
);
2752 for (node2
= sipe_xml_child(node
, "member"); node2
; node2
= sipe_xml_twin(node2
)) {
2753 struct sipe_container_member
*member
= g_new0(struct sipe_container_member
, 1);
2754 member
->type
= g_strdup(sipe_xml_attribute(node2
, "type"));
2755 member
->value
= g_strdup(sipe_xml_attribute(node2
, "value"));
2756 container
->members
= g_slist_append(container
->members
, member
);
2757 SIPE_DEBUG_INFO("sipe_process_roaming_self: added container member type=%s value=%s",
2758 member
->type
, member
->value
? member
->value
: "");
2762 SIPE_DEBUG_INFO("sipe_process_roaming_self: sip->access_level_set=%s", sip
->access_level_set
? "TRUE" : "FALSE");
2763 if (!sip
->access_level_set
&& sipe_xml_child(xml
, "containers")) {
2764 char *container_xmls
= NULL
;
2765 int sameEnterpriseAL
= sipe_find_access_level(sipe_private
, "sameEnterprise", NULL
, NULL
);
2766 int federatedAL
= sipe_find_access_level(sipe_private
, "federated", NULL
, NULL
);
2768 SIPE_DEBUG_INFO("sipe_process_roaming_self: sameEnterpriseAL=%d", sameEnterpriseAL
);
2769 SIPE_DEBUG_INFO("sipe_process_roaming_self: federatedAL=%d", federatedAL
);
2770 /* initial set-up to let counterparties see your status */
2771 if (sameEnterpriseAL
< 0) {
2772 struct sipe_container
*container
= sipe_find_container(sipe_private
, 200);
2773 guint version
= container
? container
->version
: 0;
2774 sipe_send_container_members_prepare(200, version
, "add", "sameEnterprise", NULL
, &container_xmls
);
2776 if (federatedAL
< 0) {
2777 struct sipe_container
*container
= sipe_find_container(sipe_private
, 100);
2778 guint version
= container
? container
->version
: 0;
2779 sipe_send_container_members_prepare(100, version
, "add", "federated", NULL
, &container_xmls
);
2781 sip
->access_level_set
= TRUE
;
2783 if (container_xmls
) {
2784 sipe_send_set_container_members(sipe_private
, container_xmls
);
2786 g_free(container_xmls
);
2789 /* Refresh contacts' blocked status */
2790 sipe_refresh_blocked_status(sipe_private
);
2793 for (node
= sipe_xml_child(xml
, "subscribers/subscriber"); node
; node
= sipe_xml_twin(node
)) {
2795 const char *acknowledged
;
2799 user
= sipe_xml_attribute(node
, "user"); /* without 'sip:' prefix */
2800 if (!user
) continue;
2801 SIPE_DEBUG_INFO("sipe_process_roaming_self: user %s", user
);
2802 display_name
= g_strdup(sipe_xml_attribute(node
, "displayName"));
2803 uri
= sip_uri_from_name(user
);
2805 sipe_update_user_info(sipe_private
, uri
, ALIAS_PROP
, display_name
);
2807 acknowledged
= sipe_xml_attribute(node
, "acknowledged");
2808 if(sipe_strcase_equal(acknowledged
,"false")){
2809 SIPE_DEBUG_INFO("sipe_process_roaming_self: user added you %s", user
);
2810 if (!purple_find_buddy(sip
->account
, uri
)) {
2811 purple_account_request_add(sip
->account
, uri
, _("you"), display_name
, NULL
);
2814 hdr
= g_strdup_printf(
2816 "Content-Type: application/msrtc-presence-setsubscriber+xml\r\n", contact
);
2818 body
= g_strdup_printf(
2819 "<setSubscribers xmlns=\"http://schemas.microsoft.com/2006/09/sip/presence-subscribers\">"
2820 "<subscriber user=\"%s\" acknowledged=\"true\"/>"
2821 "</setSubscribers>", user
);
2823 sip_transport_service(sipe_private
,
2831 g_free(display_name
);
2838 /* Publish initial state if not yet.
2839 * Assuming this happens on initial responce to subscription to roaming-self
2840 * so we've already updated our roaming data in full.
2843 if (!sip
->initial_state_published
) {
2844 send_publish_category_initial(sipe_private
);
2845 sip
->initial_state_published
= TRUE
;
2847 sipe_schedule_seconds(sipe_private
,
2848 "<+update-calendar>",
2850 UPDATE_CALENDAR_DELAY
,
2851 (sipe_schedule_action
)sipe_core_update_calendar
,
2853 do_update_status
= FALSE
;
2854 } else if (aggreg_avail
) {
2856 g_free(sip
->status
);
2857 if (aggreg_avail
&& aggreg_avail
< 18000) { /* not offline */
2858 sip
->status
= g_strdup(sipe_get_status_by_availability(aggreg_avail
, NULL
));
2860 sip
->status
= g_strdup(SIPE_STATUS_ID_INVISIBLE
); /* not not let offline status switch us off */
2864 if (do_update_status
) {
2865 SIPE_DEBUG_INFO("sipe_process_roaming_self: switch to '%s' for the account", sip
->status
);
2866 sipe_set_purple_account_status_and_note(sip
->account
, sip
->status
, sip
->note
, sip
->do_not_publish
);
2872 /* IM Session (INVITE and MESSAGE methods) */
2874 /* EndPoints: "alice alisson" <sip:alice@atlanta.local>, <sip:bob@atlanta.local>;epid=ebca82d94d, <sip:carol@atlanta.local> */
2876 get_end_points (struct sipe_core_private
*sipe_private
,
2877 struct sip_session
*session
)
2881 if (session
== NULL
) {
2885 res
= g_strdup_printf("<sip:%s>", sipe_private
->username
);
2887 SIPE_DIALOG_FOREACH
{
2889 res
= g_strdup_printf("%s, <%s>", res
, dialog
->with
);
2892 if (dialog
->theirepid
) {
2894 res
= g_strdup_printf("%s;epid=%s", res
, dialog
->theirepid
);
2897 } SIPE_DIALOG_FOREACH_END
;
2903 process_options_response(SIPE_UNUSED_PARAMETER
struct sipe_core_private
*sipe_private
,
2905 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
2907 gboolean ret
= TRUE
;
2909 if (msg
->response
!= 200) {
2910 SIPE_DEBUG_INFO("process_options_response: OPTIONS response is %d", msg
->response
);
2914 SIPE_DEBUG_INFO("process_options_response: body:\n%s", msg
->body
? msg
->body
: "");
2920 * Asks UA/proxy about its capabilities.
2922 static void sipe_options_request(struct sipe_core_private
*sipe_private
,
2925 gchar
*to
= sip_uri(who
);
2926 gchar
*contact
= get_contact(sipe_private
);
2927 gchar
*request
= g_strdup_printf(
2928 "Accept: application/sdp\r\n"
2929 "Contact: %s\r\n", contact
);
2932 sip_transport_request(sipe_private
,
2939 process_options_response
);
2946 sipe_notify_user(struct sipe_core_private
*sipe_private
,
2947 struct sip_session
*session
,
2948 PurpleMessageFlags flags
,
2949 const gchar
*message
)
2951 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
2952 PurpleConversation
*conv
;
2954 if (!session
->backend_session
) {
2955 conv
= purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY
, session
->with
, sip
->account
);
2957 /* TEMPORARY HACK!! */
2958 conv
= (PurpleConversation
*) session
->backend_session
;
2960 purple_conversation_write(conv
, NULL
, message
, flags
, time(NULL
));
2964 sipe_present_info(struct sipe_core_private
*sipe_private
,
2965 struct sip_session
*session
,
2966 const gchar
*message
)
2968 sipe_notify_user(sipe_private
, session
, PURPLE_MESSAGE_SYSTEM
, message
);
2972 sipe_present_err(struct sipe_core_private
*sipe_private
,
2973 struct sip_session
*session
,
2974 const gchar
*message
)
2976 sipe_notify_user(sipe_private
, session
, PURPLE_MESSAGE_ERROR
, message
);
2980 sipe_present_message_undelivered_err(struct sipe_core_private
*sipe_private
,
2981 struct sip_session
*session
,
2985 const gchar
*message
)
2987 char *msg
, *msg_tmp
, *msg_tmp2
;
2990 msg_tmp
= message
? sipe_backend_markup_strip_html(message
) : NULL
;
2991 msg
= msg_tmp
? g_strdup_printf("<font color=\"#888888\"></b>%s<b></font>", msg_tmp
) : NULL
;
2993 /* Service unavailable; Server Internal Error; Server Time-out */
2994 if (sip_error
== 606 && sip_warning
== 309) { /* Not acceptable all. */ /* Message contents not allowed by policy */
2995 label
= _("Your message or invitation was not delivered, possibly because it contains a hyperlink or other content that the system administrator has blocked.");
2998 } else if (sip_error
== 503 || sip_error
== 500 || sip_error
== 504) {
2999 label
= _("This message was not delivered to %s because the service is not available");
3000 } else if (sip_error
== 486) { /* Busy Here */
3001 label
= _("This message was not delivered to %s because one or more recipients do not want to be disturbed");
3002 } else if (sip_error
== 415) { /* Unsupported media type */
3003 label
= _("This message was not delivered to %s because one or more recipients don't support this type of message");
3005 label
= _("This message was not delivered to %s because one or more recipients are offline");
3008 msg_tmp
= g_strdup_printf( "%s%s\n%s" ,
3009 msg_tmp2
= g_strdup_printf(label
, who
? who
: ""),
3012 sipe_present_err(sipe_private
, session
, msg_tmp
);
3020 process_message_response(struct sipe_core_private
*sipe_private
,
3022 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
3024 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
3025 gboolean ret
= TRUE
;
3026 gchar
*with
= parse_from(sipmsg_find_header(msg
, "To"));
3027 struct sip_session
*session
= sipe_session_find_im(sipe_private
, with
);
3028 struct sip_dialog
*dialog
;
3031 struct queued_message
*message
;
3034 SIPE_DEBUG_INFO_NOFORMAT("process_message_response: unable to find IM session");
3039 dialog
= sipe_dialog_find(session
, with
);
3041 SIPE_DEBUG_INFO_NOFORMAT("process_message_response: session outgoing dialog is NULL");
3046 cseq
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "CSeq"), NULL
, " ", NULL
);
3047 key
= g_strdup_printf("<%s><%d><MESSAGE><%s>", sipmsg_find_header(msg
, "Call-ID"), atoi(cseq
), with
);
3049 message
= g_hash_table_lookup(session
->unconfirmed_messages
, key
);
3051 if (msg
->response
>= 400) {
3052 PurpleBuddy
*pbuddy
;
3053 const char *alias
= with
;
3054 const char *warn_hdr
= sipmsg_find_header(msg
, "Warning");
3057 SIPE_DEBUG_INFO_NOFORMAT("process_message_response: MESSAGE response >= 400");
3060 gchar
**parts
= g_strsplit(warn_hdr
, " ", 2);
3062 warning
= atoi(parts
[0]);
3067 /* cancel file transfer as rejected by server */
3068 if (msg
->response
== 606 && /* Not acceptable all. */
3069 warning
== 309 && /* Message contents not allowed by policy */
3070 message
&& g_str_has_prefix(message
->content_type
, "text/x-msmsgsinvite"))
3072 GSList
*parsed_body
= sipe_ft_parse_msg_body(msg
->body
);
3073 sipe_ft_incoming_cancel(dialog
, parsed_body
);
3074 sipe_utils_nameval_free(parsed_body
);
3077 if ((pbuddy
= purple_find_buddy(sip
->account
, with
))) {
3078 alias
= purple_buddy_get_alias(pbuddy
);
3081 sipe_present_message_undelivered_err(sipe_private
, session
, msg
->response
, warning
, alias
, (message
? message
->body
: NULL
));
3083 /* drop dangling IM sessions: assume that BYE from remote never reached us */
3084 if (msg
->response
== 408 || /* Request timeout */
3085 msg
->response
== 480 || /* Temporarily Unavailable */
3086 msg
->response
== 481) { /* Call/Transaction Does Not Exist */
3087 SIPE_DEBUG_INFO_NOFORMAT("process_message_response: assuming dangling IM session, dropping it.");
3088 sip_transport_bye(sipe_private
, dialog
);
3093 const gchar
*message_id
= sipmsg_find_header(msg
, "Message-Id");
3095 g_hash_table_insert(session
->conf_unconfirmed_messages
, g_strdup(message_id
), g_strdup(message
->body
));
3096 SIPE_DEBUG_INFO("process_message_response: added message with id %s to conf_unconfirmed_messages(count=%d)",
3097 message_id
, g_hash_table_size(session
->conf_unconfirmed_messages
));
3100 g_hash_table_remove(session
->unconfirmed_messages
, key
);
3101 SIPE_DEBUG_INFO("process_message_response: removed message %s from unconfirmed_messages(count=%d)",
3102 key
, g_hash_table_size(session
->unconfirmed_messages
));
3108 if (ret
) sipe_im_process_queue(sipe_private
, session
);
3112 static void sipe_send_message(struct sipe_core_private
*sipe_private
,
3113 struct sip_dialog
*dialog
,
3114 const char *msg
, const char *content_type
)
3118 char *msgtext
= NULL
;
3119 const gchar
*msgr
= "";
3122 if (!g_str_has_prefix(content_type
, "text/x-msmsgsinvite")) {
3126 sipe_parse_html(msg
, &msgformat
, &msgtext
);
3127 SIPE_DEBUG_INFO("sipe_send_message: msgformat=%s", msgformat
);
3129 msgr_value
= sipmsg_get_msgr_string(msgformat
);
3132 msgr
= tmp2
= g_strdup_printf(";msgr=%s", msgr_value
);
3136 msgtext
= g_strdup(msg
);
3139 tmp
= get_contact(sipe_private
);
3140 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8\r\n");
3141 //hdr = g_strdup("Content-Type: text/rtf\r\n");
3142 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8;msgr=WAAtAE0ATQBTAC....AoADQA\r\nSupported: timer\r\n");
3143 if (content_type
== NULL
)
3144 content_type
= "text/plain";
3146 hdr
= g_strdup_printf("Contact: %s\r\nContent-Type: %s; charset=UTF-8%s\r\n", tmp
, content_type
, msgr
);
3150 sip_transport_request(sipe_private
,
3157 process_message_response
);
3164 sipe_im_process_queue (struct sipe_core_private
*sipe_private
,
3165 struct sip_session
* session
)
3167 GSList
*entry2
= session
->outgoing_message_queue
;
3169 struct queued_message
*msg
= entry2
->data
;
3171 /* for multiparty chat or conference */
3172 if (session
->is_multiparty
|| session
->focus_uri
) {
3173 gchar
*who
= sip_uri_self(sipe_private
);
3174 sipe_backend_chat_message(SIPE_CORE_PUBLIC
,
3181 SIPE_DIALOG_FOREACH
{
3183 struct queued_message
*message
;
3185 if (dialog
->outgoing_invite
) continue; /* do not send messages as INVITE is not responded. */
3187 message
= g_new0(struct queued_message
,1);
3188 message
->body
= g_strdup(msg
->body
);
3189 if (msg
->content_type
!= NULL
)
3190 message
->content_type
= g_strdup(msg
->content_type
);
3192 key
= g_strdup_printf("<%s><%d><MESSAGE><%s>", dialog
->callid
, (dialog
->cseq
) + 1, dialog
->with
);
3193 g_hash_table_insert(session
->unconfirmed_messages
, g_strdup(key
), message
);
3194 SIPE_DEBUG_INFO("sipe_im_process_queue: added message %s to unconfirmed_messages(count=%d)",
3195 key
, g_hash_table_size(session
->unconfirmed_messages
));
3198 sipe_send_message(sipe_private
, dialog
, msg
->body
, msg
->content_type
);
3199 } SIPE_DIALOG_FOREACH_END
;
3201 entry2
= sipe_session_dequeue_message(session
);
3206 sipe_refer_notify(struct sipe_core_private
*sipe_private
,
3207 struct sip_session
*session
,
3214 struct sip_dialog
*dialog
= sipe_dialog_find(session
, who
);
3216 hdr
= g_strdup_printf(
3218 "Subscription-State: %s\r\n"
3219 "Content-Type: message/sipfrag\r\n",
3220 status
>= 200 ? "terminated" : "active");
3222 body
= g_strdup_printf(
3223 "SIP/2.0 %d %s\r\n",
3226 sip_transport_request(sipe_private
,
3240 process_invite_response(struct sipe_core_private
*sipe_private
,
3241 struct sipmsg
*msg
, struct transaction
*trans
)
3243 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
3244 gchar
*with
= parse_from(sipmsg_find_header(msg
, "To"));
3245 struct sip_session
*session
;
3246 struct sip_dialog
*dialog
;
3249 struct queued_message
*message
;
3250 struct sipmsg
*request_msg
= trans
->msg
;
3252 const gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
3255 session
= sipe_session_find_chat_or_im(sipe_private
, callid
, with
);
3257 SIPE_DEBUG_INFO_NOFORMAT("process_invite_response: unable to find IM session");
3262 dialog
= sipe_dialog_find(session
, with
);
3264 SIPE_DEBUG_INFO_NOFORMAT("process_invite_response: session outgoing dialog is NULL");
3269 sipe_dialog_parse(dialog
, msg
, TRUE
);
3271 cseq
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "CSeq"), NULL
, " ", NULL
);
3272 key
= g_strdup_printf("<%s><%d><INVITE>", dialog
->callid
, atoi(cseq
));
3274 message
= g_hash_table_lookup(session
->unconfirmed_messages
, key
);
3276 if (msg
->response
!= 200) {
3277 PurpleBuddy
*pbuddy
;
3278 const char *alias
= with
;
3279 const char *warn_hdr
= sipmsg_find_header(msg
, "Warning");
3282 SIPE_DEBUG_INFO_NOFORMAT("process_invite_response: INVITE response not 200");
3285 gchar
**parts
= g_strsplit(warn_hdr
, " ", 2);
3287 warning
= atoi(parts
[0]);
3292 /* cancel file transfer as rejected by server */
3293 if (msg
->response
== 606 && /* Not acceptable all. */
3294 warning
== 309 && /* Message contents not allowed by policy */
3295 message
&& g_str_has_prefix(message
->content_type
, "text/x-msmsgsinvite"))
3297 GSList
*parsed_body
= sipe_ft_parse_msg_body(message
->body
);
3298 sipe_ft_incoming_cancel(dialog
, parsed_body
);
3299 sipe_utils_nameval_free(parsed_body
);
3302 if ((pbuddy
= purple_find_buddy(sip
->account
, with
))) {
3303 alias
= purple_buddy_get_alias(pbuddy
);
3307 sipe_present_message_undelivered_err(sipe_private
, session
, msg
->response
, warning
, alias
, message
->body
);
3309 gchar
*tmp_msg
= g_strdup_printf(_("Failed to invite %s"), alias
);
3310 sipe_present_err(sipe_private
, session
, tmp_msg
);
3314 sipe_dialog_remove(session
, with
);
3322 sip_transport_ack(sipe_private
, dialog
);
3323 dialog
->outgoing_invite
= NULL
;
3324 dialog
->is_established
= TRUE
;
3326 referred_by
= parse_from(sipmsg_find_header(request_msg
, "Referred-By"));
3328 sipe_refer_notify(sipe_private
, session
, referred_by
, 200, "OK");
3329 g_free(referred_by
);
3332 /* add user to chat if it is a multiparty session */
3333 if (session
->is_multiparty
) {
3334 sipe_backend_chat_add(session
->backend_session
,
3339 if(g_slist_find_custom(dialog
->supported
, "ms-text-format", (GCompareFunc
)g_ascii_strcasecmp
)) {
3340 SIPE_DEBUG_INFO_NOFORMAT("process_invite_response: remote system accepted message in INVITE");
3341 sipe_session_dequeue_message(session
);
3344 sipe_im_process_queue(sipe_private
, session
);
3346 g_hash_table_remove(session
->unconfirmed_messages
, key
);
3347 SIPE_DEBUG_INFO("process_invite_response: removed message %s from unconfirmed_messages(count=%d)",
3348 key
, g_hash_table_size(session
->unconfirmed_messages
));
3357 sipe_invite(struct sipe_core_private
*sipe_private
,
3358 struct sip_session
*session
,
3360 const gchar
*msg_body
,
3361 const gchar
*msg_content_type
,
3362 const gchar
*referred_by
,
3363 const gboolean is_triggered
)
3370 char *ms_text_format
= NULL
;
3371 gchar
*roster_manager
;
3373 gchar
*referred_by_str
;
3374 struct sip_dialog
*dialog
= sipe_dialog_find(session
, who
);
3376 if (dialog
&& dialog
->is_established
) {
3377 SIPE_DEBUG_INFO("session with %s already has a dialog open", who
);
3382 dialog
= sipe_dialog_add(session
);
3383 dialog
->callid
= session
->callid
? g_strdup(session
->callid
) : gencallid();
3384 dialog
->with
= g_strdup(who
);
3387 if (!(dialog
->ourtag
)) {
3388 dialog
->ourtag
= gentag();
3394 char *msgtext
= NULL
;
3396 const gchar
*msgr
= "";
3398 struct queued_message
*message
;
3401 if (!g_str_has_prefix(msg_content_type
, "text/x-msmsgsinvite")) {
3405 sipe_parse_html(msg_body
, &msgformat
, &msgtext
);
3406 SIPE_DEBUG_INFO("sipe_invite: msgformat=%s", msgformat
);
3408 msgr_value
= sipmsg_get_msgr_string(msgformat
);
3411 msgr
= tmp
= g_strdup_printf(";msgr=%s", msgr_value
);
3415 msgtext
= g_strdup(msg_body
);
3418 base64_msg
= g_base64_encode((guchar
*) msgtext
, strlen(msgtext
));
3419 ms_text_format
= g_strdup_printf(SIPE_INVITE_TEXT
,
3420 msg_content_type
? msg_content_type
: "text/plain",
3427 message
= g_new0(struct queued_message
,1);
3428 message
->body
= g_strdup(msg_body
);
3429 if (msg_content_type
!= NULL
)
3430 message
->content_type
= g_strdup(msg_content_type
);
3432 key
= g_strdup_printf("<%s><%d><INVITE>", dialog
->callid
, (dialog
->cseq
) + 1);
3433 g_hash_table_insert(session
->unconfirmed_messages
, g_strdup(key
), message
);
3434 SIPE_DEBUG_INFO("sipe_invite: added message %s to unconfirmed_messages(count=%d)",
3435 key
, g_hash_table_size(session
->unconfirmed_messages
));
3439 contact
= get_contact(sipe_private
);
3440 end_points
= get_end_points(sipe_private
, session
);
3441 self
= sip_uri_self(sipe_private
);
3442 roster_manager
= g_strdup_printf(
3443 "Roster-Manager: %s\r\n"
3444 "EndPoints: %s\r\n",
3447 referred_by_str
= referred_by
?
3449 "Referred-By: %s\r\n",
3452 hdr
= g_strdup_printf(
3453 "Supported: ms-sender\r\n"
3459 "Content-Type: application/sdp\r\n",
3460 sipe_strcase_equal(session
->roster_manager
, self
) ? roster_manager
: "",
3462 is_triggered
? "TriggeredInvite: TRUE\r\n" : "",
3463 is_triggered
|| session
->is_multiparty
? "Require: com.microsoft.rtc-multiparty\r\n" : "",
3465 ms_text_format
? ms_text_format
: "");
3466 g_free(ms_text_format
);
3469 body
= g_strdup_printf(
3471 "o=- 0 0 IN IP4 %s\r\n"
3475 "m=%s %d sip null\r\n"
3476 "a=accept-types:" SDP_ACCEPT_TYPES
"\r\n",
3477 sipe_backend_network_ip_address(),
3478 sipe_backend_network_ip_address(),
3479 SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
) ? "message" : "x-ms-message",
3480 sip_transport_port(sipe_private
));
3482 dialog
->outgoing_invite
= sip_transport_request(sipe_private
,
3489 process_invite_response
);
3492 g_free(roster_manager
);
3494 g_free(referred_by_str
);
3501 sipe_convo_closed(PurpleConnection
* gc
, const char *who
)
3503 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
3505 SIPE_DEBUG_INFO("conversation with %s closed", who
);
3506 sipe_session_close(sipe_private
,
3507 sipe_session_find_im(sipe_private
, who
));
3511 sipe_chat_leave (PurpleConnection
*gc
, int id
)
3513 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
3514 struct sip_session
*session
= sipe_session_find_chat_by_id(sipe_private
,
3517 sipe_session_close(sipe_private
, session
);
3520 int sipe_im_send(PurpleConnection
*gc
, const char *who
, const char *what
,
3521 SIPE_UNUSED_PARAMETER PurpleMessageFlags flags
)
3523 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
3524 struct sip_session
*session
;
3525 struct sip_dialog
*dialog
;
3526 gchar
*uri
= sip_uri(who
);
3528 SIPE_DEBUG_INFO("sipe_im_send what='%s'", what
);
3530 session
= sipe_session_find_or_add_im(sipe_private
, uri
);
3531 dialog
= sipe_dialog_find(session
, uri
);
3533 // Queue the message
3534 sipe_session_enqueue_message(session
, what
, NULL
);
3536 if (dialog
&& !dialog
->outgoing_invite
) {
3537 sipe_im_process_queue(sipe_private
, session
);
3538 } else if (!dialog
|| !dialog
->outgoing_invite
) {
3539 // Need to send the INVITE to get the outgoing dialog setup
3540 sipe_invite(sipe_private
, session
, uri
, what
, NULL
, NULL
, FALSE
);
3547 int sipe_chat_send(PurpleConnection
*gc
, int id
, const char *what
,
3548 SIPE_UNUSED_PARAMETER PurpleMessageFlags flags
)
3550 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
3551 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
3552 struct sip_session
*session
;
3554 SIPE_DEBUG_INFO("sipe_chat_send what='%s'", what
);
3556 session
= sipe_session_find_chat_by_id(sipe_private
, id
);
3558 // Queue the message
3559 if (session
&& session
->dialogs
) {
3560 sipe_session_enqueue_message(session
,what
,NULL
);
3561 sipe_im_process_queue(sipe_private
, session
);
3563 gchar
*chat_name
= purple_find_chat(sip
->gc
, id
)->name
;
3564 const gchar
*proto_chat_id
= sipe_chat_find_name(chat_name
);
3566 SIPE_DEBUG_INFO("sipe_chat_send: chat_name='%s'", chat_name
? chat_name
: "NULL");
3567 SIPE_DEBUG_INFO("sipe_chat_send: proto_chat_id='%s'", proto_chat_id
? proto_chat_id
: "NULL");
3569 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
3570 struct sip_session
*session
= sipe_session_add_chat(sipe_private
);
3572 session
->is_multiparty
= FALSE
;
3573 session
->focus_uri
= g_strdup(proto_chat_id
);
3574 sipe_session_enqueue_message(session
, what
, NULL
);
3575 sipe_invite_conf_focus(sipe_private
, session
);
3583 * Returns 2005-style activity and Availability.
3585 * @param status Sipe statis id.
3588 sipe_get_act_avail_by_status_2005(const char *status
,
3592 int avail
= 300; /* online */
3593 int act
= 400; /* Available */
3595 if (sipe_strequal(status
, SIPE_STATUS_ID_AWAY
)) {
3597 //} else if (sipe_strequal(status, SIPE_STATUS_ID_LUNCH)) {
3599 } else if (sipe_strequal(status
, SIPE_STATUS_ID_BRB
)) {
3601 } else if (sipe_strequal(status
, SIPE_STATUS_ID_AVAILABLE
)) {
3603 //} else if (sipe_strequal(status, SIPE_STATUS_ID_ON_PHONE)) {
3605 } else if (sipe_strequal(status
, SIPE_STATUS_ID_BUSY
) ||
3606 sipe_strequal(status
, SIPE_STATUS_ID_DND
)) {
3608 } else if (sipe_strequal(status
, SIPE_STATUS_ID_INVISIBLE
) ||
3609 sipe_strequal(status
, SIPE_STATUS_ID_OFFLINE
)) {
3610 avail
= 0; /* offline */
3613 act
= 400; /* Available */
3616 if (activity
) *activity
= act
;
3617 if (availability
) *availability
= avail
;
3623 * @param activity 2005 aggregated activity. Ex.: 600
3624 * @param availablity 2005 aggregated availablity. Ex.: 300
3627 sipe_get_status_by_act_avail_2005(const int activity
,
3628 const int availablity
,
3629 char **activity_desc
)
3631 const char *status_id
= NULL
;
3632 const char *act
= NULL
;
3634 if (activity
< 150) {
3635 status_id
= SIPE_STATUS_ID_AWAY
;
3636 } else if (activity
< 200) {
3637 //status_id = SIPE_STATUS_ID_LUNCH;
3638 status_id
= SIPE_STATUS_ID_AWAY
;
3639 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_LUNCH
);
3640 } else if (activity
< 300) {
3641 //status_id = SIPE_STATUS_ID_IDLE;
3642 status_id
= SIPE_STATUS_ID_AWAY
;
3643 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_INACTIVE
);
3644 } else if (activity
< 400) {
3645 status_id
= SIPE_STATUS_ID_BRB
;
3646 } else if (activity
< 500) {
3647 status_id
= SIPE_STATUS_ID_AVAILABLE
;
3648 } else if (activity
< 600) {
3649 //status_id = SIPE_STATUS_ID_ON_PHONE;
3650 status_id
= SIPE_STATUS_ID_BUSY
;
3651 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_ON_PHONE
);
3652 } else if (activity
< 700) {
3653 status_id
= SIPE_STATUS_ID_BUSY
;
3654 } else if (activity
< 800) {
3655 status_id
= SIPE_STATUS_ID_AWAY
;
3657 status_id
= SIPE_STATUS_ID_AVAILABLE
;
3660 if (availablity
< 100)
3661 status_id
= SIPE_STATUS_ID_OFFLINE
;
3663 if (activity_desc
&& act
) {
3664 g_free(*activity_desc
);
3665 *activity_desc
= g_strdup(act
);
3672 * [MS-PRES] Table 3: Conversion of legacyInterop elements and attributes to MSRTC elements and attributes.
3675 sipe_get_status_by_availability(int avail
,
3676 char** activity_desc
)
3679 const char *act
= NULL
;
3682 status
= SIPE_STATUS_ID_OFFLINE
;
3683 } else if (avail
< 4500) {
3684 status
= SIPE_STATUS_ID_AVAILABLE
;
3685 } else if (avail
< 6000) {
3686 //status = SIPE_STATUS_ID_IDLE;
3687 status
= SIPE_STATUS_ID_AVAILABLE
;
3688 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_INACTIVE
);
3689 } else if (avail
< 7500) {
3690 status
= SIPE_STATUS_ID_BUSY
;
3691 } else if (avail
< 9000) {
3692 //status = SIPE_STATUS_ID_BUSYIDLE;
3693 status
= SIPE_STATUS_ID_BUSY
;
3694 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_BUSYIDLE
);
3695 } else if (avail
< 12000) {
3696 status
= SIPE_STATUS_ID_DND
;
3697 } else if (avail
< 15000) {
3698 status
= SIPE_STATUS_ID_BRB
;
3699 } else if (avail
< 18000) {
3700 status
= SIPE_STATUS_ID_AWAY
;
3702 status
= SIPE_STATUS_ID_OFFLINE
;
3705 if (activity_desc
&& act
) {
3706 g_free(*activity_desc
);
3707 *activity_desc
= g_strdup(act
);
3714 * Returns 2007-style availability value
3716 * @param sipe_status_id (in)
3717 * @param activity_token (out) Must be g_free()'d after use if consumed.
3720 sipe_get_availability_by_status(const char* sipe_status_id
, char** activity_token
)
3723 sipe_activity activity
= SIPE_ACTIVITY_UNSET
;
3725 if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_AWAY
)) {
3726 availability
= 15500;
3727 if (!activity_token
|| !(*activity_token
)) {
3728 activity
= SIPE_ACTIVITY_AWAY
;
3730 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_BRB
)) {
3731 availability
= 12500;
3732 activity
= SIPE_ACTIVITY_BRB
;
3733 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_DND
)) {
3734 availability
= 9500;
3735 activity
= SIPE_ACTIVITY_DND
;
3736 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_BUSY
)) {
3737 availability
= 6500;
3738 if (!activity_token
|| !(*activity_token
)) {
3739 activity
= SIPE_ACTIVITY_BUSY
;
3741 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_AVAILABLE
)) {
3742 availability
= 3500;
3743 activity
= SIPE_ACTIVITY_ONLINE
;
3744 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_UNKNOWN
)) {
3747 // Offline or invisible
3748 availability
= 18500;
3749 activity
= SIPE_ACTIVITY_OFFLINE
;
3752 if (activity_token
) {
3753 *activity_token
= g_strdup(sipe_activity_map
[activity
].token
);
3755 return availability
;
3758 static void process_incoming_notify_rlmi(struct sipe_core_private
*sipe_private
,
3759 const gchar
*data
, unsigned len
)
3761 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
3763 sipe_xml
*xn_categories
;
3764 const sipe_xml
*xn_category
;
3765 const char *status
= NULL
;
3766 gboolean do_update_status
= FALSE
;
3767 gboolean has_note_cleaned
= FALSE
;
3768 gboolean has_free_busy_cleaned
= FALSE
;
3770 xn_categories
= sipe_xml_parse(data
, len
);
3771 uri
= sipe_xml_attribute(xn_categories
, "uri"); /* with 'sip:' prefix */
3773 for (xn_category
= sipe_xml_child(xn_categories
, "category");
3775 xn_category
= sipe_xml_twin(xn_category
) )
3777 const sipe_xml
*xn_node
;
3779 const char *attrVar
= sipe_xml_attribute(xn_category
, "name");
3780 time_t publish_time
= (tmp
= sipe_xml_attribute(xn_category
, "publishTime")) ?
3781 sipe_utils_str_to_time(tmp
) : 0;
3784 if (sipe_strequal(attrVar
, "contactCard"))
3786 const sipe_xml
*card
= sipe_xml_child(xn_category
, "contactCard");
3789 const sipe_xml
*node
;
3790 /* identity - Display Name and email */
3791 node
= sipe_xml_child(card
, "identity");
3793 char* display_name
= sipe_xml_data(
3794 sipe_xml_child(node
, "name/displayName"));
3795 char* email
= sipe_xml_data(
3796 sipe_xml_child(node
, "email"));
3798 sipe_update_user_info(sipe_private
, uri
, ALIAS_PROP
, display_name
);
3799 sipe_update_user_info(sipe_private
, uri
, EMAIL_PROP
, email
);
3801 g_free(display_name
);
3805 node
= sipe_xml_child(card
, "company");
3807 char* company
= sipe_xml_data(node
);
3808 sipe_update_user_info(sipe_private
, uri
, COMPANY_PROP
, company
);
3812 node
= sipe_xml_child(card
, "department");
3814 char* department
= sipe_xml_data(node
);
3815 sipe_update_user_info(sipe_private
, uri
, DEPARTMENT_PROP
, department
);
3819 node
= sipe_xml_child(card
, "title");
3821 char* title
= sipe_xml_data(node
);
3822 sipe_update_user_info(sipe_private
, uri
, TITLE_PROP
, title
);
3826 node
= sipe_xml_child(card
, "office");
3828 char* office
= sipe_xml_data(node
);
3829 sipe_update_user_info(sipe_private
, uri
, OFFICE_PROP
, office
);
3833 node
= sipe_xml_child(card
, "url");
3835 char* site
= sipe_xml_data(node
);
3836 sipe_update_user_info(sipe_private
, uri
, SITE_PROP
, site
);
3840 for (node
= sipe_xml_child(card
, "phone");
3842 node
= sipe_xml_twin(node
))
3844 const char *phone_type
= sipe_xml_attribute(node
, "type");
3845 char* phone
= sipe_xml_data(sipe_xml_child(node
, "uri"));
3846 char* phone_display_string
= sipe_xml_data(sipe_xml_child(node
, "displayString"));
3848 sipe_update_user_phone(sipe_private
, uri
, phone_type
, phone
, phone_display_string
);
3851 g_free(phone_display_string
);
3854 for (node
= sipe_xml_child(card
, "address");
3856 node
= sipe_xml_twin(node
))
3858 if (sipe_strequal(sipe_xml_attribute(node
, "type"), "work")) {
3859 char* street
= sipe_xml_data(sipe_xml_child(node
, "street"));
3860 char* city
= sipe_xml_data(sipe_xml_child(node
, "city"));
3861 char* state
= sipe_xml_data(sipe_xml_child(node
, "state"));
3862 char* zipcode
= sipe_xml_data(sipe_xml_child(node
, "zipcode"));
3863 char* country_code
= sipe_xml_data(sipe_xml_child(node
, "countryCode"));
3865 sipe_update_user_info(sipe_private
, uri
, ADDRESS_STREET_PROP
, street
);
3866 sipe_update_user_info(sipe_private
, uri
, ADDRESS_CITY_PROP
, city
);
3867 sipe_update_user_info(sipe_private
, uri
, ADDRESS_STATE_PROP
, state
);
3868 sipe_update_user_info(sipe_private
, uri
, ADDRESS_ZIPCODE_PROP
, zipcode
);
3869 sipe_update_user_info(sipe_private
, uri
, ADDRESS_COUNTRYCODE_PROP
, country_code
);
3875 g_free(country_code
);
3883 else if (sipe_strequal(attrVar
, "note"))
3886 struct sipe_buddy
*sbuddy
= g_hash_table_lookup(sipe_private
->buddies
, uri
);
3888 if (!has_note_cleaned
) {
3889 has_note_cleaned
= TRUE
;
3891 g_free(sbuddy
->note
);
3892 sbuddy
->note
= NULL
;
3893 sbuddy
->is_oof_note
= FALSE
;
3894 sbuddy
->note_since
= publish_time
;
3896 do_update_status
= TRUE
;
3898 if (sbuddy
&& (publish_time
>= sbuddy
->note_since
)) {
3899 /* clean up in case no 'note' element is supplied
3900 * which indicate note removal in client
3902 g_free(sbuddy
->note
);
3903 sbuddy
->note
= NULL
;
3904 sbuddy
->is_oof_note
= FALSE
;
3905 sbuddy
->note_since
= publish_time
;
3907 xn_node
= sipe_xml_child(xn_category
, "note/body");
3910 sbuddy
->note
= g_markup_escape_text((tmp
= sipe_xml_data(xn_node
)), -1);
3912 sbuddy
->is_oof_note
= sipe_strequal(sipe_xml_attribute(xn_node
, "type"), "OOF");
3913 sbuddy
->note_since
= publish_time
;
3915 SIPE_DEBUG_INFO("process_incoming_notify_rlmi: uri(%s), note(%s)",
3916 uri
, sbuddy
->note
? sbuddy
->note
: "");
3918 /* to trigger UI refresh in case no status info is supplied in this update */
3919 do_update_status
= TRUE
;
3924 else if(sipe_strequal(attrVar
, "state"))
3928 const sipe_xml
*xn_availability
;
3929 const sipe_xml
*xn_activity
;
3930 const sipe_xml
*xn_meeting_subject
;
3931 const sipe_xml
*xn_meeting_location
;
3932 struct sipe_buddy
*sbuddy
= uri
? g_hash_table_lookup(sipe_private
->buddies
, uri
) : NULL
;
3934 xn_node
= sipe_xml_child(xn_category
, "state");
3935 if (!xn_node
) continue;
3936 xn_availability
= sipe_xml_child(xn_node
, "availability");
3937 if (!xn_availability
) continue;
3938 xn_activity
= sipe_xml_child(xn_node
, "activity");
3939 xn_meeting_subject
= sipe_xml_child(xn_node
, "meetingSubject");
3940 xn_meeting_location
= sipe_xml_child(xn_node
, "meetingLocation");
3942 tmp
= sipe_xml_data(xn_availability
);
3943 availability
= atoi(tmp
);
3946 /* activity, meeting_subject, meeting_location */
3951 g_free(sbuddy
->activity
);
3952 sbuddy
->activity
= NULL
;
3954 const char *token
= sipe_xml_attribute(xn_activity
, "token");
3955 const sipe_xml
*xn_custom
= sipe_xml_child(xn_activity
, "custom");
3958 if (!is_empty(token
)) {
3959 sbuddy
->activity
= g_strdup(sipe_get_activity_desc_by_token(token
));
3961 /* from custom element */
3963 char *custom
= sipe_xml_data(xn_custom
);
3965 if (!is_empty(custom
)) {
3966 sbuddy
->activity
= custom
;
3972 /* meeting_subject */
3973 g_free(sbuddy
->meeting_subject
);
3974 sbuddy
->meeting_subject
= NULL
;
3975 if (xn_meeting_subject
) {
3976 char *meeting_subject
= sipe_xml_data(xn_meeting_subject
);
3978 if (!is_empty(meeting_subject
)) {
3979 sbuddy
->meeting_subject
= meeting_subject
;
3980 meeting_subject
= NULL
;
3982 g_free(meeting_subject
);
3984 /* meeting_location */
3985 g_free(sbuddy
->meeting_location
);
3986 sbuddy
->meeting_location
= NULL
;
3987 if (xn_meeting_location
) {
3988 char *meeting_location
= sipe_xml_data(xn_meeting_location
);
3990 if (!is_empty(meeting_location
)) {
3991 sbuddy
->meeting_location
= meeting_location
;
3992 meeting_location
= NULL
;
3994 g_free(meeting_location
);
3997 status
= sipe_get_status_by_availability(availability
, &tmp
);
3998 if (sbuddy
->activity
&& tmp
) {
3999 char *tmp2
= sbuddy
->activity
;
4001 sbuddy
->activity
= g_strdup_printf("%s, %s", sbuddy
->activity
, tmp
);
4005 sbuddy
->activity
= tmp
;
4009 do_update_status
= TRUE
;
4012 else if(sipe_strequal(attrVar
, "calendarData"))
4014 struct sipe_buddy
*sbuddy
= uri
? g_hash_table_lookup(sipe_private
->buddies
, uri
) : NULL
;
4015 const sipe_xml
*xn_free_busy
= sipe_xml_child(xn_category
, "calendarData/freeBusy");
4016 const sipe_xml
*xn_working_hours
= sipe_xml_child(xn_category
, "calendarData/WorkingHours");
4018 if (sbuddy
&& xn_free_busy
) {
4019 if (!has_free_busy_cleaned
) {
4020 has_free_busy_cleaned
= TRUE
;
4022 g_free(sbuddy
->cal_start_time
);
4023 sbuddy
->cal_start_time
= NULL
;
4025 g_free(sbuddy
->cal_free_busy_base64
);
4026 sbuddy
->cal_free_busy_base64
= NULL
;
4028 g_free(sbuddy
->cal_free_busy
);
4029 sbuddy
->cal_free_busy
= NULL
;
4031 sbuddy
->cal_free_busy_published
= publish_time
;
4034 if (publish_time
>= sbuddy
->cal_free_busy_published
) {
4035 g_free(sbuddy
->cal_start_time
);
4036 sbuddy
->cal_start_time
= g_strdup(sipe_xml_attribute(xn_free_busy
, "startTime"));
4038 sbuddy
->cal_granularity
= sipe_strcase_equal(sipe_xml_attribute(xn_free_busy
, "granularity"), "PT15M") ?
4041 g_free(sbuddy
->cal_free_busy_base64
);
4042 sbuddy
->cal_free_busy_base64
= sipe_xml_data(xn_free_busy
);
4044 g_free(sbuddy
->cal_free_busy
);
4045 sbuddy
->cal_free_busy
= NULL
;
4047 sbuddy
->cal_free_busy_published
= publish_time
;
4049 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
);
4053 if (sbuddy
&& xn_working_hours
) {
4054 sipe_cal_parse_working_hours(xn_working_hours
, sbuddy
);
4059 if (do_update_status
) {
4060 if (!status
) { /* no status category in this update, using contact's current status */
4061 PurpleBuddy
*pbuddy
= purple_find_buddy((PurpleAccount
*)sip
->account
, uri
);
4062 const PurplePresence
*presence
= purple_buddy_get_presence(pbuddy
);
4063 const PurpleStatus
*pstatus
= purple_presence_get_active_status(presence
);
4064 status
= purple_status_get_id(pstatus
);
4067 SIPE_DEBUG_INFO("process_incoming_notify_rlmi: %s", status
);
4068 sipe_got_user_status(sipe_private
, uri
, status
);
4071 sipe_xml_free(xn_categories
);
4074 static void sipe_subscribe_poolfqdn_resource_uri(const char *host
,
4076 struct sipe_core_private
*sipe_private
)
4078 struct presence_batched_routed
*payload
= g_malloc(sizeof(struct presence_batched_routed
));
4079 SIPE_DEBUG_INFO("process_incoming_notify_rlmi_resub: pool(%s)", host
);
4080 payload
->host
= g_strdup(host
);
4081 payload
->buddies
= server
;
4082 sipe_subscribe_presence_batched_routed(sipe_private
,
4084 sipe_subscribe_presence_batched_routed_free(payload
);
4087 static void process_incoming_notify_rlmi_resub(struct sipe_core_private
*sipe_private
,
4088 const gchar
*data
, unsigned len
)
4091 const sipe_xml
*xn_resource
;
4092 GHashTable
*servers
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
4097 xn_list
= sipe_xml_parse(data
, len
);
4099 for (xn_resource
= sipe_xml_child(xn_list
, "resource");
4101 xn_resource
= sipe_xml_twin(xn_resource
) )
4103 const char *uri
, *state
;
4104 const sipe_xml
*xn_instance
;
4106 xn_instance
= sipe_xml_child(xn_resource
, "instance");
4107 if (!xn_instance
) continue;
4109 uri
= sipe_xml_attribute(xn_resource
, "uri");
4110 state
= sipe_xml_attribute(xn_instance
, "state");
4111 SIPE_DEBUG_INFO("process_incoming_notify_rlmi_resub: uri(%s),state(%s)", uri
, state
);
4113 if (strstr(state
, "resubscribe")) {
4114 const char *poolFqdn
= sipe_xml_attribute(xn_instance
, "poolFqdn");
4116 if (poolFqdn
) { //[MS-PRES] Section 3.4.5.1.3 Processing Details
4117 gchar
*user
= g_strdup(uri
);
4118 host
= g_strdup(poolFqdn
);
4119 server
= g_hash_table_lookup(servers
, host
);
4120 server
= g_slist_append(server
, user
);
4121 g_hash_table_insert(servers
, host
, server
);
4123 sipe_subscribe_presence_single(sipe_private
,
4129 /* Send out any deferred poolFqdn subscriptions */
4130 g_hash_table_foreach(servers
, (GHFunc
) sipe_subscribe_poolfqdn_resource_uri
, sipe_private
);
4131 g_hash_table_destroy(servers
);
4133 sipe_xml_free(xn_list
);
4136 static void process_incoming_notify_pidf(struct sipe_core_private
*sipe_private
,
4137 const gchar
*data
, unsigned len
)
4141 gchar
*activity
= NULL
;
4143 const sipe_xml
*basicstatus
= NULL
, *tuple
, *status
;
4144 gboolean isonline
= FALSE
;
4145 const sipe_xml
*display_name_node
;
4147 pidf
= sipe_xml_parse(data
, len
);
4149 SIPE_DEBUG_INFO("process_incoming_notify_pidf: no parseable pidf:%s", data
);
4153 if ((tuple
= sipe_xml_child(pidf
, "tuple")))
4155 if ((status
= sipe_xml_child(tuple
, "status"))) {
4156 basicstatus
= sipe_xml_child(status
, "basic");
4161 SIPE_DEBUG_INFO_NOFORMAT("process_incoming_notify_pidf: no basic found");
4162 sipe_xml_free(pidf
);
4166 getbasic
= sipe_xml_data(basicstatus
);
4168 SIPE_DEBUG_INFO_NOFORMAT("process_incoming_notify_pidf: no basic data found");
4169 sipe_xml_free(pidf
);
4173 SIPE_DEBUG_INFO("process_incoming_notify_pidf: basic-status(%s)", getbasic
);
4174 if (strstr(getbasic
, "open")) {
4179 uri
= sip_uri(sipe_xml_attribute(pidf
, "entity")); /* with 'sip:' prefix */ /* AOL comes without the prefix */
4181 display_name_node
= sipe_xml_child(pidf
, "display-name");
4182 if (display_name_node
) {
4183 char * display_name
= sipe_xml_data(display_name_node
);
4185 sipe_update_user_info(sipe_private
, uri
, ALIAS_PROP
, display_name
);
4186 g_free(display_name
);
4189 if ((tuple
= sipe_xml_child(pidf
, "tuple"))) {
4190 if ((status
= sipe_xml_child(tuple
, "status"))) {
4191 if ((basicstatus
= sipe_xml_child(status
, "activities"))) {
4192 if ((basicstatus
= sipe_xml_child(basicstatus
, "activity"))) {
4193 activity
= sipe_xml_data(basicstatus
);
4194 SIPE_DEBUG_INFO("process_incoming_notify_pidf: activity(%s)", activity
);
4201 const gchar
* status_id
= NULL
;
4203 if (sipe_strequal(activity
, sipe_activity_map
[SIPE_ACTIVITY_BUSY
].token
)) {
4204 status_id
= SIPE_STATUS_ID_BUSY
;
4205 } else if (sipe_strequal(activity
, sipe_activity_map
[SIPE_ACTIVITY_AWAY
].token
)) {
4206 status_id
= SIPE_STATUS_ID_AWAY
;
4211 status_id
= SIPE_STATUS_ID_AVAILABLE
;
4214 SIPE_DEBUG_INFO("process_incoming_notify_pidf: status_id(%s)", status_id
);
4215 sipe_got_user_status(sipe_private
, uri
, status_id
);
4217 sipe_got_user_status(sipe_private
, uri
, SIPE_STATUS_ID_OFFLINE
);
4222 sipe_xml_free(pidf
);
4227 sipe_user_info_has_updated(struct sipe_core_private
*sipe_private
,
4228 const sipe_xml
*xn_userinfo
)
4230 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
4231 const sipe_xml
*xn_states
;
4233 g_free(sip
->user_states
);
4234 sip
->user_states
= NULL
;
4235 if ((xn_states
= sipe_xml_child(xn_userinfo
, "states")) != NULL
) {
4236 gchar
*orig
= sip
->user_states
= sipe_xml_stringify(xn_states
);
4238 /* this is a hack-around to remove added newline after inner element,
4239 * state in this case, where it shouldn't be.
4240 * After several use of sipe_xml_stringify, amount of added newlines
4241 * grows significantly.
4244 gchar c
, *stripped
= orig
;
4245 while ((c
= *orig
++)) {
4246 if ((c
!= '\n') /* && (c != '\r') */) {
4254 /* Publish initial state if not yet.
4255 * Assuming this happens on initial responce to self subscription
4256 * so we've already updated our UserInfo.
4258 if (!sip
->initial_state_published
) {
4259 send_presence_soap(sipe_private
, FALSE
);
4261 sipe_schedule_seconds(sipe_private
,
4262 "<+update-calendar>",
4264 UPDATE_CALENDAR_DELAY
,
4265 (sipe_schedule_action
) sipe_core_update_calendar
,
4270 static void process_incoming_notify_msrtc(struct sipe_core_private
*sipe_private
,
4271 const gchar
*data
, unsigned len
)
4273 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
4274 char *activity
= NULL
;
4276 const char *status_id
= NULL
;
4279 char *self_uri
= sip_uri_self(sipe_private
);
4282 const char *device_name
= NULL
;
4283 const char *cal_start_time
= NULL
;
4284 const char *cal_granularity
= NULL
;
4285 char *cal_free_busy_base64
= NULL
;
4286 struct sipe_buddy
*sbuddy
;
4287 const sipe_xml
*node
;
4288 sipe_xml
*xn_presentity
;
4289 const sipe_xml
*xn_availability
;
4290 const sipe_xml
*xn_activity
;
4291 const sipe_xml
*xn_display_name
;
4292 const sipe_xml
*xn_email
;
4293 const sipe_xml
*xn_phone_number
;
4294 const sipe_xml
*xn_userinfo
;
4295 const sipe_xml
*xn_note
;
4296 const sipe_xml
*xn_oof
;
4297 const sipe_xml
*xn_state
;
4298 const sipe_xml
*xn_contact
;
4300 char *free_activity
;
4302 const char *user_avail_nil
;
4304 time_t user_avail_since
= 0;
4305 time_t activity_since
= 0;
4307 /* fix for Reuters environment on Linux */
4308 if (data
&& strstr(data
, "encoding=\"utf-16\"")) {
4310 tmp_data
= replace(data
, "encoding=\"utf-16\"", "encoding=\"utf-8\"");
4311 xn_presentity
= sipe_xml_parse(tmp_data
, strlen(tmp_data
));
4314 xn_presentity
= sipe_xml_parse(data
, len
);
4317 xn_availability
= sipe_xml_child(xn_presentity
, "availability");
4318 xn_activity
= sipe_xml_child(xn_presentity
, "activity");
4319 xn_display_name
= sipe_xml_child(xn_presentity
, "displayName");
4320 xn_email
= sipe_xml_child(xn_presentity
, "email");
4321 xn_phone_number
= sipe_xml_child(xn_presentity
, "phoneNumber");
4322 xn_userinfo
= sipe_xml_child(xn_presentity
, "userInfo");
4323 xn_oof
= xn_userinfo
? sipe_xml_child(xn_userinfo
, "oof") : NULL
;
4324 xn_state
= xn_userinfo
? sipe_xml_child(xn_userinfo
, "states/state"): NULL
;
4325 user_avail
= xn_state
? sipe_xml_int_attribute(xn_state
, "avail", 0) : 0;
4326 user_avail_since
= xn_state
? sipe_utils_str_to_time(sipe_xml_attribute(xn_state
, "since")) : 0;
4327 user_avail_nil
= xn_state
? sipe_xml_attribute(xn_state
, "nil") : NULL
;
4328 xn_contact
= xn_userinfo
? sipe_xml_child(xn_userinfo
, "contact") : NULL
;
4329 xn_note
= xn_userinfo
? sipe_xml_child(xn_userinfo
, "note") : NULL
;
4330 note
= xn_note
? sipe_xml_data(xn_note
) : NULL
;
4332 if (sipe_strequal(user_avail_nil
, "true")) { /* null-ed */
4334 user_avail_since
= 0;
4337 free_activity
= NULL
;
4339 name
= sipe_xml_attribute(xn_presentity
, "uri"); /* without 'sip:' prefix */
4340 uri
= sip_uri_from_name(name
);
4341 avl
= sipe_xml_int_attribute(xn_availability
, "aggregate", 0);
4342 epid
= sipe_xml_attribute(xn_availability
, "epid");
4343 act
= sipe_xml_int_attribute(xn_activity
, "aggregate", 0);
4345 status_id
= sipe_get_status_by_act_avail_2005(act
, avl
, &activity
);
4346 res_avail
= sipe_get_availability_by_status(status_id
, NULL
);
4347 if (user_avail
> res_avail
) {
4348 res_avail
= user_avail
;
4349 status_id
= sipe_get_status_by_availability(user_avail
, NULL
);
4352 if (xn_display_name
) {
4353 char *display_name
= g_strdup(sipe_xml_attribute(xn_display_name
, "displayName"));
4354 char *email
= xn_email
? g_strdup(sipe_xml_attribute(xn_email
, "email")) : NULL
;
4355 char *phone_label
= xn_phone_number
? g_strdup(sipe_xml_attribute(xn_phone_number
, "label")) : NULL
;
4356 char *phone_number
= xn_phone_number
? g_strdup(sipe_xml_attribute(xn_phone_number
, "number")) : NULL
;
4357 char *tel_uri
= sip_to_tel_uri(phone_number
);
4359 sipe_update_user_info(sipe_private
, uri
, ALIAS_PROP
, display_name
);
4360 sipe_update_user_info(sipe_private
, uri
, EMAIL_PROP
, email
);
4361 sipe_update_user_info(sipe_private
, uri
, PHONE_PROP
, tel_uri
);
4362 sipe_update_user_info(sipe_private
, uri
, PHONE_DISPLAY_PROP
, !is_empty(phone_label
) ? phone_label
: phone_number
);
4365 g_free(phone_label
);
4366 g_free(phone_number
);
4368 g_free(display_name
);
4373 for (node
= sipe_xml_child(xn_contact
, "tel"); node
; node
= sipe_xml_twin(node
))
4375 /* Ex.: <tel type="work">tel:+3222220000</tel> */
4376 const char *phone_type
= sipe_xml_attribute(node
, "type");
4377 char* phone
= sipe_xml_data(node
);
4379 sipe_update_user_phone(sipe_private
, uri
, phone_type
, phone
, NULL
);
4385 /* devicePresence */
4386 for (node
= sipe_xml_child(xn_presentity
, "devices/devicePresence"); node
; node
= sipe_xml_twin(node
)) {
4387 const sipe_xml
*xn_device_name
;
4388 const sipe_xml
*xn_calendar_info
;
4389 const sipe_xml
*xn_state
;
4393 if (sipe_strequal(sipe_xml_attribute(node
, "epid"), epid
)) {
4394 xn_device_name
= sipe_xml_child(node
, "deviceName");
4395 device_name
= xn_device_name
? sipe_xml_attribute(xn_device_name
, "name") : NULL
;
4399 xn_calendar_info
= sipe_xml_child(node
, "calendarInfo");
4400 if (xn_calendar_info
) {
4401 const char *cal_start_time_tmp
= sipe_xml_attribute(xn_calendar_info
, "startTime");
4403 if (cal_start_time
) {
4404 time_t cal_start_time_t
= sipe_utils_str_to_time(cal_start_time
);
4405 time_t cal_start_time_t_tmp
= sipe_utils_str_to_time(cal_start_time_tmp
);
4407 if (cal_start_time_t_tmp
> cal_start_time_t
) {
4408 cal_start_time
= cal_start_time_tmp
;
4409 cal_granularity
= sipe_xml_attribute(xn_calendar_info
, "granularity");
4410 g_free(cal_free_busy_base64
);
4411 cal_free_busy_base64
= sipe_xml_data(xn_calendar_info
);
4413 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
);
4416 cal_start_time
= cal_start_time_tmp
;
4417 cal_granularity
= sipe_xml_attribute(xn_calendar_info
, "granularity");
4418 g_free(cal_free_busy_base64
);
4419 cal_free_busy_base64
= sipe_xml_data(xn_calendar_info
);
4421 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
);
4426 xn_state
= sipe_xml_child(node
, "states/state");
4428 int dev_avail
= sipe_xml_int_attribute(xn_state
, "avail", 0);
4429 time_t dev_avail_since
= sipe_utils_str_to_time(sipe_xml_attribute(xn_state
, "since"));
4431 state
= sipe_xml_data(xn_state
);
4432 if (dev_avail_since
> user_avail_since
&&
4433 dev_avail
>= res_avail
)
4435 res_avail
= dev_avail
;
4436 if (!is_empty(state
))
4438 if (sipe_strequal(state
, sipe_activity_map
[SIPE_ACTIVITY_ON_PHONE
].token
)) {
4440 activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_ON_PHONE
));
4441 } else if (sipe_strequal(state
, "presenting")) {
4443 activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_IN_CONF
));
4448 activity_since
= dev_avail_since
;
4450 status_id
= sipe_get_status_by_availability(res_avail
, &activity
);
4457 if (xn_oof
&& res_avail
>= 15000) { /* 12000 in 2007 */
4459 activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_OOF
));
4463 sbuddy
= g_hash_table_lookup(sipe_private
->buddies
, uri
);
4466 g_free(sbuddy
->activity
);
4467 sbuddy
->activity
= activity
;
4470 sbuddy
->activity_since
= activity_since
;
4472 sbuddy
->user_avail
= user_avail
;
4473 sbuddy
->user_avail_since
= user_avail_since
;
4475 g_free(sbuddy
->note
);
4476 sbuddy
->note
= NULL
;
4477 if (!is_empty(note
)) { sbuddy
->note
= g_markup_escape_text(note
, -1); }
4479 sbuddy
->is_oof_note
= (xn_oof
!= NULL
);
4481 g_free(sbuddy
->device_name
);
4482 sbuddy
->device_name
= NULL
;
4483 if (!is_empty(device_name
)) { sbuddy
->device_name
= g_strdup(device_name
); }
4485 if (!is_empty(cal_free_busy_base64
)) {
4486 g_free(sbuddy
->cal_start_time
);
4487 sbuddy
->cal_start_time
= g_strdup(cal_start_time
);
4489 sbuddy
->cal_granularity
= sipe_strcase_equal(cal_granularity
, "PT15M") ? 15 : 0;
4491 g_free(sbuddy
->cal_free_busy_base64
);
4492 sbuddy
->cal_free_busy_base64
= cal_free_busy_base64
;
4493 cal_free_busy_base64
= NULL
;
4495 g_free(sbuddy
->cal_free_busy
);
4496 sbuddy
->cal_free_busy
= NULL
;
4499 sbuddy
->last_non_cal_status_id
= status_id
;
4500 g_free(sbuddy
->last_non_cal_activity
);
4501 sbuddy
->last_non_cal_activity
= g_strdup(sbuddy
->activity
);
4503 if (sipe_strcase_equal(sbuddy
->name
, self_uri
)) {
4504 if (!sipe_strequal(sbuddy
->note
, sip
->note
)) /* not same */
4506 sip
->is_oof_note
= sbuddy
->is_oof_note
;
4509 sip
->note
= g_strdup(sbuddy
->note
);
4511 sip
->note_since
= time(NULL
);
4514 g_free(sip
->status
);
4515 sip
->status
= g_strdup(sbuddy
->last_non_cal_status_id
);
4518 g_free(cal_free_busy_base64
);
4521 SIPE_DEBUG_INFO("process_incoming_notify_msrtc: status(%s)", status_id
);
4522 sipe_got_user_status(sipe_private
, uri
, status_id
);
4524 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
) && sipe_strcase_equal(self_uri
, uri
)) {
4525 sipe_user_info_has_updated(sipe_private
, xn_userinfo
);
4529 sipe_xml_free(xn_presentity
);
4534 static void sipe_presence_mime_cb(gpointer user_data
, /* sipe_core_private */
4535 const GSList
*fields
,
4539 const gchar
*type
= sipe_utils_nameval_find(fields
, "Content-Type");
4541 if (strstr(type
,"application/rlmi+xml")) {
4542 process_incoming_notify_rlmi_resub(user_data
, body
, length
);
4543 } else if (strstr(type
, "text/xml+msrtc.pidf")) {
4544 process_incoming_notify_msrtc(user_data
, body
, length
);
4546 process_incoming_notify_rlmi(user_data
, body
, length
);
4550 static void sipe_process_presence(struct sipe_core_private
*sipe_private
,
4553 const char *ctype
= sipmsg_find_header(msg
, "Content-Type");
4555 SIPE_DEBUG_INFO("sipe_process_presence: Content-Type: %s", ctype
? ctype
: "");
4558 (strstr(ctype
, "application/rlmi+xml") ||
4559 strstr(ctype
, "application/msrtc-event-categories+xml")))
4561 if (strstr(ctype
, "multipart"))
4563 sipe_mime_parts_foreach(ctype
, msg
->body
, sipe_presence_mime_cb
, sipe_private
);
4565 else if(strstr(ctype
, "application/msrtc-event-categories+xml") )
4567 process_incoming_notify_rlmi(sipe_private
, msg
->body
, msg
->bodylen
);
4569 else if(strstr(ctype
, "application/rlmi+xml"))
4571 process_incoming_notify_rlmi_resub(sipe_private
, msg
->body
, msg
->bodylen
);
4574 else if(ctype
&& strstr(ctype
, "text/xml+msrtc.pidf"))
4576 process_incoming_notify_msrtc(sipe_private
, msg
->body
, msg
->bodylen
);
4580 process_incoming_notify_pidf(sipe_private
, msg
->body
, msg
->bodylen
);
4584 static void sipe_presence_timeout_mime_cb(gpointer user_data
,
4585 SIPE_UNUSED_PARAMETER
const GSList
*fields
,
4589 GSList
**buddies
= user_data
;
4590 sipe_xml
*xml
= sipe_xml_parse(body
, length
);
4592 if (xml
&& !sipe_strequal(sipe_xml_name(xml
), "list")) {
4593 const gchar
*uri
= sipe_xml_attribute(xml
, "uri");
4594 const sipe_xml
*xn_category
;
4597 * automaton: presence is never expected to change
4599 * see: http://msdn.microsoft.com/en-us/library/ee354295(office.13).aspx
4601 for (xn_category
= sipe_xml_child(xml
, "category");
4603 xn_category
= sipe_xml_twin(xn_category
)) {
4604 if (sipe_strequal(sipe_xml_attribute(xn_category
, "name"),
4606 const sipe_xml
*node
= sipe_xml_child(xn_category
, "contactCard/automaton");
4608 char *boolean
= sipe_xml_data(node
);
4609 if (sipe_strequal(boolean
, "true")) {
4610 SIPE_DEBUG_INFO("sipe_process_presence_timeout: %s is an automaton: - not subscribing to presence updates",
4621 *buddies
= g_slist_append(*buddies
, sip_uri(uri
));
4628 static void sipe_process_presence_timeout(struct sipe_core_private
*sipe_private
,
4629 struct sipmsg
*msg
, gchar
*who
,
4632 const char *ctype
= sipmsg_find_header(msg
, "Content-Type");
4633 gchar
*action_name
= sipe_utils_presence_key(who
);
4635 SIPE_DEBUG_INFO("sipe_process_presence_timeout: Content-Type: %s", ctype
? ctype
: "");
4638 strstr(ctype
, "multipart") &&
4639 (strstr(ctype
, "application/rlmi+xml") ||
4640 strstr(ctype
, "application/msrtc-event-categories+xml"))) {
4641 GSList
*buddies
= NULL
;
4643 sipe_mime_parts_foreach(ctype
, msg
->body
, sipe_presence_timeout_mime_cb
, &buddies
);
4646 struct presence_batched_routed
*payload
= g_malloc(sizeof(struct presence_batched_routed
));
4647 payload
->host
= g_strdup(who
);
4648 payload
->buddies
= buddies
;
4649 sipe_schedule_seconds(sipe_private
,
4653 sipe_subscribe_presence_batched_routed
,
4654 sipe_subscribe_presence_batched_routed_free
);
4655 SIPE_DEBUG_INFO("Resubscription multiple contacts with batched support & route(%s) in %d", who
, timeout
);
4659 sipe_schedule_seconds(sipe_private
,
4663 sipe_subscribe_presence_single
,
4665 SIPE_DEBUG_INFO("Resubscription single contact with batched support(%s) in %d", who
, timeout
);
4667 g_free(action_name
);
4671 * Dispatcher for all incoming subscription information
4672 * whether it comes from NOTIFY, BENOTIFY requests or
4673 * piggy-backed to subscription's OK responce.
4675 * @param request whether initiated from BE/NOTIFY request or OK-response message.
4676 * @param benotify whether initiated from NOTIFY or BENOTIFY request.
4678 void process_incoming_notify(struct sipe_core_private
*sipe_private
,
4680 gboolean request
, gboolean benotify
)
4682 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
4683 const gchar
*content_type
= sipmsg_find_header(msg
, "Content-Type");
4684 const gchar
*event
= sipmsg_find_header(msg
, "Event");
4685 const gchar
*subscription_state
= sipmsg_find_header(msg
, "subscription-state");
4687 SIPE_DEBUG_INFO("process_incoming_notify: subscription_state: %s", subscription_state
? subscription_state
: "");
4689 /* implicit subscriptions */
4690 if (content_type
&& g_str_has_prefix(content_type
, "application/ms-imdn+xml")) {
4691 sipe_process_imdn(sipe_private
, msg
);
4695 /* for one off subscriptions (send with Expire: 0) */
4696 if (sipe_strcase_equal(event
, "vnd-microsoft-provisioning-v2"))
4698 sipe_process_provisioning_v2(sipe_private
, msg
);
4700 else if (sipe_strcase_equal(event
, "vnd-microsoft-provisioning"))
4702 sipe_process_provisioning(sipe_private
, msg
);
4704 else if (sipe_strcase_equal(event
, "presence"))
4706 sipe_process_presence(sipe_private
, msg
);
4708 else if (sipe_strcase_equal(event
, "registration-notify"))
4710 sipe_process_registration_notify(sipe_private
, msg
);
4713 if (!subscription_state
|| strstr(subscription_state
, "active"))
4715 if (sipe_strcase_equal(event
, "vnd-microsoft-roaming-contacts"))
4717 sipe_process_roaming_contacts(sipe_private
, msg
);
4719 else if (sipe_strcase_equal(event
, "vnd-microsoft-roaming-self"))
4721 sipe_process_roaming_self(sipe_private
, msg
);
4723 else if (sipe_strcase_equal(event
, "vnd-microsoft-roaming-ACL"))
4725 sipe_process_roaming_acl(sipe_private
, msg
);
4727 else if (sipe_strcase_equal(event
, "presence.wpending"))
4729 sipe_process_presence_wpending(sipe_private
, msg
);
4731 else if (sipe_strcase_equal(event
, "conference"))
4733 sipe_process_conference(sipe_private
, msg
);
4738 /* The server sends status 'terminated' */
4739 if (subscription_state
&& strstr(subscription_state
, "terminated") ) {
4740 gchar
*who
= parse_from(sipmsg_find_header(msg
, request
? "From" : "To"));
4741 gchar
*key
= sipe_utils_subscription_key(event
, who
);
4743 SIPE_DEBUG_INFO("process_incoming_notify: server says that subscription to %s was terminated.", who
);
4746 sipe_subscriptions_remove(sipe_private
, key
);
4750 if (!request
&& event
) {
4751 const gchar
*expires_header
= sipmsg_find_header(msg
, "Expires");
4752 int timeout
= expires_header
? strtol(expires_header
, NULL
, 10) : 0;
4753 SIPE_DEBUG_INFO("process_incoming_notify: subscription expires:%d", timeout
);
4756 /* 2 min ahead of expiration */
4757 timeout
= (timeout
- 120) > 120 ? (timeout
- 120) : timeout
;
4759 if (sipe_strcase_equal(event
, "presence.wpending") &&
4760 g_slist_find_custom(sip
->allow_events
, "presence.wpending", (GCompareFunc
)g_ascii_strcasecmp
))
4762 gchar
*action_name
= g_strdup_printf("<%s>", "presence.wpending");
4763 sipe_schedule_seconds(sipe_private
,
4767 sipe_subscribe_presence_wpending
,
4769 g_free(action_name
);
4771 else if (sipe_strcase_equal(event
, "presence") &&
4772 g_slist_find_custom(sip
->allow_events
, "presence", (GCompareFunc
)g_ascii_strcasecmp
))
4774 gchar
*who
= parse_from(sipmsg_find_header(msg
, "To"));
4775 gchar
*action_name
= sipe_utils_presence_key(who
);
4777 if (sip
->batched_support
) {
4778 sipe_process_presence_timeout(sipe_private
, msg
, who
, timeout
);
4781 sipe_schedule_seconds(sipe_private
,
4785 sipe_subscribe_presence_single
,
4787 SIPE_DEBUG_INFO("Resubscription single contact (%s) in %d", who
, timeout
);
4789 g_free(action_name
);
4795 /* The client responses on received a NOTIFY message */
4796 if (request
&& !benotify
)
4798 sip_transport_response(sipe_private
, msg
, 200, "OK", NULL
);
4803 * Whether user manually changed status or
4804 * it was changed automatically due to user
4805 * became inactive/active again
4808 sipe_is_user_state(struct sipe_core_private
*sipe_private
)
4810 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
4812 time_t now
= time(NULL
);
4814 SIPE_DEBUG_INFO("sipe_is_user_state: sip->idle_switch : %s", asctime(localtime(&(sip
->idle_switch
))));
4815 SIPE_DEBUG_INFO("sipe_is_user_state: now : %s", asctime(localtime(&now
)));
4817 res
= ((now
- SIPE_IDLE_SET_DELAY
* 2) >= sip
->idle_switch
);
4819 SIPE_DEBUG_INFO("sipe_is_user_state: res = %s", res
? "USER" : "MACHINE");
4824 send_presence_soap0(struct sipe_core_private
*sipe_private
,
4825 gboolean do_publish_calendar
,
4826 gboolean do_reset_status
)
4828 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
4829 struct sipe_calendar
* cal
= sip
->cal
;
4830 int availability
= 0;
4835 gchar
*res_note
= NULL
;
4836 gchar
*res_oof
= NULL
;
4837 const gchar
*note_pub
= NULL
;
4838 gchar
*states
= NULL
;
4839 gchar
*calendar_data
= NULL
;
4840 gchar
*epid
= get_epid(sipe_private
);
4841 time_t now
= time(NULL
);
4842 gchar
*since_time_str
= sipe_utils_time_to_str(now
);
4843 const gchar
*oof_note
= cal
? sipe_ews_get_oof_note(cal
) : NULL
;
4844 const char *user_input
;
4845 gboolean pub_oof
= cal
&& oof_note
&& (!sip
->note
|| cal
->updated
> sip
->note_since
);
4847 if (oof_note
&& sip
->note
) {
4848 SIPE_DEBUG_INFO("cal->oof_start : %s", asctime(localtime(&(cal
->oof_start
))));
4849 SIPE_DEBUG_INFO("sip->note_since : %s", asctime(localtime(&(sip
->note_since
))));
4852 SIPE_DEBUG_INFO("sip->note : %s", sip
->note
? sip
->note
: "");
4854 if (!sip
->initial_state_published
||
4857 g_free(sip
->status
);
4858 sip
->status
= g_strdup(SIPE_STATUS_ID_AVAILABLE
);
4861 sipe_get_act_avail_by_status_2005(sip
->status
, &activity
, &availability
);
4865 note_pub
= oof_note
;
4866 res_oof
= SIPE_SOAP_SET_PRESENCE_OOF_XML
;
4867 cal
->published
= TRUE
;
4868 } else if (sip
->note
) {
4869 if (sip
->is_oof_note
&& !oof_note
) { /* stale OOF note, as it's not present in cal already */
4872 sip
->is_oof_note
= FALSE
;
4873 sip
->note_since
= 0;
4875 note_pub
= sip
->note
;
4876 res_oof
= sip
->is_oof_note
? SIPE_SOAP_SET_PRESENCE_OOF_XML
: "";
4882 /* to protocol internal plain text format */
4883 tmp
= sipe_backend_markup_strip_html(note_pub
);
4884 res_note
= g_markup_printf_escaped(SIPE_SOAP_SET_PRESENCE_NOTE_XML
, tmp
);
4889 if (!do_reset_status
) {
4890 if (sipe_is_user_state(sipe_private
) && !do_publish_calendar
&& sip
->initial_state_published
)
4892 gchar
*activity_token
= NULL
;
4893 int avail_2007
= sipe_get_availability_by_status(sip
->status
, &activity_token
);
4895 states
= g_strdup_printf(SIPE_SOAP_SET_PRESENCE_STATES
,
4900 g_free(activity_token
);
4902 else /* preserve existing publication */
4904 if (sip
->user_states
) {
4905 states
= g_strdup(sip
->user_states
);
4909 /* do nothing - then User state will be erased */
4911 sip
->initial_state_published
= TRUE
;
4914 if (cal
&& (!is_empty(cal
->legacy_dn
) || !is_empty(cal
->email
)) && cal
->fb_start
&& !is_empty(cal
->free_busy
))
4916 char *fb_start_str
= sipe_utils_time_to_str(cal
->fb_start
);
4917 char *free_busy_base64
= sipe_cal_get_freebusy_base64(cal
->free_busy
);
4918 calendar_data
= g_strdup_printf(SIPE_SOAP_SET_PRESENCE_CALENDAR
,
4919 !is_empty(cal
->legacy_dn
) ? cal
->legacy_dn
: cal
->email
,
4922 g_free(fb_start_str
);
4923 g_free(free_busy_base64
);
4926 user_input
= !sipe_is_user_state(sipe_private
) && sip
->status
!= SIPE_STATUS_ID_AVAILABLE
? "idle" : "active";
4928 /* forming resulting XML */
4929 body
= g_strdup_printf(SIPE_SOAP_SET_PRESENCE
,
4930 sipe_private
->username
,
4933 (tmp
= g_ascii_strup(g_get_host_name(), -1)),
4934 res_note
? res_note
: "",
4935 res_oof
? res_oof
: "",
4936 states
? states
: "",
4937 calendar_data
? calendar_data
: "",
4946 g_free(calendar_data
);
4948 send_soap_request(sipe_private
, body
);
4951 g_free(since_time_str
);
4956 send_presence_soap(struct sipe_core_private
*sipe_private
,
4957 gboolean do_publish_calendar
)
4959 return send_presence_soap0(sipe_private
, do_publish_calendar
, FALSE
);
4964 process_send_presence_category_publish_response(struct sipe_core_private
*sipe_private
,
4966 struct transaction
*trans
)
4968 const gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
4970 if (msg
->response
== 409 && g_str_has_prefix(contenttype
, "application/msrtc-fault+xml")) {
4972 const sipe_xml
*node
;
4976 gboolean has_device_publication
= FALSE
;
4978 xml
= sipe_xml_parse(msg
->body
, msg
->bodylen
);
4980 /* test if version mismatch fault */
4981 fault_code
= sipe_xml_data(sipe_xml_child(xml
, "Faultcode"));
4982 if (!sipe_strequal(fault_code
, "Client.BadCall.WrongDelta")) {
4983 SIPE_DEBUG_INFO("process_send_presence_category_publish_response: unsupported fault code:%s returning.", fault_code
);
4990 /* accumulating information about faulty versions */
4991 faults
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, g_free
);
4992 for (node
= sipe_xml_child(xml
, "details/operation");
4994 node
= sipe_xml_twin(node
))
4996 const gchar
*index
= sipe_xml_attribute(node
, "index");
4997 const gchar
*curVersion
= sipe_xml_attribute(node
, "curVersion");
4999 g_hash_table_insert(faults
, g_strdup(index
), g_strdup(curVersion
));
5000 SIPE_DEBUG_INFO("fault added: index:%s curVersion:%s", index
, curVersion
);
5004 /* here we are parsing own request to figure out what publication
5005 * referensed here only by index went wrong
5007 xml
= sipe_xml_parse(trans
->msg
->body
, trans
->msg
->bodylen
);
5010 for (node
= sipe_xml_child(xml
, "publications/publication"),
5011 index_our
= 1; /* starts with 1 - our first publication */
5013 node
= sipe_xml_twin(node
), index_our
++)
5015 gchar
*idx
= g_strdup_printf("%d", index_our
);
5016 const gchar
*curVersion
= g_hash_table_lookup(faults
, idx
);
5017 const gchar
*categoryName
= sipe_xml_attribute(node
, "categoryName");
5020 if (sipe_strequal("device", categoryName
)) {
5021 has_device_publication
= TRUE
;
5024 if (curVersion
) { /* fault exist on this index */
5025 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
5026 const gchar
*container
= sipe_xml_attribute(node
, "container");
5027 const gchar
*instance
= sipe_xml_attribute(node
, "instance");
5028 /* key is <category><instance><container> */
5029 gchar
*key
= g_strdup_printf("<%s><%s><%s>", categoryName
, instance
, container
);
5030 GHashTable
*category
= g_hash_table_lookup(sip
->our_publications
, categoryName
);
5033 struct sipe_publication
*publication
=
5034 g_hash_table_lookup(category
, key
);
5036 SIPE_DEBUG_INFO("key is %s", key
);
5039 SIPE_DEBUG_INFO("Updating %s with version %s. Was %d before.",
5040 key
, curVersion
, publication
->version
);
5041 /* updating publication's version to the correct one */
5042 publication
->version
= atoi(curVersion
);
5045 /* We somehow lost this category from our publications... */
5046 struct sipe_publication
*publication
= g_new0(struct sipe_publication
, 1);
5047 publication
->category
= g_strdup(categoryName
);
5048 publication
->instance
= atoi(instance
);
5049 publication
->container
= atoi(container
);
5050 publication
->version
= atoi(curVersion
);
5051 category
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
5052 g_free
, (GDestroyNotify
)free_publication
);
5053 g_hash_table_insert(category
, g_strdup(key
), publication
);
5054 g_hash_table_insert(sip
->our_publications
, g_strdup(categoryName
), category
);
5055 SIPE_DEBUG_INFO("added lost category '%s' key '%s'", categoryName
, key
);
5061 g_hash_table_destroy(faults
);
5063 /* rebublishing with right versions */
5064 if (has_device_publication
) {
5065 send_publish_category_initial(sipe_private
);
5067 send_presence_status(sipe_private
, NULL
);
5074 * Returns 'device' XML part for publication.
5075 * Must be g_free'd after use.
5078 sipe_publish_get_category_device(struct sipe_core_private
*sipe_private
)
5080 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
5083 gchar
*epid
= get_epid(sipe_private
);
5084 gchar
*uuid
= generateUUIDfromEPID(epid
);
5085 guint device_instance
= sipe_get_pub_instance(sipe_private
, SIPE_PUB_DEVICE
);
5086 /* key is <category><instance><container> */
5087 gchar
*key
= g_strdup_printf("<%s><%u><%u>", "device", device_instance
, 2);
5088 struct sipe_publication
*publication
=
5089 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "device"), key
);
5094 uri
= sip_uri_self(sipe_private
);
5095 doc
= g_strdup_printf(SIPE_PUB_XML_DEVICE
,
5097 publication
? publication
->version
: 0,
5100 "00:00:00+01:00", /* @TODO make timezone real*/
5111 * A service method - use
5112 * - send_publish_get_category_state_machine and
5113 * - send_publish_get_category_state_user instead.
5114 * Must be g_free'd after use.
5117 sipe_publish_get_category_state(struct sipe_core_private
*sipe_private
,
5118 gboolean is_user_state
)
5120 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
5121 int availability
= sipe_get_availability_by_status(sip
->status
, NULL
);
5122 guint instance
= is_user_state
? sipe_get_pub_instance(sipe_private
, SIPE_PUB_STATE_USER
) :
5123 sipe_get_pub_instance(sipe_private
, SIPE_PUB_STATE_MACHINE
);
5124 /* key is <category><instance><container> */
5125 gchar
*key_2
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 2);
5126 gchar
*key_3
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 3);
5127 struct sipe_publication
*publication_2
=
5128 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "state"), key_2
);
5129 struct sipe_publication
*publication_3
=
5130 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "state"), key_3
);
5135 if (publication_2
&& (publication_2
->availability
== availability
))
5137 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_state: state has NOT changed. Exiting.");
5138 return NULL
; /* nothing to update */
5141 return g_strdup_printf( is_user_state
? SIPE_PUB_XML_STATE_USER
: SIPE_PUB_XML_STATE_MACHINE
,
5143 publication_2
? publication_2
->version
: 0,
5146 publication_3
? publication_3
->version
: 0,
5151 * Only Busy and OOF calendar event are published.
5152 * Different instances are used for that.
5154 * Must be g_free'd after use.
5157 sipe_publish_get_category_state_calendar(struct sipe_core_private
*sipe_private
,
5158 struct sipe_cal_event
*event
,
5162 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
5163 gchar
*start_time_str
;
5164 int availability
= 0;
5167 guint instance
= (cal_satus
== SIPE_CAL_OOF
) ?
5168 sipe_get_pub_instance(sipe_private
, SIPE_PUB_STATE_CALENDAR_OOF
) :
5169 sipe_get_pub_instance(sipe_private
, SIPE_PUB_STATE_CALENDAR
);
5171 /* key is <category><instance><container> */
5172 gchar
*key_2
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 2);
5173 gchar
*key_3
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 3);
5174 struct sipe_publication
*publication_2
=
5175 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "state"), key_2
);
5176 struct sipe_publication
*publication_3
=
5177 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "state"), key_3
);
5182 if (!publication_3
&& !event
) { /* was nothing, have nothing, exiting */
5183 SIPE_DEBUG_INFO("sipe_publish_get_category_state_calendar: "
5184 "Exiting as no publication and no event for cal_satus:%d", cal_satus
);
5190 (publication_3
->availability
== availability
) &&
5191 sipe_strequal(publication_3
->cal_event_hash
, (tmp
= sipe_cal_event_hash(event
))))
5194 SIPE_DEBUG_INFO("sipe_publish_get_category_state_calendar: "
5195 "cal state has NOT changed for cal_satus:%d. Exiting.", cal_satus
);
5196 return NULL
; /* nothing to update */
5201 (event
->cal_status
== SIPE_CAL_BUSY
||
5202 event
->cal_status
== SIPE_CAL_OOF
))
5204 gchar
*availability_xml_str
= NULL
;
5205 gchar
*activity_xml_str
= NULL
;
5207 if (event
->cal_status
== SIPE_CAL_BUSY
) {
5208 availability_xml_str
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_AVAIL
, 6500);
5211 if (event
->cal_status
== SIPE_CAL_BUSY
&& event
->is_meeting
) {
5212 activity_xml_str
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY
,
5213 sipe_activity_map
[SIPE_ACTIVITY_IN_MEETING
].token
,
5214 "minAvailability=\"6500\"",
5215 "maxAvailability=\"8999\"");
5216 } else if (event
->cal_status
== SIPE_CAL_OOF
) {
5217 activity_xml_str
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY
,
5218 sipe_activity_map
[SIPE_ACTIVITY_OOF
].token
,
5219 "minAvailability=\"12000\"",
5222 start_time_str
= sipe_utils_time_to_str(event
->start_time
);
5224 res
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR
,
5226 publication_2
? publication_2
->version
: 0,
5229 availability_xml_str
? availability_xml_str
: "",
5230 activity_xml_str
? activity_xml_str
: "",
5231 event
->subject
? event
->subject
: "",
5232 event
->location
? event
->location
: "",
5235 publication_3
? publication_3
->version
: 0,
5238 availability_xml_str
? availability_xml_str
: "",
5239 activity_xml_str
? activity_xml_str
: "",
5240 event
->subject
? event
->subject
: "",
5241 event
->location
? event
->location
: ""
5243 g_free(start_time_str
);
5244 g_free(availability_xml_str
);
5245 g_free(activity_xml_str
);
5248 else /* including !event, SIPE_CAL_FREE, SIPE_CAL_TENTATIVE */
5250 res
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_CLEAR
,
5252 publication_2
? publication_2
->version
: 0,
5255 publication_3
? publication_3
->version
: 0
5263 * Returns 'machineState' XML part for publication.
5264 * Must be g_free'd after use.
5267 sipe_publish_get_category_state_machine(struct sipe_core_private
*sipe_private
)
5269 return sipe_publish_get_category_state(sipe_private
, FALSE
);
5273 * Returns 'userState' XML part for publication.
5274 * Must be g_free'd after use.
5277 sipe_publish_get_category_state_user(struct sipe_core_private
*sipe_private
)
5279 return sipe_publish_get_category_state(sipe_private
, TRUE
);
5283 * Returns 'note' XML part for publication.
5284 * Must be g_free'd after use.
5286 * Protocol format for Note is plain text.
5288 * @param note a note in Sipe internal HTML format
5289 * @param note_type either personal or OOF
5292 sipe_publish_get_category_note(struct sipe_core_private
*sipe_private
,
5293 const char *note
, /* html */
5294 const char *note_type
,
5298 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
5299 guint instance
= sipe_strequal("OOF", note_type
) ? sipe_get_pub_instance(sipe_private
, SIPE_PUB_NOTE_OOF
) : 0;
5300 /* key is <category><instance><container> */
5301 gchar
*key_note_200
= g_strdup_printf("<%s><%u><%u>", "note", instance
, 200);
5302 gchar
*key_note_300
= g_strdup_printf("<%s><%u><%u>", "note", instance
, 300);
5303 gchar
*key_note_400
= g_strdup_printf("<%s><%u><%u>", "note", instance
, 400);
5305 struct sipe_publication
*publication_note_200
=
5306 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "note"), key_note_200
);
5307 struct sipe_publication
*publication_note_300
=
5308 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "note"), key_note_300
);
5309 struct sipe_publication
*publication_note_400
=
5310 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "note"), key_note_400
);
5312 char *tmp
= note
? sipe_backend_markup_strip_html(note
) : NULL
;
5313 char *n1
= tmp
? g_markup_escape_text(tmp
, -1) : NULL
;
5314 const char *n2
= publication_note_200
? publication_note_200
->note
: NULL
;
5315 char *res
, *tmp1
, *tmp2
, *tmp3
;
5316 char *start_time_attr
;
5317 char *end_time_attr
;
5321 g_free(key_note_200
);
5322 g_free(key_note_300
);
5323 g_free(key_note_400
);
5325 /* we even need to republish empty note */
5326 if (sipe_strequal(n1
, n2
))
5328 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_note: note has NOT changed. Exiting.");
5330 return NULL
; /* nothing to update */
5333 start_time_attr
= note_start
? g_strdup_printf(" startTime=\"%s\"", (tmp
= sipe_utils_time_to_str(note_start
))) : NULL
;
5336 end_time_attr
= note_end
? g_strdup_printf(" endTime=\"%s\"", (tmp
= sipe_utils_time_to_str(note_end
))) : NULL
;
5340 tmp1
= g_strdup_printf(SIPE_PUB_XML_NOTE
,
5343 publication_note_200
? publication_note_200
->version
: 0,
5345 start_time_attr
? start_time_attr
: "",
5346 end_time_attr
? end_time_attr
: "",
5349 tmp2
= g_strdup_printf(SIPE_PUB_XML_NOTE
,
5352 publication_note_300
? publication_note_300
->version
: 0,
5354 start_time_attr
? start_time_attr
: "",
5355 end_time_attr
? end_time_attr
: "",
5358 tmp3
= g_strdup_printf(SIPE_PUB_XML_NOTE
,
5361 publication_note_400
? publication_note_400
->version
: 0,
5363 start_time_attr
? start_time_attr
: "",
5364 end_time_attr
? end_time_attr
: "",
5367 tmp1
= g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR
,
5371 publication_note_200
? publication_note_200
->version
: 0,
5373 tmp2
= g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR
,
5377 publication_note_200
? publication_note_200
->version
: 0,
5379 tmp3
= g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR
,
5383 publication_note_200
? publication_note_200
->version
: 0,
5386 res
= g_strconcat(tmp1
, tmp2
, tmp3
, NULL
);
5388 g_free(start_time_attr
);
5389 g_free(end_time_attr
);
5399 * Returns 'calendarData' XML part with WorkingHours for publication.
5400 * Must be g_free'd after use.
5403 sipe_publish_get_category_cal_working_hours(struct sipe_core_private
*sipe_private
)
5405 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
5406 struct sipe_calendar
* cal
= sip
->cal
;
5408 /* key is <category><instance><container> */
5409 gchar
*key_cal_1
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 1);
5410 gchar
*key_cal_100
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 100);
5411 gchar
*key_cal_200
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 200);
5412 gchar
*key_cal_300
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 300);
5413 gchar
*key_cal_400
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 400);
5414 gchar
*key_cal_32000
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 32000);
5416 struct sipe_publication
*publication_cal_1
=
5417 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_1
);
5418 struct sipe_publication
*publication_cal_100
=
5419 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_100
);
5420 struct sipe_publication
*publication_cal_200
=
5421 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_200
);
5422 struct sipe_publication
*publication_cal_300
=
5423 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_300
);
5424 struct sipe_publication
*publication_cal_400
=
5425 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_400
);
5426 struct sipe_publication
*publication_cal_32000
=
5427 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_32000
);
5429 const char *n1
= cal
? cal
->working_hours_xml_str
: NULL
;
5430 const char *n2
= publication_cal_300
? publication_cal_300
->working_hours_xml_str
: NULL
;
5433 g_free(key_cal_100
);
5434 g_free(key_cal_200
);
5435 g_free(key_cal_300
);
5436 g_free(key_cal_400
);
5437 g_free(key_cal_32000
);
5439 if (!cal
|| is_empty(cal
->email
) || is_empty(cal
->working_hours_xml_str
)) {
5440 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_working_hours: no data to publish, exiting");
5444 if (sipe_strequal(n1
, n2
))
5446 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_working_hours: WorkingHours has NOT changed. Exiting.");
5447 return NULL
; /* nothing to update */
5450 return g_strdup_printf(SIPE_PUB_XML_WORKING_HOURS
,
5452 publication_cal_1
? publication_cal_1
->version
: 0,
5454 cal
->working_hours_xml_str
,
5456 publication_cal_100
? publication_cal_100
->version
: 0,
5458 publication_cal_200
? publication_cal_200
->version
: 0,
5460 cal
->working_hours_xml_str
,
5462 publication_cal_300
? publication_cal_300
->version
: 0,
5464 cal
->working_hours_xml_str
,
5465 /* 400 - Personal */
5466 publication_cal_400
? publication_cal_400
->version
: 0,
5468 cal
->working_hours_xml_str
,
5469 /* 32000 - Blocked */
5470 publication_cal_32000
? publication_cal_32000
->version
: 0
5475 * Returns 'calendarData' XML part with FreeBusy for publication.
5476 * Must be g_free'd after use.
5479 sipe_publish_get_category_cal_free_busy(struct sipe_core_private
*sipe_private
)
5481 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
5482 struct sipe_calendar
* cal
= sip
->cal
;
5483 guint cal_data_instance
= sipe_get_pub_instance(sipe_private
, SIPE_PUB_CALENDAR_DATA
);
5485 char *free_busy_base64
;
5490 /* key is <category><instance><container> */
5491 gchar
*key_cal_1
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 1);
5492 gchar
*key_cal_100
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 100);
5493 gchar
*key_cal_200
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 200);
5494 gchar
*key_cal_300
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 300);
5495 gchar
*key_cal_400
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 400);
5496 gchar
*key_cal_32000
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 32000);
5498 struct sipe_publication
*publication_cal_1
=
5499 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_1
);
5500 struct sipe_publication
*publication_cal_100
=
5501 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_100
);
5502 struct sipe_publication
*publication_cal_200
=
5503 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_200
);
5504 struct sipe_publication
*publication_cal_300
=
5505 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_300
);
5506 struct sipe_publication
*publication_cal_400
=
5507 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_400
);
5508 struct sipe_publication
*publication_cal_32000
=
5509 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_32000
);
5512 g_free(key_cal_100
);
5513 g_free(key_cal_200
);
5514 g_free(key_cal_300
);
5515 g_free(key_cal_400
);
5516 g_free(key_cal_32000
);
5518 if (!cal
|| is_empty(cal
->email
) || !cal
->fb_start
|| is_empty(cal
->free_busy
)) {
5519 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_free_busy: no data to publish, exiting");
5523 fb_start_str
= sipe_utils_time_to_str(cal
->fb_start
);
5524 free_busy_base64
= sipe_cal_get_freebusy_base64(cal
->free_busy
);
5526 st
= publication_cal_300
? publication_cal_300
->fb_start_str
: NULL
;
5527 fb
= publication_cal_300
? publication_cal_300
->free_busy_base64
: NULL
;
5529 /* we will rebuplish the same data to refresh publication time,
5530 * so if data from multiple sources, most recent will be choosen
5532 //if (sipe_strequal(st, fb_start_str) && sipe_strequal(fb, free_busy_base64))
5534 // SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_free_busy: FreeBusy has NOT changed. Exiting.");
5535 // g_free(fb_start_str);
5536 // g_free(free_busy_base64);
5537 // return NULL; /* nothing to update */
5540 res
= g_strdup_printf(SIPE_PUB_XML_FREE_BUSY
,
5543 publication_cal_1
? publication_cal_1
->version
: 0,
5546 publication_cal_100
? publication_cal_100
->version
: 0,
5549 publication_cal_200
? publication_cal_200
->version
: 0,
5555 publication_cal_300
? publication_cal_300
->version
: 0,
5559 /* 400 - Personal */
5561 publication_cal_400
? publication_cal_400
->version
: 0,
5565 /* 32000 - Blocked */
5567 publication_cal_32000
? publication_cal_32000
->version
: 0
5570 g_free(fb_start_str
);
5571 g_free(free_busy_base64
);
5575 static void send_presence_publish(struct sipe_core_private
*sipe_private
,
5576 const char *publications
)
5583 uri
= sip_uri_self(sipe_private
);
5584 doc
= g_strdup_printf(SIPE_SEND_PRESENCE
,
5588 tmp
= get_contact(sipe_private
);
5589 hdr
= g_strdup_printf("Contact: %s\r\n"
5590 "Content-Type: application/msrtc-category-publish+xml\r\n", tmp
);
5592 sip_transport_service(sipe_private
,
5596 process_send_presence_category_publish_response
);
5605 send_publish_category_initial(struct sipe_core_private
*sipe_private
)
5607 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
5608 gchar
*pub_device
= sipe_publish_get_category_device(sipe_private
);
5610 gchar
*publications
;
5612 g_free(sip
->status
);
5613 sip
->status
= g_strdup(SIPE_STATUS_ID_AVAILABLE
); /* our initial state */
5615 pub_machine
= sipe_publish_get_category_state_machine(sipe_private
);
5616 publications
= g_strdup_printf("%s%s",
5618 pub_machine
? pub_machine
: "");
5620 g_free(pub_machine
);
5622 send_presence_publish(sipe_private
, publications
);
5623 g_free(publications
);
5627 send_presence_category_publish(struct sipe_core_private
*sipe_private
)
5629 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
5630 gchar
*pub_state
= sipe_is_user_state(sipe_private
) ?
5631 sipe_publish_get_category_state_user(sipe_private
) :
5632 sipe_publish_get_category_state_machine(sipe_private
);
5633 gchar
*pub_note
= sipe_publish_get_category_note(sipe_private
,
5635 sip
->is_oof_note
? "OOF" : "personal",
5638 gchar
*publications
;
5640 if (!pub_state
&& !pub_note
) {
5641 SIPE_DEBUG_INFO_NOFORMAT("send_presence_category_publish: nothing has changed. Exiting.");
5645 publications
= g_strdup_printf("%s%s",
5646 pub_state
? pub_state
: "",
5647 pub_note
? pub_note
: "");
5652 send_presence_publish(sipe_private
, publications
);
5653 g_free(publications
);
5657 * Publishes self status
5658 * based on own calendar information.
5663 publish_calendar_status_self(struct sipe_core_private
*sipe_private
,
5664 SIPE_UNUSED_PARAMETER
void *unused
)
5666 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
5667 struct sipe_cal_event
* event
= NULL
;
5668 gchar
*pub_cal_working_hours
= NULL
;
5669 gchar
*pub_cal_free_busy
= NULL
;
5670 gchar
*pub_calendar
= NULL
;
5671 gchar
*pub_calendar2
= NULL
;
5672 gchar
*pub_oof_note
= NULL
;
5673 const gchar
*oof_note
;
5674 time_t oof_start
= 0;
5678 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self() no calendar data.");
5682 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self() started.");
5683 if (sip
->cal
->cal_events
) {
5684 event
= sipe_cal_get_event(sip
->cal
->cal_events
, time(NULL
));
5688 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self: current event is NULL");
5690 char *desc
= sipe_cal_event_describe(event
);
5691 SIPE_DEBUG_INFO("publish_calendar_status_self: current event is:\n%s", desc
? desc
: "");
5697 OOF publish, Busy clean
5699 OOF clean, Busy publish
5701 OOF clean, Busy clean
5703 if (event
&& event
->cal_status
== SIPE_CAL_OOF
) {
5704 pub_calendar
= sipe_publish_get_category_state_calendar(sipe_private
, event
, sip
->cal
->email
, SIPE_CAL_OOF
);
5705 pub_calendar2
= sipe_publish_get_category_state_calendar(sipe_private
, NULL
, sip
->cal
->email
, SIPE_CAL_BUSY
);
5706 } else if (event
&& event
->cal_status
== SIPE_CAL_BUSY
) {
5707 pub_calendar
= sipe_publish_get_category_state_calendar(sipe_private
, NULL
, sip
->cal
->email
, SIPE_CAL_OOF
);
5708 pub_calendar2
= sipe_publish_get_category_state_calendar(sipe_private
, event
, sip
->cal
->email
, SIPE_CAL_BUSY
);
5710 pub_calendar
= sipe_publish_get_category_state_calendar(sipe_private
, NULL
, sip
->cal
->email
, SIPE_CAL_OOF
);
5711 pub_calendar2
= sipe_publish_get_category_state_calendar(sipe_private
, NULL
, sip
->cal
->email
, SIPE_CAL_BUSY
);
5714 oof_note
= sipe_ews_get_oof_note(sip
->cal
);
5715 if (sipe_strequal("Scheduled", sip
->cal
->oof_state
)) {
5716 oof_start
= sip
->cal
->oof_start
;
5717 oof_end
= sip
->cal
->oof_end
;
5719 pub_oof_note
= sipe_publish_get_category_note(sipe_private
, oof_note
, "OOF", oof_start
, oof_end
);
5721 pub_cal_working_hours
= sipe_publish_get_category_cal_working_hours(sipe_private
);
5722 pub_cal_free_busy
= sipe_publish_get_category_cal_free_busy(sipe_private
);
5724 if (!pub_cal_working_hours
&& !pub_cal_free_busy
&& !pub_calendar
&& !pub_calendar2
&& !pub_oof_note
) {
5725 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self: nothing has changed.");
5727 gchar
*publications
= g_strdup_printf("%s%s%s%s%s",
5728 pub_cal_working_hours
? pub_cal_working_hours
: "",
5729 pub_cal_free_busy
? pub_cal_free_busy
: "",
5730 pub_calendar
? pub_calendar
: "",
5731 pub_calendar2
? pub_calendar2
: "",
5732 pub_oof_note
? pub_oof_note
: "");
5734 send_presence_publish(sipe_private
, publications
);
5735 g_free(publications
);
5738 g_free(pub_cal_working_hours
);
5739 g_free(pub_cal_free_busy
);
5740 g_free(pub_calendar
);
5741 g_free(pub_calendar2
);
5742 g_free(pub_oof_note
);
5744 /* repeat scheduling */
5745 sipe_sched_calendar_status_self_publish(sipe_private
, time(NULL
));
5748 static void send_presence_status(struct sipe_core_private
*sipe_private
,
5749 SIPE_UNUSED_PARAMETER
void *unused
)
5751 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
5752 PurpleStatus
* status
= purple_account_get_active_status(sip
->account
);
5754 if (!status
) return;
5756 SIPE_DEBUG_INFO("send_presence_status: status: %s (%s)",
5757 purple_status_get_id(status
) ? purple_status_get_id(status
) : "",
5758 sipe_is_user_state(sipe_private
) ? "USER" : "MACHINE");
5760 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
5761 send_presence_category_publish(sipe_private
);
5763 send_presence_soap(sipe_private
, FALSE
);
5767 static guint
sipe_ht_hash_nick(const char *nick
)
5769 char *lc
= g_utf8_strdown(nick
, -1);
5770 guint bucket
= g_str_hash(lc
);
5776 static gboolean
sipe_ht_equals_nick(const char *nick1
, const char *nick2
)
5778 char *nick1_norm
= NULL
;
5779 char *nick2_norm
= NULL
;
5782 if (nick1
== NULL
&& nick2
== NULL
) return TRUE
;
5783 if (nick1
== NULL
|| nick2
== NULL
||
5784 !g_utf8_validate(nick1
, -1, NULL
) ||
5785 !g_utf8_validate(nick2
, -1, NULL
)) return FALSE
;
5787 nick1_norm
= g_utf8_casefold(nick1
, -1);
5788 nick2_norm
= g_utf8_casefold(nick2
, -1);
5789 equal
= g_utf8_collate(nick1_norm
, nick2_norm
) == 0;
5796 /* temporary function */
5797 void sipe_purple_setup(struct sipe_core_public
*sipe_public
,
5798 PurpleConnection
*gc
)
5800 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA
;
5802 sip
->account
= purple_connection_get_account(gc
);
5805 struct sipe_core_public
*sipe_core_allocate(const gchar
*signin_name
,
5806 const gchar
*login_domain
,
5807 const gchar
*login_account
,
5808 const gchar
*password
,
5810 const gchar
*email_url
,
5811 const gchar
**errmsg
)
5813 struct sipe_core_private
*sipe_private
;
5814 struct sipe_account_data
*sip
;
5815 gchar
**user_domain
;
5817 SIPE_DEBUG_INFO("sipe_core_allocate: signin_name '%s'", signin_name
);
5819 /* ensure that sign-in name doesn't contain invalid characters */
5820 if (strpbrk(signin_name
, "\t\v\r\n") != NULL
) {
5821 *errmsg
= _("SIP Exchange user name contains invalid characters");
5825 /* ensure that sign-in name format is name@domain */
5826 if (!strchr(signin_name
, '@') ||
5827 g_str_has_prefix(signin_name
, "@") ||
5828 g_str_has_suffix(signin_name
, "@")) {
5829 *errmsg
= _("User name should be a valid SIP URI\nExample: user@company.com");
5833 /* ensure that email format is name@domain (if provided) */
5834 if (!is_empty(email
) &&
5835 (!strchr(email
, '@') ||
5836 g_str_has_prefix(email
, "@") ||
5837 g_str_has_suffix(email
, "@")))
5839 *errmsg
= _("Email address should be valid if provided\nExample: user@company.com");
5843 /* ensure that user name doesn't contain spaces */
5844 user_domain
= g_strsplit(signin_name
, "@", 2);
5845 SIPE_DEBUG_INFO("sipe_core_allocate: user '%s' domain '%s'", user_domain
[0], user_domain
[1]);
5846 if (strchr(user_domain
[0], ' ') != NULL
) {
5847 g_strfreev(user_domain
);
5848 *errmsg
= _("SIP Exchange user name contains whitespace");
5852 /* ensure that email_url is in proper format if enabled (if provided).
5853 * Example (Exchange): https://server.company.com/EWS/Exchange.asmx
5854 * Example (Domino) : https://[domino_server]/[mail_database_name].nsf
5856 if (!is_empty(email_url
)) {
5857 char *tmp
= g_ascii_strdown(email_url
, -1);
5858 if (!g_str_has_prefix(tmp
, "https://"))
5861 g_strfreev(user_domain
);
5862 *errmsg
= _("Email services URL should be valid if provided\n"
5863 "Example: https://exchange.corp.com/EWS/Exchange.asmx\n"
5864 "Example: https://domino.corp.com/maildatabase.nsf");
5870 sipe_private
= g_new0(struct sipe_core_private
, 1);
5871 sipe_private
->temporary
= sip
= g_new0(struct sipe_account_data
, 1);
5872 sip
->subscribed_buddies
= FALSE
;
5873 sip
->initial_state_published
= FALSE
;
5874 sipe_private
->username
= g_strdup(signin_name
);
5875 sip
->email
= is_empty(email
) ? g_strdup(signin_name
) : g_strdup(email
);
5876 sip
->authdomain
= is_empty(login_domain
) ? NULL
: g_strdup(login_domain
);
5877 sip
->authuser
= is_empty(login_account
) ? NULL
: g_strdup(login_account
);
5878 sip
->password
= g_strdup(password
);
5879 sipe_private
->public.sip_name
= g_strdup(user_domain
[0]);
5880 sipe_private
->public.sip_domain
= g_strdup(user_domain
[1]);
5881 g_strfreev(user_domain
);
5883 sipe_private
->buddies
= g_hash_table_new((GHashFunc
)sipe_ht_hash_nick
, (GEqualFunc
)sipe_ht_equals_nick
);
5884 sip
->our_publications
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
5885 g_free
, (GDestroyNotify
)g_hash_table_destroy
);
5886 sipe_subscriptions_init(sipe_private
);
5887 sip
->status
= g_strdup(SIPE_STATUS_ID_UNKNOWN
);
5889 return((struct sipe_core_public
*)sipe_private
);
5892 void sipe_connection_cleanup(struct sipe_core_private
*sipe_private
)
5894 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
5896 g_free(sipe_private
->epid
);
5897 sipe_private
->epid
= NULL
;
5899 sip_transport_disconnect(sipe_private
);
5901 sipe_schedule_cancel_all(sipe_private
);
5903 if (sip
->allow_events
) {
5904 GSList
*entry
= sip
->allow_events
;
5906 g_free(entry
->data
);
5907 entry
= entry
->next
;
5910 g_slist_free(sip
->allow_events
);
5912 if (sip
->containers
) {
5913 GSList
*entry
= sip
->containers
;
5915 free_container((struct sipe_container
*)entry
->data
);
5916 entry
= entry
->next
;
5919 g_slist_free(sip
->containers
);
5921 if (sipe_private
->contact
)
5922 g_free(sipe_private
->contact
);
5923 sipe_private
->contact
= NULL
;
5925 g_free(sip
->regcallid
);
5926 sip
->regcallid
= NULL
;
5928 if (sipe_private
->focus_factory_uri
)
5929 g_free(sipe_private
->focus_factory_uri
);
5930 sipe_private
->focus_factory_uri
= NULL
;
5933 sipe_cal_calendar_free(sip
->cal
);
5939 * A callback for g_hash_table_foreach_remove
5941 static gboolean
sipe_buddy_remove(SIPE_UNUSED_PARAMETER gpointer key
, gpointer buddy
,
5942 SIPE_UNUSED_PARAMETER gpointer user_data
)
5944 sipe_free_buddy((struct sipe_buddy
*) buddy
);
5946 /* We must return TRUE as the key/value have already been deleted */
5950 void sipe_buddy_free_all(struct sipe_core_private
*sipe_private
)
5952 g_hash_table_foreach_steal(sipe_private
->buddies
, sipe_buddy_remove
, NULL
);
5955 static void sipe_searchresults_im_buddy(PurpleConnection
*gc
, GList
*row
,
5956 SIPE_UNUSED_PARAMETER
void *user_data
)
5958 PurpleAccount
*acct
= purple_connection_get_account(gc
);
5959 char *id
= sip_uri_from_name((gchar
*)g_list_nth_data(row
, 0));
5960 PurpleConversation
*conv
= purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM
, id
, acct
);
5962 conv
= purple_conversation_new(PURPLE_CONV_TYPE_IM
, acct
, id
);
5963 purple_conversation_present(conv
);
5967 static void sipe_searchresults_add_buddy(PurpleConnection
*gc
, GList
*row
,
5968 SIPE_UNUSED_PARAMETER
void *user_data
)
5971 purple_blist_request_add_buddy(purple_connection_get_account(gc
),
5972 g_list_nth_data(row
, 0), _("Other Contacts"), g_list_nth_data(row
, 1));
5975 static gboolean
process_search_contact_response(struct sipe_core_private
*sipe_private
,
5977 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
5979 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
5980 PurpleNotifySearchResults
*results
;
5981 PurpleNotifySearchColumn
*column
;
5982 sipe_xml
*searchResults
;
5983 const sipe_xml
*mrow
;
5984 int match_count
= 0;
5985 gboolean more
= FALSE
;
5988 SIPE_DEBUG_INFO("process_search_contact_response: body:\n%s", msg
->body
? msg
->body
: "");
5990 searchResults
= sipe_xml_parse(msg
->body
, msg
->bodylen
);
5991 if (!searchResults
) {
5992 SIPE_DEBUG_INFO_NOFORMAT("process_search_contact_response: no parseable searchResults");
5996 results
= purple_notify_searchresults_new();
5998 if (results
== NULL
) {
5999 SIPE_DEBUG_ERROR_NOFORMAT("purple_parse_searchreply: Unable to display the search results.");
6000 purple_notify_error(sip
->gc
, NULL
, _("Unable to display the search results"), NULL
);
6002 sipe_xml_free(searchResults
);
6006 column
= purple_notify_searchresults_column_new(_("User name"));
6007 purple_notify_searchresults_column_add(results
, column
);
6009 column
= purple_notify_searchresults_column_new(_("Name"));
6010 purple_notify_searchresults_column_add(results
, column
);
6012 column
= purple_notify_searchresults_column_new(_("Company"));
6013 purple_notify_searchresults_column_add(results
, column
);
6015 column
= purple_notify_searchresults_column_new(_("Country"));
6016 purple_notify_searchresults_column_add(results
, column
);
6018 column
= purple_notify_searchresults_column_new(_("Email"));
6019 purple_notify_searchresults_column_add(results
, column
);
6021 for (mrow
= sipe_xml_child(searchResults
, "Body/Array/row"); mrow
; mrow
= sipe_xml_twin(mrow
)) {
6024 gchar
**uri_parts
= g_strsplit(sipe_xml_attribute(mrow
, "uri"), ":", 2);
6025 row
= g_list_append(row
, g_strdup(uri_parts
[1]));
6026 g_strfreev(uri_parts
);
6028 row
= g_list_append(row
, g_strdup(sipe_xml_attribute(mrow
, "displayName")));
6029 row
= g_list_append(row
, g_strdup(sipe_xml_attribute(mrow
, "company")));
6030 row
= g_list_append(row
, g_strdup(sipe_xml_attribute(mrow
, "country")));
6031 row
= g_list_append(row
, g_strdup(sipe_xml_attribute(mrow
, "email")));
6033 purple_notify_searchresults_row_add(results
, row
);
6037 if ((mrow
= sipe_xml_child(searchResults
, "Body/directorySearch/moreAvailable")) != NULL
) {
6038 char *data
= sipe_xml_data(mrow
);
6039 more
= (g_strcasecmp(data
, "true") == 0);
6043 secondary
= g_strdup_printf(
6044 dngettext(PACKAGE_NAME
,
6045 "Found %d contact%s:",
6046 "Found %d contacts%s:", match_count
),
6047 match_count
, more
? _(" (more matched your query)") : "");
6049 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_IM
, sipe_searchresults_im_buddy
);
6050 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_ADD
, sipe_searchresults_add_buddy
);
6051 purple_notify_searchresults(sip
->gc
, NULL
, NULL
, secondary
, results
, NULL
, NULL
);
6054 sipe_xml_free(searchResults
);
6058 void sipe_search_contact_with_cb(PurpleConnection
*gc
, PurpleRequestFields
*fields
)
6060 GList
*entries
= purple_request_field_group_get_fields(purple_request_fields_get_groups(fields
)->data
);
6061 gchar
**attrs
= g_new(gchar
*, g_list_length(entries
) + 1);
6067 PurpleRequestField
*field
= entries
->data
;
6068 const char *id
= purple_request_field_get_id(field
);
6069 const char *value
= purple_request_field_string_get_value(field
);
6071 SIPE_DEBUG_INFO("sipe_search_contact_with_cb: %s = '%s'", id
, value
? value
: "");
6073 if (value
!= NULL
) attrs
[i
++] = g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW
, id
, value
);
6074 } while ((entries
= g_list_next(entries
)) != NULL
);
6078 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
6079 gchar
*domain_uri
= sip_uri_from_name(sipe_private
->public.sip_domain
);
6080 gchar
*query
= g_strjoinv(NULL
, attrs
);
6081 gchar
*body
= g_strdup_printf(SIPE_SOAP_SEARCH_CONTACT
, 100, query
);
6082 SIPE_DEBUG_INFO("sipe_search_contact_with_cb: body:\n%s", body
? body
: "");
6083 send_soap_request_with_cb(sipe_private
, domain_uri
, body
,
6084 process_search_contact_response
, NULL
);
6093 static void sipe_publish_get_cat_state_user_to_clear(SIPE_UNUSED_PARAMETER
const char *name
,
6097 struct sipe_publication
*publication
= value
;
6099 g_string_append_printf( str
,
6100 SIPE_PUB_XML_PUBLICATION_CLEAR
,
6101 publication
->category
,
6102 publication
->instance
,
6103 publication
->container
,
6104 publication
->version
,
6108 void sipe_core_reset_status(struct sipe_core_public
*sipe_public
)
6110 struct sipe_core_private
*sipe_private
= SIPE_CORE_PRIVATE
;
6111 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA
;
6112 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) /* 2007+ */
6114 GString
* str
= g_string_new(NULL
);
6115 gchar
*publications
;
6117 if (!sip
->user_state_publications
|| g_hash_table_size(sip
->user_state_publications
) == 0) {
6118 SIPE_DEBUG_INFO_NOFORMAT("sipe_reset_status: no userState publications, exiting.");
6122 g_hash_table_foreach(sip
->user_state_publications
, (GHFunc
)sipe_publish_get_cat_state_user_to_clear
, str
);
6123 publications
= g_string_free(str
, FALSE
);
6125 send_presence_publish(sipe_private
, publications
);
6126 g_free(publications
);
6130 send_presence_soap0(sipe_private
, FALSE
, TRUE
);
6134 /** for Access levels menu */
6135 #define INDENT_FMT " %s"
6137 /** Member is directly placed to access level container.
6138 * For example SIP URI of user is in the container.
6140 #define INDENT_MARKED_FMT "* %s"
6142 /** Member is indirectly belong to access level container.
6143 * For example 'sameEnterprise' is in the container and user
6144 * belongs to that same enterprise.
6146 #define INDENT_MARKED_INHERITED_FMT "= %s"
6148 GSList
*sipe_core_buddy_info(struct sipe_core_public
*sipe_public
,
6150 const gchar
*status_name
,
6153 struct sipe_core_private
*sipe_private
= SIPE_CORE_PRIVATE
;
6155 gboolean is_oof_note
= FALSE
;
6156 gchar
*activity
= NULL
;
6157 gchar
*calendar
= NULL
;
6158 gchar
*meeting_subject
= NULL
;
6159 gchar
*meeting_location
= NULL
;
6160 gchar
*access_text
= NULL
;
6161 GSList
*info
= NULL
;
6163 #define SIPE_ADD_BUDDY_INFO(l, t) \
6165 struct sipe_buddy_info *sbi = g_malloc(sizeof(struct sipe_buddy_info)); \
6168 info = g_slist_append(info, sbi); \
6171 if (sipe_public
) { //happens on pidgin exit
6172 struct sipe_buddy
*sbuddy
= g_hash_table_lookup(sipe_private
->buddies
, name
);
6174 note
= sbuddy
->note
;
6175 is_oof_note
= sbuddy
->is_oof_note
;
6176 activity
= sbuddy
->activity
;
6177 calendar
= sipe_cal_get_description(sbuddy
);
6178 meeting_subject
= sbuddy
->meeting_subject
;
6179 meeting_location
= sbuddy
->meeting_location
;
6181 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
6182 gboolean is_group_access
= FALSE
;
6183 const int container_id
= sipe_find_access_level(sipe_private
, "user", sipe_get_no_sip_uri(name
), &is_group_access
);
6184 const char *access_level
= sipe_get_access_level_name(container_id
);
6185 access_text
= is_group_access
?
6186 g_strdup(access_level
) :
6187 g_strdup_printf(INDENT_MARKED_FMT
, access_level
);
6194 gchar
*status_str
= g_strdup(activity
? activity
: status_name
);
6196 SIPE_ADD_BUDDY_INFO(_("Status"), status_str
);
6198 if (is_online
&& !is_empty(calendar
))
6200 SIPE_ADD_BUDDY_INFO(_("Calendar"), calendar
);
6204 if (!is_empty(meeting_location
))
6206 SIPE_ADD_BUDDY_INFO(_("Meeting in"), g_strdup(meeting_location
));
6208 if (!is_empty(meeting_subject
))
6210 SIPE_ADD_BUDDY_INFO(_("Meeting about"), g_strdup(meeting_subject
));
6214 SIPE_DEBUG_INFO("sipe_tooltip_text: %s note: '%s'", name
, note
);
6215 SIPE_ADD_BUDDY_INFO(is_oof_note
? _("Out of office note") : _("Note"),
6216 g_strdup_printf("<i>%s</i>", note
));
6219 SIPE_ADD_BUDDY_INFO(_("Access level"), access_text
);
6225 static PurpleBuddy
*
6226 purple_blist_add_buddy_clone(PurpleGroup
* group
, PurpleBuddy
* buddy
)
6229 const gchar
*server_alias
, *email
;
6230 const PurpleStatus
*status
= purple_presence_get_active_status(purple_buddy_get_presence(buddy
));
6232 clone
= purple_buddy_new(buddy
->account
, buddy
->name
, buddy
->alias
);
6234 purple_blist_add_buddy(clone
, NULL
, group
, NULL
);
6236 server_alias
= purple_buddy_get_server_alias(buddy
);
6238 purple_blist_server_alias_buddy(clone
, server_alias
);
6241 email
= purple_blist_node_get_string(&buddy
->node
, EMAIL_PROP
);
6243 purple_blist_node_set_string(&clone
->node
, EMAIL_PROP
, email
);
6246 purple_presence_set_status_active(purple_buddy_get_presence(clone
), purple_status_get_id(status
), TRUE
);
6248 purple_prpl_got_user_status(clone
->account
, clone
->name
, purple_status_get_id(status
), NULL
);
6253 sipe_buddy_menu_copy_to_cb(PurpleBlistNode
*node
, const char *group_name
)
6255 PurpleBuddy
*buddy
, *b
;
6256 PurpleConnection
*gc
;
6257 PurpleGroup
* group
= purple_find_group(group_name
);
6259 g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node
));
6261 buddy
= (PurpleBuddy
*)node
;
6263 SIPE_DEBUG_INFO("sipe_buddy_menu_copy_to_cb: copying %s to %s", buddy
->name
, group_name
);
6264 gc
= purple_account_get_connection(buddy
->account
);
6266 b
= purple_find_buddy_in_group(buddy
->account
, buddy
->name
, group
);
6268 purple_blist_add_buddy_clone(group
, buddy
);
6271 sipe_group_buddy(gc
, buddy
->name
, NULL
, group_name
);
6275 sipe_buddy_menu_chat_new_cb(PurpleBuddy
*buddy
)
6277 struct sipe_core_private
*sipe_private
= PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE
;
6279 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_new_cb: buddy->name=%s", buddy
->name
);
6281 /* 2007+ conference */
6282 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
))
6284 sipe_conf_add(sipe_private
, buddy
->name
);
6286 else /* 2005- multiparty chat */
6288 gchar
*self
= sip_uri_self(sipe_private
);
6289 struct sip_session
*session
;
6291 session
= sipe_session_add_chat(sipe_private
);
6292 session
->chat_title
= sipe_chat_get_name(session
->callid
);
6293 session
->roster_manager
= g_strdup(self
);
6295 session
->backend_session
= sipe_backend_chat_create(SIPE_CORE_PUBLIC
,
6297 session
->chat_title
,
6300 sipe_backend_chat_add(session
->backend_session
,
6303 sipe_invite(sipe_private
, session
, buddy
->name
, NULL
, NULL
, NULL
, FALSE
);
6310 * For 2007+ conference only.
6313 sipe_buddy_menu_chat_make_leader_cb(PurpleBuddy
*buddy
, const char *chat_title
)
6315 struct sipe_core_private
*sipe_private
= PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE
;
6316 struct sip_session
*session
;
6318 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_make_leader_cb: buddy->name=%s", buddy
->name
);
6319 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_make_leader_cb: chat_title=%s", chat_title
);
6321 session
= sipe_session_find_chat_by_title(sipe_private
, chat_title
);
6323 sipe_conf_modify_user_role(sipe_private
, session
, buddy
->name
);
6327 * For 2007+ conference only.
6330 sipe_buddy_menu_chat_remove_cb(PurpleBuddy
*buddy
, const char *chat_title
)
6332 struct sipe_core_private
*sipe_private
= PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE
;
6333 struct sip_session
*session
;
6335 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_remove_cb: buddy->name=%s", buddy
->name
);
6336 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_remove_cb: chat_title=%s", chat_title
);
6338 session
= sipe_session_find_chat_by_title(sipe_private
, chat_title
);
6340 sipe_conf_delete_user(sipe_private
, session
, buddy
->name
);
6344 sipe_buddy_menu_chat_invite_cb(PurpleBuddy
*buddy
, char *chat_title
)
6346 struct sipe_core_private
*sipe_private
= PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE
;
6347 struct sip_session
*session
;
6349 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_invite_cb: buddy->name=%s", buddy
->name
);
6350 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_invite_cb: chat_title=%s", chat_title
);
6352 session
= sipe_session_find_chat_by_title(sipe_private
, chat_title
);
6354 sipe_invite_to_chat(sipe_private
, session
, buddy
->name
);
6358 sipe_buddy_menu_make_call_cb(PurpleBuddy
*buddy
, const char *phone
)
6360 struct sipe_core_private
*sipe_private
= PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE
;
6362 SIPE_DEBUG_INFO("sipe_buddy_menu_make_call_cb: buddy->name=%s", buddy
->name
);
6364 char *tel_uri
= sip_to_tel_uri(phone
);
6366 SIPE_DEBUG_INFO("sipe_buddy_menu_make_call_cb: going to call number: %s", tel_uri
? tel_uri
: "");
6367 sip_csta_make_call(sipe_private
, tel_uri
);
6374 sipe_buddy_menu_access_level_help_cb(PurpleBuddy
*buddy
)
6376 /** Translators: replace with URL to localized page
6377 * If it doesn't exist copy the original URL */
6378 purple_notify_uri(buddy
->account
->gc
, _("https://sourceforge.net/apps/mediawiki/sipe/index.php?title=Access_Levels"));
6382 sipe_buddy_menu_send_email_cb(PurpleBuddy
*buddy
)
6385 SIPE_DEBUG_INFO("sipe_buddy_menu_send_email_cb: buddy->name=%s", buddy
->name
);
6387 email
= purple_blist_node_get_string(&buddy
->node
, EMAIL_PROP
);
6390 char *command_line
= g_strdup_printf(
6396 " mailto:%s", email
);
6397 SIPE_DEBUG_INFO("sipe_buddy_menu_send_email_cb: going to call email client: %s", command_line
);
6399 g_spawn_command_line_async(command_line
, NULL
);
6400 g_free(command_line
);
6404 SIPE_DEBUG_INFO("sipe_buddy_menu_send_email_cb: no email address stored for buddy=%s", buddy
->name
);
6409 sipe_buddy_menu_access_level_cb(PurpleBuddy
*buddy
,
6410 struct sipe_container
*container
)
6412 struct sipe_core_private
*sipe_private
= PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE
;
6413 struct sipe_container_member
*member
;
6415 if (!container
|| !container
->members
) return;
6417 member
= ((struct sipe_container_member
*)container
->members
->data
);
6419 if (!member
->type
) return;
6421 SIPE_DEBUG_INFO("sipe_buddy_menu_access_level_cb: container->id=%d, member->type=%s, member->value=%s",
6422 container
->id
, member
->type
, member
->value
? member
->value
: "");
6424 sipe_change_access_level(sipe_private
, container
->id
, member
->type
, member
->value
);
6428 sipe_get_access_control_menu(struct sipe_core_private
*sipe_private
,
6432 * A menu which appear when right-clicking on buddy in contact list.
6435 sipe_buddy_menu(PurpleBuddy
*buddy
)
6437 PurpleBlistNode
*g_node
;
6438 PurpleGroup
*group
, *gr_parent
;
6439 PurpleMenuAction
*act
;
6441 GList
*menu_groups
= NULL
;
6442 struct sipe_core_private
*sipe_private
= PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE
;
6443 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
6446 const char *phone_disp_str
;
6447 gchar
*self
= sip_uri_self(sipe_private
);
6449 SIPE_SESSION_FOREACH
{
6450 if (!sipe_strcase_equal(self
, buddy
->name
) && session
->chat_title
&& session
->backend_session
)
6452 if (sipe_backend_chat_find(session
->backend_session
, buddy
->name
))
6454 gboolean conf_op
= sipe_backend_chat_is_operator(session
->backend_session
, self
);
6456 if (session
->focus_uri
6457 && !sipe_backend_chat_is_operator(session
->backend_session
, buddy
->name
) /* Not conf OP */
6458 && conf_op
) /* We are a conf OP */
6460 gchar
*label
= g_strdup_printf(_("Make leader of '%s'"), session
->chat_title
);
6461 act
= purple_menu_action_new(label
,
6462 PURPLE_CALLBACK(sipe_buddy_menu_chat_make_leader_cb
),
6463 session
->chat_title
, NULL
);
6465 menu
= g_list_prepend(menu
, act
);
6468 if (session
->focus_uri
6469 && conf_op
) /* We are a conf OP */
6471 gchar
*label
= g_strdup_printf(_("Remove from '%s'"), session
->chat_title
);
6472 act
= purple_menu_action_new(label
,
6473 PURPLE_CALLBACK(sipe_buddy_menu_chat_remove_cb
),
6474 session
->chat_title
, NULL
);
6476 menu
= g_list_prepend(menu
, act
);
6481 if (!session
->focus_uri
6482 || (session
->focus_uri
&& !session
->locked
))
6484 gchar
*label
= g_strdup_printf(_("Invite to '%s'"), session
->chat_title
);
6485 act
= purple_menu_action_new(label
,
6486 PURPLE_CALLBACK(sipe_buddy_menu_chat_invite_cb
),
6487 session
->chat_title
, NULL
);
6489 menu
= g_list_prepend(menu
, act
);
6493 } SIPE_SESSION_FOREACH_END
;
6495 act
= purple_menu_action_new(_("New chat"),
6496 PURPLE_CALLBACK(sipe_buddy_menu_chat_new_cb
),
6498 menu
= g_list_prepend(menu
, act
);
6500 if (sip
->csta
&& !sip
->csta
->line_status
) {
6503 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_PROP
);
6504 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_DISPLAY_PROP
);
6506 gchar
*label
= g_strdup_printf(_("Work %s"),
6507 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
6508 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
6512 menu
= g_list_prepend(menu
, act
);
6516 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_MOBILE_PROP
);
6517 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_MOBILE_DISPLAY_PROP
);
6519 gchar
*label
= g_strdup_printf(_("Mobile %s"),
6520 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
6521 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
6525 menu
= g_list_prepend(menu
, act
);
6529 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_HOME_PROP
);
6530 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_HOME_DISPLAY_PROP
);
6532 gchar
*label
= g_strdup_printf(_("Home %s"),
6533 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
6534 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
6538 menu
= g_list_prepend(menu
, act
);
6542 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_OTHER_PROP
);
6543 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_OTHER_DISPLAY_PROP
);
6545 gchar
*label
= g_strdup_printf(_("Other %s"),
6546 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
6547 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
6551 menu
= g_list_prepend(menu
, act
);
6555 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_CUSTOM1_PROP
);
6556 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_CUSTOM1_DISPLAY_PROP
);
6558 gchar
*label
= g_strdup_printf(_("Custom1 %s"),
6559 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
6560 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
6564 menu
= g_list_prepend(menu
, act
);
6568 email
= purple_blist_node_get_string(&buddy
->node
, EMAIL_PROP
);
6570 act
= purple_menu_action_new(_("Send email..."),
6571 PURPLE_CALLBACK(sipe_buddy_menu_send_email_cb
),
6573 menu
= g_list_prepend(menu
, act
);
6577 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
6578 GList
*menu_access_levels
= sipe_get_access_control_menu(sipe_private
, buddy
->name
);
6580 act
= purple_menu_action_new(_("Access level"),
6582 NULL
, menu_access_levels
);
6583 menu
= g_list_prepend(menu
, act
);
6587 gr_parent
= purple_buddy_get_group(buddy
);
6588 for (g_node
= purple_blist_get_root(); g_node
; g_node
= g_node
->next
) {
6589 if (g_node
->type
!= PURPLE_BLIST_GROUP_NODE
)
6592 group
= (PurpleGroup
*)g_node
;
6593 if (group
== gr_parent
)
6596 if (purple_find_buddy_in_group(buddy
->account
, buddy
->name
, group
))
6599 act
= purple_menu_action_new(purple_group_get_name(group
),
6600 PURPLE_CALLBACK(sipe_buddy_menu_copy_to_cb
),
6602 menu_groups
= g_list_prepend(menu_groups
, act
);
6604 menu_groups
= g_list_reverse(menu_groups
);
6606 act
= purple_menu_action_new(_("Copy to"),
6609 menu
= g_list_prepend(menu
, act
);
6611 menu
= g_list_reverse(menu
);
6618 sipe_ask_access_domain_cb(PurpleConnection
*gc
, PurpleRequestFields
*fields
)
6620 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
6621 const char *domain
= purple_request_fields_get_string(fields
, "access_domain");
6622 int index
= purple_request_fields_get_choice(fields
, "container_id");
6623 /* move Blocked first */
6624 int i
= (index
== 4) ? 0 : index
+ 1;
6625 int container_id
= containers
[i
];
6627 SIPE_DEBUG_INFO("sipe_ask_access_domain_cb: domain=%s, container_id=(%d)%d", domain
? domain
: "", index
, container_id
);
6629 sipe_change_access_level(sipe_private
, container_id
, "domain", domain
);
6633 sipe_ask_access_domain(struct sipe_core_private
*sipe_private
)
6635 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
6636 PurpleAccount
*account
= sip
->account
;
6637 PurpleConnection
*gc
= sip
->gc
;
6638 PurpleRequestFields
*fields
;
6639 PurpleRequestFieldGroup
*g
;
6640 PurpleRequestField
*f
;
6642 fields
= purple_request_fields_new();
6644 g
= purple_request_field_group_new(NULL
);
6645 f
= purple_request_field_string_new("access_domain", _("Domain"), "partner-company.com", FALSE
);
6646 purple_request_field_set_required(f
, TRUE
);
6647 purple_request_field_group_add_field(g
, f
);
6649 f
= purple_request_field_choice_new("container_id", _("Access level"), 0);
6650 purple_request_field_choice_add(f
, _("Personal")); /* index 0 */
6651 purple_request_field_choice_add(f
, _("Team"));
6652 purple_request_field_choice_add(f
, _("Company"));
6653 purple_request_field_choice_add(f
, _("Public"));
6654 purple_request_field_choice_add(f
, _("Blocked")); /* index 4 */
6655 purple_request_field_choice_set_default_value(f
, 3); /* index */
6656 purple_request_field_set_required(f
, TRUE
);
6657 purple_request_field_group_add_field(g
, f
);
6659 purple_request_fields_add_group(fields
, g
);
6661 purple_request_fields(gc
, _("Add new domain"),
6662 _("Add new domain"), NULL
, fields
,
6663 _("Add"), G_CALLBACK(sipe_ask_access_domain_cb
),
6665 account
, NULL
, NULL
, gc
);
6669 sipe_buddy_menu_access_level_add_domain_cb(PurpleBuddy
*buddy
)
6671 sipe_ask_access_domain(PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE
);
6675 sipe_get_access_levels_menu(struct sipe_core_private
*sipe_private
,
6676 const char* member_type
,
6677 const char* member_value
,
6678 const gboolean extra_menu
)
6680 GList
*menu_access_levels
= NULL
;
6683 PurpleMenuAction
*act
;
6684 struct sipe_container
*container
;
6685 struct sipe_container_member
*member
;
6686 gboolean is_group_access
= FALSE
;
6687 int container_id
= sipe_find_access_level(sipe_private
, member_type
, member_value
, &is_group_access
);
6689 for (i
= 1; i
<= CONTAINERS_LEN
; i
++) {
6690 /* to put Blocked level last in menu list.
6691 * Blocked should remaim in the first place in the containers[] array.
6693 unsigned int j
= (i
== CONTAINERS_LEN
) ? 0 : i
;
6694 const char *acc_level_name
= sipe_get_access_level_name(containers
[j
]);
6696 container
= g_new0(struct sipe_container
, 1);
6697 member
= g_new0(struct sipe_container_member
, 1);
6698 container
->id
= containers
[j
];
6699 container
->members
= g_slist_append(container
->members
, member
);
6700 member
->type
= g_strdup(member_type
);
6701 member
->value
= g_strdup(member_value
);
6703 /* current container/access level */
6704 if (((int)containers
[j
]) == container_id
) {
6705 menu_name
= is_group_access
?
6706 g_strdup_printf(INDENT_MARKED_INHERITED_FMT
, acc_level_name
) :
6707 g_strdup_printf(INDENT_MARKED_FMT
, acc_level_name
);
6709 menu_name
= g_strdup_printf(INDENT_FMT
, acc_level_name
);
6712 act
= purple_menu_action_new(menu_name
,
6713 PURPLE_CALLBACK(sipe_buddy_menu_access_level_cb
),
6716 menu_access_levels
= g_list_prepend(menu_access_levels
, act
);
6719 if (extra_menu
&& (container_id
>= 0)) {
6721 act
= purple_menu_action_new(" --------------", NULL
, NULL
, NULL
);
6722 menu_access_levels
= g_list_prepend(menu_access_levels
, act
);
6724 if (!is_group_access
) {
6725 container
= g_new0(struct sipe_container
, 1);
6726 member
= g_new0(struct sipe_container_member
, 1);
6728 container
->members
= g_slist_append(container
->members
, member
);
6729 member
->type
= g_strdup(member_type
);
6730 member
->value
= g_strdup(member_value
);
6732 /* Translators: remove (clear) previously assigned access level */
6733 menu_name
= g_strdup_printf(INDENT_FMT
, _("Unspecify"));
6734 act
= purple_menu_action_new(menu_name
,
6735 PURPLE_CALLBACK(sipe_buddy_menu_access_level_cb
),
6738 menu_access_levels
= g_list_prepend(menu_access_levels
, act
);
6742 menu_access_levels
= g_list_reverse(menu_access_levels
);
6743 return menu_access_levels
;
6747 sipe_get_access_groups_menu(struct sipe_core_private
*sipe_private
)
6749 GList
*menu_access_groups
= NULL
;
6750 PurpleMenuAction
*act
;
6751 GSList
*access_domains
= NULL
;
6756 act
= purple_menu_action_new(_("People in my company"),
6758 NULL
, sipe_get_access_levels_menu(sipe_private
, "sameEnterprise", NULL
, FALSE
));
6759 menu_access_groups
= g_list_prepend(menu_access_groups
, act
);
6761 /* this is original name, don't edit */
6762 act
= purple_menu_action_new(_("People in domains connected with my company"),
6764 NULL
, sipe_get_access_levels_menu(sipe_private
, "federated", NULL
, FALSE
));
6765 menu_access_groups
= g_list_prepend(menu_access_groups
, act
);
6767 act
= purple_menu_action_new(_("People in public domains"),
6769 NULL
, sipe_get_access_levels_menu(sipe_private
, "publicCloud", NULL
, TRUE
));
6770 menu_access_groups
= g_list_prepend(menu_access_groups
, act
);
6772 access_domains
= sipe_get_access_domains(sipe_private
);
6773 entry
= access_domains
;
6775 domain
= entry
->data
;
6777 menu_name
= g_strdup_printf(_("People at %s"), domain
);
6778 act
= purple_menu_action_new(menu_name
,
6780 NULL
, sipe_get_access_levels_menu(sipe_private
, "domain", g_strdup(domain
), TRUE
));
6781 menu_access_groups
= g_list_prepend(menu_access_groups
, act
);
6784 entry
= entry
->next
;
6788 /* People in domains connected with my company */
6789 act
= purple_menu_action_new("-------------------------------------------", NULL
, NULL
, NULL
);
6790 menu_access_groups
= g_list_prepend(menu_access_groups
, act
);
6792 act
= purple_menu_action_new(_("Add new domain..."),
6793 PURPLE_CALLBACK(sipe_buddy_menu_access_level_add_domain_cb
),
6795 menu_access_groups
= g_list_prepend(menu_access_groups
, act
);
6797 menu_access_groups
= g_list_reverse(menu_access_groups
);
6799 return menu_access_groups
;
6803 sipe_get_access_control_menu(struct sipe_core_private
*sipe_private
,
6806 GList
*menu_access_levels
= NULL
;
6807 GList
*menu_access_groups
= NULL
;
6809 PurpleMenuAction
*act
;
6811 menu_access_levels
= sipe_get_access_levels_menu(sipe_private
, "user", sipe_get_no_sip_uri(uri
), TRUE
);
6813 menu_access_groups
= sipe_get_access_groups_menu(sipe_private
);
6815 menu_name
= g_strdup_printf(INDENT_FMT
, _("Access groups"));
6816 act
= purple_menu_action_new(menu_name
,
6818 NULL
, menu_access_groups
);
6820 menu_access_levels
= g_list_append(menu_access_levels
, act
);
6822 menu_name
= g_strdup_printf(INDENT_FMT
, _("Online help..."));
6823 act
= purple_menu_action_new(menu_name
,
6824 PURPLE_CALLBACK(sipe_buddy_menu_access_level_help_cb
),
6827 menu_access_levels
= g_list_append(menu_access_levels
, act
);
6829 return menu_access_levels
;
6833 sipe_conf_modify_lock(PurpleChat
*chat
, gboolean locked
)
6835 struct sipe_core_private
*sipe_private
= PURPLE_CHAT_TO_SIPE_CORE_PRIVATE
;
6836 struct sip_session
*session
;
6838 session
= sipe_session_find_chat_by_title(sipe_private
,
6839 (gchar
*)g_hash_table_lookup(chat
->components
, "channel"));
6840 sipe_conf_modify_conference_lock(sipe_private
, session
, locked
);
6844 sipe_chat_menu_unlock_cb(PurpleChat
*chat
)
6846 SIPE_DEBUG_INFO_NOFORMAT("sipe_chat_menu_unlock_cb() called");
6847 sipe_conf_modify_lock(chat
, FALSE
);
6851 sipe_chat_menu_lock_cb(PurpleChat
*chat
)
6853 SIPE_DEBUG_INFO_NOFORMAT("sipe_chat_menu_lock_cb() called");
6854 sipe_conf_modify_lock(chat
, TRUE
);
6858 sipe_chat_menu(PurpleChat
*chat
)
6860 PurpleMenuAction
*act
;
6862 struct sipe_core_private
*sipe_private
= PURPLE_CHAT_TO_SIPE_CORE_PRIVATE
;
6863 struct sip_session
*session
;
6866 session
= sipe_session_find_chat_by_title(sipe_private
,
6867 (gchar
*)g_hash_table_lookup(chat
->components
, "channel"));
6868 if (!session
) return NULL
;
6870 self
= sip_uri_self(sipe_private
);
6872 if (session
->focus_uri
&&
6873 sipe_backend_chat_is_operator(session
->backend_session
, self
))
6875 if (session
->locked
) {
6876 act
= purple_menu_action_new(_("Unlock"),
6877 PURPLE_CALLBACK(sipe_chat_menu_unlock_cb
),
6879 menu
= g_list_prepend(menu
, act
);
6881 act
= purple_menu_action_new(_("Lock"),
6882 PURPLE_CALLBACK(sipe_chat_menu_lock_cb
),
6884 menu
= g_list_prepend(menu
, act
);
6888 menu
= g_list_reverse(menu
);
6895 process_get_info_response(struct sipe_core_private
*sipe_private
,
6896 struct sipmsg
*msg
, struct transaction
*trans
)
6898 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
6899 char *uri
= trans
->payload
->data
;
6901 PurpleNotifyUserInfo
*info
;
6902 PurpleBuddy
*pbuddy
= NULL
;
6903 struct sipe_buddy
*sbuddy
;
6904 const char *alias
= NULL
;
6905 char *device_name
= NULL
;
6906 char *server_alias
= NULL
;
6907 char *phone_number
= NULL
;
6910 char *first_name
= NULL
;
6911 char *last_name
= NULL
;
6913 if (!sip
) return FALSE
;
6915 SIPE_DEBUG_INFO("Fetching %s's user info for %s", uri
, sipe_private
->username
);
6917 pbuddy
= purple_find_buddy((PurpleAccount
*)sip
->account
, uri
);
6918 alias
= purple_buddy_get_local_alias(pbuddy
);
6920 //will query buddy UA's capabilities and send answer to log
6921 sipe_options_request(sipe_private
, uri
);
6923 sbuddy
= g_hash_table_lookup(sipe_private
->buddies
, uri
);
6925 device_name
= sbuddy
->device_name
? g_strdup(sbuddy
->device_name
) : NULL
;
6928 info
= purple_notify_user_info_new();
6930 if (msg
->response
!= 200) {
6931 SIPE_DEBUG_INFO("process_get_info_response: SERVICE response is %d", msg
->response
);
6933 sipe_xml
*searchResults
;
6934 const sipe_xml
*mrow
;
6936 SIPE_DEBUG_INFO("process_get_info_response: body:\n%s", msg
->body
? msg
->body
: "");
6937 searchResults
= sipe_xml_parse(msg
->body
, msg
->bodylen
);
6938 if (!searchResults
) {
6939 SIPE_DEBUG_INFO_NOFORMAT("process_get_info_response: no parseable searchResults");
6940 } else if ((mrow
= sipe_xml_child(searchResults
, "Body/Array/row"))) {
6942 server_alias
= g_strdup(sipe_xml_attribute(mrow
, "displayName"));
6943 email
= g_strdup(sipe_xml_attribute(mrow
, "email"));
6944 phone_number
= g_strdup(sipe_xml_attribute(mrow
, "phone"));
6946 /* For 2007 system we will take this from ContactCard -
6947 * it has cleaner tel: URIs at least
6949 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
6950 char *tel_uri
= sip_to_tel_uri(phone_number
);
6951 /* trims its parameters, so call first */
6952 sipe_update_user_info(sipe_private
, uri
, ALIAS_PROP
, server_alias
);
6953 sipe_update_user_info(sipe_private
, uri
, EMAIL_PROP
, email
);
6954 sipe_update_user_info(sipe_private
, uri
, PHONE_PROP
, tel_uri
);
6955 sipe_update_user_info(sipe_private
, uri
, PHONE_DISPLAY_PROP
, phone_number
);
6959 if (server_alias
&& strlen(server_alias
) > 0) {
6960 purple_notify_user_info_add_pair(info
, _("Display name"), server_alias
);
6962 if ((value
= sipe_xml_attribute(mrow
, "title")) && strlen(value
) > 0) {
6963 purple_notify_user_info_add_pair(info
, _("Job title"), value
);
6965 if ((value
= sipe_xml_attribute(mrow
, "office")) && strlen(value
) > 0) {
6966 purple_notify_user_info_add_pair(info
, _("Office"), value
);
6968 if (phone_number
&& strlen(phone_number
) > 0) {
6969 purple_notify_user_info_add_pair(info
, _("Business phone"), phone_number
);
6971 if ((value
= sipe_xml_attribute(mrow
, "company")) && strlen(value
) > 0) {
6972 purple_notify_user_info_add_pair(info
, _("Company"), value
);
6974 if ((value
= sipe_xml_attribute(mrow
, "city")) && strlen(value
) > 0) {
6975 purple_notify_user_info_add_pair(info
, _("City"), value
);
6977 if ((value
= sipe_xml_attribute(mrow
, "state")) && strlen(value
) > 0) {
6978 purple_notify_user_info_add_pair(info
, _("State"), value
);
6980 if ((value
= sipe_xml_attribute(mrow
, "country")) && strlen(value
) > 0) {
6981 purple_notify_user_info_add_pair(info
, _("Country"), value
);
6983 if (email
&& strlen(email
) > 0) {
6984 purple_notify_user_info_add_pair(info
, _("Email address"), email
);
6988 sipe_xml_free(searchResults
);
6991 purple_notify_user_info_add_section_break(info
);
6993 if (is_empty(server_alias
)) {
6994 g_free(server_alias
);
6995 server_alias
= g_strdup(purple_buddy_get_server_alias(pbuddy
));
6997 purple_notify_user_info_add_pair(info
, _("Display name"), server_alias
);
7001 /* present alias if it differs from server alias */
7002 if (alias
&& !sipe_strequal(alias
, server_alias
))
7004 purple_notify_user_info_add_pair(info
, _("Alias"), alias
);
7007 if (is_empty(email
)) {
7009 email
= g_strdup(purple_blist_node_get_string(&pbuddy
->node
, EMAIL_PROP
));
7011 purple_notify_user_info_add_pair(info
, _("Email address"), email
);
7015 site
= purple_blist_node_get_string(&pbuddy
->node
, SITE_PROP
);
7017 purple_notify_user_info_add_pair(info
, _("Site"), site
);
7020 sipe_get_first_last_names(sipe_private
, uri
, &first_name
, &last_name
);
7021 if (first_name
&& last_name
) {
7022 char *link
= g_strconcat("http://www.linkedin.com/pub/dir/", first_name
, "/", last_name
, NULL
);
7024 purple_notify_user_info_add_pair(info
, _("Find on LinkedIn"), link
);
7031 purple_notify_user_info_add_pair(info
, _("Device"), device_name
);
7034 /* show a buddy's user info in a nice dialog box */
7035 purple_notify_userinfo(sip
->gc
, /* connection the buddy info came through */
7036 uri
, /* buddy's URI */
7038 NULL
, /* callback called when dialog closed */
7039 NULL
); /* userdata for callback */
7041 g_free(phone_number
);
7042 g_free(server_alias
);
7044 g_free(device_name
);
7050 * AD search first, LDAP based
7052 void sipe_get_info(PurpleConnection
*gc
, const char *username
)
7054 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
7055 gchar
*domain_uri
= sip_uri_from_name(sipe_private
->public.sip_domain
);
7056 char *row
= g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW
, "msRTCSIP-PrimaryUserAddress", username
);
7057 gchar
*body
= g_strdup_printf(SIPE_SOAP_SEARCH_CONTACT
, 1, row
);
7058 struct transaction_payload
*payload
= g_new0(struct transaction_payload
, 1);
7060 payload
->destroy
= g_free
;
7061 payload
->data
= g_strdup(username
);
7063 SIPE_DEBUG_INFO("sipe_get_contact_data: body:\n%s", body
? body
: "");
7064 send_soap_request_with_cb(sipe_private
, domain_uri
, body
,
7065 process_get_info_response
, payload
);