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"
85 #include "sipe-media.h"
87 #include "sipe-utils.h"
92 #define SIPE_IDLE_SET_DELAY 1 /* 1 sec */
94 #define UPDATE_CALENDAR_DELAY 1*60 /* 1 min */
95 #define UPDATE_CALENDAR_INTERVAL 30*60 /* 30 min */
97 /* Status identifiers (see also: sipe_status_types()) */
98 #define SIPE_STATUS_ID_UNKNOWN purple_primitive_get_id_from_type(PURPLE_STATUS_UNSET) /* Unset (primitive) */
99 #define SIPE_STATUS_ID_OFFLINE purple_primitive_get_id_from_type(PURPLE_STATUS_OFFLINE) /* Offline (primitive) */
100 #define SIPE_STATUS_ID_AVAILABLE purple_primitive_get_id_from_type(PURPLE_STATUS_AVAILABLE) /* Online */
101 /* PURPLE_STATUS_UNAVAILABLE: */
102 #define SIPE_STATUS_ID_BUSY "busy" /* Busy */
103 #define SIPE_STATUS_ID_BUSYIDLE "busyidle" /* BusyIdle */
104 #define SIPE_STATUS_ID_DND "do-not-disturb" /* Do Not Disturb */
105 #define SIPE_STATUS_ID_IN_MEETING "in-a-meeting" /* In a meeting */
106 #define SIPE_STATUS_ID_IN_CONF "in-a-conference" /* In a conference */
107 #define SIPE_STATUS_ID_ON_PHONE "on-the-phone" /* On the phone */
108 #define SIPE_STATUS_ID_INVISIBLE purple_primitive_get_id_from_type(PURPLE_STATUS_INVISIBLE) /* Appear Offline */
109 /* PURPLE_STATUS_AWAY: */
110 #define SIPE_STATUS_ID_IDLE "idle" /* Idle/Inactive */
111 #define SIPE_STATUS_ID_BRB "be-right-back" /* Be Right Back */
112 #define SIPE_STATUS_ID_AWAY purple_primitive_get_id_from_type(PURPLE_STATUS_AWAY) /* Away (primitive) */
113 /** Reuters status (user settable) */
114 #define SIPE_STATUS_ID_LUNCH "out-to-lunch" /* Out To Lunch */
115 /* ??? PURPLE_STATUS_EXTENDED_AWAY */
116 /* ??? PURPLE_STATUS_MOBILE */
117 /* ??? PURPLE_STATUS_TUNE */
119 /* Status attributes (see also sipe_status_types() */
120 #define SIPE_STATUS_ATTR_ID_MESSAGE "message"
122 static struct sipe_activity_map_struct
127 const char *status_id
;
129 } const sipe_activity_map
[] =
131 /* This has nothing to do with Availability numbers, like 3500 (online).
132 * Just a mapping of Communicator Activities to Purple statuses to be able display them in Pidgin.
134 { SIPE_ACTIVITY_UNSET
, "unset", NULL
, NULL
},
135 { SIPE_ACTIVITY_ONLINE
, "online", NULL
, NULL
},
136 { SIPE_ACTIVITY_INACTIVE
, SIPE_STATUS_ID_IDLE
, N_("Inactive") , NULL
},
137 { SIPE_ACTIVITY_BUSY
, SIPE_STATUS_ID_BUSY
, N_("Busy") , SIPE_STATUS_ID_BUSY
},
138 { SIPE_ACTIVITY_BUSYIDLE
, SIPE_STATUS_ID_BUSYIDLE
, N_("Busy-Idle") , NULL
},
139 { SIPE_ACTIVITY_DND
, SIPE_STATUS_ID_DND
, NULL
, SIPE_STATUS_ID_DND
},
140 { SIPE_ACTIVITY_BRB
, SIPE_STATUS_ID_BRB
, N_("Be right back") , SIPE_STATUS_ID_BRB
},
141 { SIPE_ACTIVITY_AWAY
, "away", NULL
, NULL
},
142 { SIPE_ACTIVITY_LUNCH
, SIPE_STATUS_ID_LUNCH
, N_("Out to lunch") , NULL
},
143 { SIPE_ACTIVITY_OFFLINE
, "offline", NULL
, NULL
},
144 { SIPE_ACTIVITY_ON_PHONE
, SIPE_STATUS_ID_ON_PHONE
, N_("In a call") , NULL
},
145 { SIPE_ACTIVITY_IN_CONF
, SIPE_STATUS_ID_IN_CONF
, N_("In a conference") , NULL
},
146 { SIPE_ACTIVITY_IN_MEETING
, SIPE_STATUS_ID_IN_MEETING
, N_("In a meeting") , NULL
},
147 { SIPE_ACTIVITY_OOF
, "out-of-office", N_("Out of office") , NULL
},
148 { SIPE_ACTIVITY_URGENT_ONLY
, "urgent-interruptions-only", N_("Urgent interruptions only") , NULL
}
150 /** @param x is sipe_activity */
151 #define SIPE_ACTIVITY_I18N(x) gettext(sipe_activity_map[x].desc)
154 sipe_get_activity_by_token(const char *token
)
158 for (i
= 0; i
< SIPE_ACTIVITY_NUM_TYPES
; i
++)
160 if (sipe_strequal(token
, sipe_activity_map
[i
].token
))
161 return sipe_activity_map
[i
].type
;
164 return sipe_activity_map
[0].type
;
168 sipe_get_activity_desc_by_token(const char *token
)
170 if (!token
) return NULL
;
172 return SIPE_ACTIVITY_I18N(sipe_get_activity_by_token(token
));
175 static void send_presence_status(struct sipe_core_private
*sipe_private
,
179 * @param from0 from URI (with 'sip:' prefix). Will be filled with self-URI if NULL passed.
182 send_soap_request_with_cb(struct sipe_core_private
*sipe_private
,
185 TransCallback callback
,
186 struct transaction_payload
*payload
)
188 gchar
*from
= from0
? g_strdup(from0
) : sip_uri_self(sipe_private
);
189 gchar
*contact
= get_contact(sipe_private
);
190 gchar
*hdr
= g_strdup_printf("Contact: %s\r\n"
191 "Content-Type: application/SOAP+xml\r\n",contact
);
193 struct transaction
*trans
= sip_transport_service(sipe_private
,
198 trans
->payload
= payload
;
205 static void send_soap_request(struct sipe_core_private
*sipe_private
,
208 send_soap_request_with_cb(sipe_private
, NULL
, body
, NULL
, NULL
);
212 * Returns pointer to URI without sip: prefix if any
214 * @param sip_uri SIP URI possibly with sip: prefix. Example: sip:first.last@hq.company.com
215 * @return pointer to URL without sip: prefix. Coresponding example: first.last@hq.company.com
217 * Doesn't allocate memory
220 sipe_get_no_sip_uri(const char *sip_uri
)
222 const char *prefix
= "sip:";
223 if (!sip_uri
) return NULL
;
225 if (g_str_has_prefix(sip_uri
, prefix
)) {
226 return (sip_uri
+strlen(prefix
));
233 sipe_contact_set_acl (struct sipe_core_private
*sipe_private
,
237 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
238 gchar
* body
= g_strdup_printf(SIPE_SOAP_ALLOW_DENY
, who
, rights
, sip
->acl_delta
++);
239 send_soap_request(sipe_private
, body
);
244 sipe_change_access_level(struct sipe_core_private
*sipe_private
,
245 const int container_id
,
250 sipe_core_contact_allow_deny (struct sipe_core_public
*sipe_public
,
251 const gchar
* who
, gboolean allow
)
253 struct sipe_core_private
*sipe_private
= SIPE_CORE_PRIVATE
;
256 SIPE_DEBUG_INFO("Authorizing contact %s", who
);
258 SIPE_DEBUG_INFO("Blocking contact %s", who
);
261 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
262 sipe_change_access_level(sipe_private
, (allow
? -1 : 32000), "user", sipe_get_no_sip_uri(who
));
264 sipe_contact_set_acl(sipe_private
, who
, allow
? "AA" : "BD");
269 void sipe_auth_user_cb(void * data
)
271 struct sipe_auth_job
* job
= (struct sipe_auth_job
*) data
;
274 sipe_core_contact_allow_deny((struct sipe_core_public
*)job
->sipe_private
, job
->who
, TRUE
);
279 void sipe_deny_user_cb(void * data
)
281 struct sipe_auth_job
* job
= (struct sipe_auth_job
*) data
;
284 sipe_core_contact_allow_deny((struct sipe_core_public
*)job
->sipe_private
, job
->who
, FALSE
);
288 /** @applicable: 2005-
291 sipe_process_presence_wpending (struct sipe_core_private
*sipe_private
,
294 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
296 const sipe_xml
*watcher
;
297 // Ensure it's either not a response (eg it's a BENOTIFY) or that it's a 200 OK response
298 if (msg
->response
!= 0 && msg
->response
!= 200) return;
300 if (msg
->bodylen
== 0 || msg
->body
== NULL
|| sipe_strequal(sipmsg_find_header(msg
, "Event"), "msrtc.wpending")) return;
302 watchers
= sipe_xml_parse(msg
->body
, msg
->bodylen
);
303 if (!watchers
) return;
305 for (watcher
= sipe_xml_child(watchers
, "watcher"); watcher
; watcher
= sipe_xml_twin(watcher
)) {
306 gchar
* remote_user
= g_strdup(sipe_xml_attribute(watcher
, "uri"));
307 gchar
* alias
= g_strdup(sipe_xml_attribute(watcher
, "displayName"));
308 gboolean on_list
= g_hash_table_lookup(sipe_private
->buddies
, remote_user
) != NULL
;
310 // TODO pull out optional displayName to pass as alias
312 struct sipe_auth_job
* job
= g_new0(struct sipe_auth_job
, 1);
313 job
->who
= remote_user
;
314 job
->sipe_private
= sipe_private
;
315 purple_account_request_authorization(
329 sipe_xml_free(watchers
);
334 sipe_group_add(struct sipe_core_private
*sipe_private
,
335 struct sipe_group
* group
)
337 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
338 PurpleGroup
* purple_group
= purple_find_group(group
->name
);
340 purple_group
= purple_group_new(group
->name
);
341 purple_blist_add_group(purple_group
, NULL
);
345 group
->purple_group
= purple_group
;
346 sip
->groups
= g_slist_append(sip
->groups
, group
);
347 SIPE_DEBUG_INFO("added group %s (id %d)", group
->name
, group
->id
);
349 SIPE_DEBUG_INFO("did not add group %s", group
->name
? group
->name
: "");
353 static struct sipe_group
*sipe_group_find_by_id(struct sipe_core_private
*sipe_private
,
356 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
357 struct sipe_group
*group
;
366 if (group
->id
== id
) {
374 static struct sipe_group
*sipe_group_find_by_name(struct sipe_core_private
*sipe_private
,
377 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
378 struct sipe_group
*group
;
387 if (sipe_strequal(group
->name
, name
)) {
396 sipe_group_rename(struct sipe_core_private
*sipe_private
,
397 struct sipe_group
*group
,
400 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
402 SIPE_DEBUG_INFO("Renaming group %s to %s", group
->name
, name
);
403 body
= g_markup_printf_escaped(SIPE_SOAP_MOD_GROUP
, group
->id
, name
, sip
->contacts_delta
++);
404 send_soap_request(sipe_private
, body
);
407 group
->name
= g_strdup(name
);
411 * Only appends if no such value already stored.
414 GSList
* slist_insert_unique_sorted(GSList
*list
, gpointer data
, GCompareFunc func
) {
416 if (!g_slist_find_custom(list
, data
, func
)) {
417 res
= g_slist_insert_sorted(list
, data
, func
);
423 sipe_group_compare(struct sipe_group
*group1
, struct sipe_group
*group2
) {
424 return group1
->id
- group2
->id
;
428 * Returns string like "2 4 7 8" - group ids buddy belong to.
431 sipe_get_buddy_groups_string (struct sipe_buddy
*buddy
) {
434 //creating array from GList, converting int to gchar*
435 gchar
**ids_arr
= g_new(gchar
*, g_slist_length(buddy
->groups
) + 1);
436 GSList
*entry
= buddy
->groups
;
438 if (!ids_arr
) return NULL
;
441 struct sipe_group
* group
= entry
->data
;
442 ids_arr
[i
] = g_strdup_printf("%d", group
->id
);
447 res
= g_strjoinv(" ", ids_arr
);
453 * Sends buddy update to server
456 sipe_core_group_set_user(struct sipe_core_public
*sipe_public
, const gchar
* who
)
458 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA
;
459 struct sipe_buddy
*buddy
= g_hash_table_lookup(SIPE_CORE_PRIVATE
->buddies
, who
);
460 PurpleBuddy
*purple_buddy
= purple_find_buddy (sip
->account
, who
);
462 if (buddy
&& purple_buddy
) {
463 const char *alias
= purple_buddy_get_alias(purple_buddy
);
464 gchar
*groups
= sipe_get_buddy_groups_string(buddy
);
467 SIPE_DEBUG_INFO("Saving buddy %s with alias %s and groups %s", who
, alias
, groups
);
469 body
= g_markup_printf_escaped(SIPE_SOAP_SET_CONTACT
,
470 alias
, groups
, "true", buddy
->name
, sip
->contacts_delta
++
472 send_soap_request(SIPE_CORE_PRIVATE
, body
);
479 static gboolean
process_add_group_response(struct sipe_core_private
*sipe_private
,
481 struct transaction
*trans
)
483 if (msg
->response
== 200) {
484 struct sipe_group
*group
;
485 struct group_user_context
*ctx
= trans
->payload
->data
;
487 const sipe_xml
*node
;
489 struct sipe_buddy
*buddy
;
491 xml
= sipe_xml_parse(msg
->body
, msg
->bodylen
);
496 node
= sipe_xml_child(xml
, "Body/addGroup/groupID");
502 group_id
= sipe_xml_data(node
);
508 group
= g_new0(struct sipe_group
, 1);
509 group
->id
= (int)g_ascii_strtod(group_id
, NULL
);
511 group
->name
= g_strdup(ctx
->group_name
);
513 sipe_group_add(sipe_private
, group
);
515 if (ctx
->user_name
) {
516 buddy
= g_hash_table_lookup(sipe_private
->buddies
, ctx
->user_name
);
518 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, group
, (GCompareFunc
)sipe_group_compare
);
521 sipe_core_group_set_user(SIPE_CORE_PUBLIC
, ctx
->user_name
);
530 static void sipe_group_context_destroy(gpointer data
)
532 struct group_user_context
*ctx
= data
;
533 g_free(ctx
->group_name
);
534 g_free(ctx
->user_name
);
538 static void sipe_group_create (struct sipe_core_private
*sipe_private
,
539 const gchar
*name
, const gchar
* who
)
541 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
542 struct transaction_payload
*payload
= g_new0(struct transaction_payload
, 1);
543 struct group_user_context
*ctx
= g_new0(struct group_user_context
, 1);
544 const gchar
*soap_name
= sipe_strequal(name
, _("Other Contacts")) ? "~" : name
;
546 ctx
->group_name
= g_strdup(name
);
547 ctx
->user_name
= g_strdup(who
);
548 payload
->destroy
= sipe_group_context_destroy
;
551 body
= g_markup_printf_escaped(SIPE_SOAP_ADD_GROUP
, soap_name
, sip
->contacts_delta
++);
552 send_soap_request_with_cb(sipe_private
, NULL
, body
, process_add_group_response
, payload
);
557 sipe_sched_calendar_status_update(struct sipe_core_private
*sipe_private
,
558 time_t calculate_from
);
561 sipe_get_availability_by_status(const char* sipe_status_id
, char** activity_token
);
564 sipe_get_status_by_availability(int avail
,
568 sipe_set_purple_account_status_and_note(const PurpleAccount
*account
,
569 const char *status_id
,
571 time_t do_not_publish
[]);
574 sipe_apply_calendar_status(struct sipe_core_private
*sipe_private
,
575 struct sipe_buddy
*sbuddy
,
576 const char *status_id
)
578 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
579 time_t cal_avail_since
;
580 int cal_status
= sipe_cal_get_status(sbuddy
, time(NULL
), &cal_avail_since
);
586 if (cal_status
< SIPE_CAL_NO_DATA
) {
587 SIPE_DEBUG_INFO("sipe_apply_calendar_status: cal_status : %d for %s", cal_status
, sbuddy
->name
);
588 SIPE_DEBUG_INFO("sipe_apply_calendar_status: cal_avail_since : %s", asctime(localtime(&cal_avail_since
)));
591 /* scheduled Cal update call */
593 status_id
= sbuddy
->last_non_cal_status_id
;
594 g_free(sbuddy
->activity
);
595 sbuddy
->activity
= g_strdup(sbuddy
->last_non_cal_activity
);
599 SIPE_DEBUG_INFO("sipe_apply_calendar_status: status_id is NULL for %s, exiting.",
600 sbuddy
->name
? sbuddy
->name
: "" );
604 /* adjust to calendar status */
605 if (cal_status
!= SIPE_CAL_NO_DATA
) {
606 SIPE_DEBUG_INFO("sipe_apply_calendar_status: user_avail_since: %s", asctime(localtime(&sbuddy
->user_avail_since
)));
608 if (cal_status
== SIPE_CAL_BUSY
609 && cal_avail_since
> sbuddy
->user_avail_since
610 && 6500 >= sipe_get_availability_by_status(status_id
, NULL
))
612 status_id
= SIPE_STATUS_ID_BUSY
;
613 g_free(sbuddy
->activity
);
614 sbuddy
->activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_IN_MEETING
));
616 avail
= sipe_get_availability_by_status(status_id
, NULL
);
618 SIPE_DEBUG_INFO("sipe_apply_calendar_status: activity_since : %s", asctime(localtime(&sbuddy
->activity_since
)));
619 if (cal_avail_since
> sbuddy
->activity_since
) {
620 if (cal_status
== SIPE_CAL_OOF
621 && avail
>= 15000) /* 12000 in 2007 */
623 g_free(sbuddy
->activity
);
624 sbuddy
->activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_OOF
));
629 /* then set status_id actually */
630 SIPE_DEBUG_INFO("sipe_apply_calendar_status: to %s for %s", status_id
, sbuddy
->name
? sbuddy
->name
: "" );
631 purple_prpl_got_user_status(sip
->account
, sbuddy
->name
, status_id
, NULL
);
633 /* set our account state to the one in roaming (including calendar info) */
634 self_uri
= sip_uri_self(sipe_private
);
635 if (sip
->initial_state_published
&& sipe_strcase_equal(sbuddy
->name
, self_uri
)) {
636 if (sipe_strequal(status_id
, SIPE_STATUS_ID_OFFLINE
)) {
637 status_id
= g_strdup(SIPE_STATUS_ID_INVISIBLE
); /* not not let offline status switch us off */
640 SIPE_DEBUG_INFO("sipe_apply_calendar_status: switch to '%s' for the account", sip
->status
);
641 sipe_set_purple_account_status_and_note(sip
->account
, status_id
, sip
->note
, sip
->do_not_publish
);
647 sipe_got_user_status(struct sipe_core_private
*sipe_private
,
649 const char *status_id
)
651 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
652 struct sipe_buddy
*sbuddy
= g_hash_table_lookup(sipe_private
->buddies
, uri
);
656 /* Check if on 2005 system contact's calendar,
657 * then set/preserve it.
659 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
660 sipe_apply_calendar_status(sipe_private
, sbuddy
, status_id
);
662 purple_prpl_got_user_status(sip
->account
, uri
, status_id
, NULL
);
667 update_calendar_status_cb(SIPE_UNUSED_PARAMETER
char *name
,
668 struct sipe_buddy
*sbuddy
,
669 struct sipe_core_private
*sipe_private
)
671 sipe_apply_calendar_status(sipe_private
, sbuddy
, NULL
);
675 * Updates contact's status
676 * based on their calendar information.
678 * Applicability: 2005 systems
681 update_calendar_status(struct sipe_core_private
*sipe_private
,
682 SIPE_UNUSED_PARAMETER
void *unused
)
684 SIPE_DEBUG_INFO_NOFORMAT("update_calendar_status() started.");
685 g_hash_table_foreach(sipe_private
->buddies
, (GHFunc
)update_calendar_status_cb
, sipe_private
);
687 /* repeat scheduling */
688 sipe_sched_calendar_status_update(sipe_private
, time(NULL
) + 3*60 /* 3 min */);
692 * Schedules process of contacts' status update
693 * based on their calendar information.
694 * Should be scheduled to the beginning of every
695 * 15 min interval, like:
696 * 13:00, 13:15, 13:30, 13:45, etc.
698 * Applicability: 2005 systems
701 sipe_sched_calendar_status_update(struct sipe_core_private
*sipe_private
,
702 time_t calculate_from
)
704 int interval
= 15*60;
705 /** start of the beginning of closest 15 min interval. */
706 time_t next_start
= ((time_t)((int)((int)calculate_from
)/interval
+ 1)*interval
);
708 SIPE_DEBUG_INFO("sipe_sched_calendar_status_update: calculate_from time: %s",
709 asctime(localtime(&calculate_from
)));
710 SIPE_DEBUG_INFO("sipe_sched_calendar_status_update: next start time : %s",
711 asctime(localtime(&next_start
)));
713 sipe_schedule_seconds(sipe_private
,
714 "<+2005-cal-status>",
716 next_start
- time(NULL
),
717 update_calendar_status
,
722 * Schedules process of self status publish
723 * based on own calendar information.
724 * Should be scheduled to the beginning of every
725 * 15 min interval, like:
726 * 13:00, 13:15, 13:30, 13:45, etc.
728 * Applicability: 2007+ systems
731 sipe_sched_calendar_status_self_publish(struct sipe_core_private
*sipe_private
,
732 time_t calculate_from
)
735 /** start of the beginning of closest 5 min interval. */
736 time_t next_start
= ((time_t)((int)((int)calculate_from
)/interval
+ 1)*interval
);
738 SIPE_DEBUG_INFO("sipe_sched_calendar_status_self_publish: calculate_from time: %s",
739 asctime(localtime(&calculate_from
)));
740 SIPE_DEBUG_INFO("sipe_sched_calendar_status_self_publish: next start time : %s",
741 asctime(localtime(&next_start
)));
743 sipe_schedule_seconds(sipe_private
,
744 "<+2007-cal-status>",
746 next_start
- time(NULL
),
747 publish_calendar_status_self
,
751 static void sipe_subscribe_resource_uri(const char *name
,
752 SIPE_UNUSED_PARAMETER gpointer value
,
753 gchar
**resources_uri
)
755 gchar
*tmp
= *resources_uri
;
756 *resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"/>\n", tmp
, name
);
760 static void sipe_subscribe_resource_uri_with_context(const char *name
, gpointer value
, gchar
**resources_uri
)
762 struct sipe_buddy
*sbuddy
= (struct sipe_buddy
*)value
;
763 gchar
*context
= sbuddy
&& sbuddy
->just_added
? "><context/></resource>" : "/>";
764 gchar
*tmp
= *resources_uri
;
766 if (sbuddy
) sbuddy
->just_added
= FALSE
; /* should be enought to include context one time */
768 *resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"%s\n", tmp
, name
, context
);
773 * Support for Batch Category SUBSCRIBE [MS-PRES] - msrtc-event-categories+xml OCS 2007
774 * Support for Batch Category SUBSCRIBE [MS-SIP] - adrl+xml LCS 2005
775 * The user sends an initial batched category SUBSCRIBE request against all contacts on his roaming list in only a request
776 * A batch category SUBSCRIBE request MUST have the same To-URI and From-URI.
777 * This header will be send only if adhoclist there is a "Supported: adhoclist" in REGISTER answer else will be send a Single Category SUBSCRIBE
780 static void sipe_subscribe_presence_batched_to(struct sipe_core_private
*sipe_private
,
781 gchar
*resources_uri
,
784 gchar
*contact
= get_contact(sipe_private
);
789 gchar
*autoextend
= "";
792 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
793 require
= ", categoryList";
794 accept
= ", application/msrtc-event-categories+xml, application/xpidf+xml, application/pidf+xml";
795 content_type
= "application/msrtc-adrl-categorylist+xml";
796 content
= g_strdup_printf(
797 "<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
798 "<action name=\"subscribe\" id=\"63792024\">\n"
799 "<adhocList>\n%s</adhocList>\n"
800 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
801 "<category name=\"calendarData\"/>\n"
802 "<category name=\"contactCard\"/>\n"
803 "<category name=\"note\"/>\n"
804 "<category name=\"state\"/>\n"
807 "</batchSub>", sipe_private
->username
, resources_uri
);
809 autoextend
= "Supported: com.microsoft.autoextend\r\n";
810 content_type
= "application/adrl+xml";
811 content
= g_strdup_printf(
812 "<adhoclist xmlns=\"urn:ietf:params:xml:ns:adrl\" uri=\"sip:%s\" name=\"sip:%s\">\n"
813 "<create xmlns=\"\">\n%s</create>\n"
814 "</adhoclist>\n", sipe_private
->username
, sipe_private
->username
, resources_uri
);
816 g_free(resources_uri
);
818 request
= g_strdup_printf(
819 "Require: adhoclist%s\r\n"
820 "Supported: eventlist\r\n"
821 "Accept: application/rlmi+xml, multipart/related, text/xml+msrtc.pidf%s\r\n"
822 "Supported: ms-piggyback-first-notify\r\n"
823 "%sSupported: ms-benotify\r\n"
824 "Proxy-Require: ms-benotify\r\n"
825 "Event: presence\r\n"
826 "Content-Type: %s\r\n"
827 "Contact: %s\r\n", require
, accept
, autoextend
, content_type
, contact
);
830 sipe_subscribe_presence_buddy(sipe_private
, to
, request
, content
);
837 static void sipe_subscribe_presence_batched(struct sipe_core_private
*sipe_private
,
838 SIPE_UNUSED_PARAMETER
void *unused
)
840 gchar
*to
= sip_uri_self(sipe_private
);
841 gchar
*resources_uri
= g_strdup("");
842 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
843 g_hash_table_foreach(sipe_private
->buddies
, (GHFunc
) sipe_subscribe_resource_uri_with_context
, &resources_uri
);
845 g_hash_table_foreach(sipe_private
->buddies
, (GHFunc
) sipe_subscribe_resource_uri
, &resources_uri
);
848 sipe_subscribe_presence_batched_to(sipe_private
, resources_uri
, to
);
851 struct presence_batched_routed
{
856 static void sipe_subscribe_presence_batched_routed_free(void *payload
)
858 struct presence_batched_routed
*data
= payload
;
859 GSList
*buddies
= data
->buddies
;
861 g_free(buddies
->data
);
862 buddies
= buddies
->next
;
864 g_slist_free(data
->buddies
);
869 static void sipe_subscribe_presence_batched_routed(struct sipe_core_private
*sipe_private
,
872 struct presence_batched_routed
*data
= payload
;
873 GSList
*buddies
= data
->buddies
;
874 gchar
*resources_uri
= g_strdup("");
876 gchar
*tmp
= resources_uri
;
877 resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"/>\n", tmp
, (char *) buddies
->data
);
879 buddies
= buddies
->next
;
881 sipe_subscribe_presence_batched_to(sipe_private
, resources_uri
,
882 g_strdup(data
->host
));
886 * Single Category SUBSCRIBE [MS-PRES] ; To send when the server returns a 200 OK message with state="resubscribe" in response.
887 * The user sends a single SUBSCRIBE request to the subscribed contact.
888 * The To-URI and the URI listed in the resource list MUST be the same for a single category SUBSCRIBE request.
892 static void sipe_subscribe_presence_single(struct sipe_core_private
*sipe_private
,
895 gchar
*to
= sip_uri((char *)buddy_name
);
896 gchar
*tmp
= get_contact(sipe_private
);
898 gchar
*content
= NULL
;
899 gchar
*autoextend
= "";
900 gchar
*content_type
= "";
901 struct sipe_buddy
*sbuddy
= g_hash_table_lookup(sipe_private
->buddies
, to
);
902 gchar
*context
= sbuddy
&& sbuddy
->just_added
? "><context/></resource>" : "/>";
904 if (sbuddy
) sbuddy
->just_added
= FALSE
;
906 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
907 content_type
= "Content-Type: application/msrtc-adrl-categorylist+xml\r\n";
909 autoextend
= "Supported: com.microsoft.autoextend\r\n";
912 request
= g_strdup_printf(
913 "Accept: application/msrtc-event-categories+xml, text/xml+msrtc.pidf, application/xpidf+xml, application/pidf+xml, application/rlmi+xml, multipart/related\r\n"
914 "Supported: ms-piggyback-first-notify\r\n"
915 "%s%sSupported: ms-benotify\r\n"
916 "Proxy-Require: ms-benotify\r\n"
917 "Event: presence\r\n"
918 "Contact: %s\r\n", autoextend
, content_type
, tmp
);
920 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
921 content
= g_strdup_printf(
922 "<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
923 "<action name=\"subscribe\" id=\"63792024\"><adhocList>\n"
924 "<resource uri=\"%s\"%s\n"
926 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
927 "<category name=\"calendarData\"/>\n"
928 "<category name=\"contactCard\"/>\n"
929 "<category name=\"note\"/>\n"
930 "<category name=\"state\"/>\n"
933 "</batchSub>", sipe_private
->username
, to
, context
);
938 sipe_subscribe_presence_buddy(sipe_private
, to
, request
, content
);
945 void sipe_set_status(PurpleAccount
*account
, PurpleStatus
*status
)
947 SIPE_DEBUG_INFO("sipe_set_status: status=%s", purple_status_get_id(status
));
949 if (!purple_status_is_active(status
))
953 struct sipe_core_private
*sipe_private
= PURPLE_ACCOUNT_TO_SIPE_CORE_PRIVATE
;
954 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
959 time_t now
= time(NULL
);
960 const char *status_id
= purple_status_get_id(status
);
961 const char *note
= purple_status_get_attr_string(status
, SIPE_STATUS_ATTR_ID_MESSAGE
);
962 sipe_activity activity
= sipe_get_activity_by_token(status_id
);
963 gboolean do_not_publish
= ((now
- sip
->do_not_publish
[activity
]) <= 2);
965 /* when other point of presence clears note, but we are keeping
968 if (do_not_publish
&& !note
&& sip
->cal
&& sip
->cal
->oof_note
) {
969 SIPE_DEBUG_INFO_NOFORMAT("sipe_set_status: enabling publication as OOF note keepers.");
970 do_not_publish
= FALSE
;
973 SIPE_DEBUG_INFO("sipe_set_status: was: sip->do_not_publish[%s]=%d [?] now(time)=%d",
974 status_id
, (int)sip
->do_not_publish
[activity
], (int)now
);
976 sip
->do_not_publish
[activity
] = 0;
977 SIPE_DEBUG_INFO("sipe_set_status: set: sip->do_not_publish[%s]=%d [0]",
978 status_id
, (int)sip
->do_not_publish
[activity
]);
982 SIPE_DEBUG_INFO_NOFORMAT("sipe_set_status: publication was switched off, exiting.");
987 sip
->status
= g_strdup(status_id
);
989 /* hack to escape apostrof before comparison */
990 tmp
= note
? sipe_utils_str_replace(note
, "'", "'") : NULL
;
992 /* this will preserve OOF flag as well */
993 if (!sipe_strequal(tmp
, sip
->note
)) {
994 sip
->is_oof_note
= FALSE
;
996 sip
->note
= g_strdup(note
);
997 sip
->note_since
= time(NULL
);
1001 /* schedule 2 sec to capture idle flag */
1002 action_name
= g_strdup_printf("<%s>", "+set-status");
1003 sipe_schedule_seconds(sipe_private
,
1006 SIPE_IDLE_SET_DELAY
,
1007 send_presence_status
,
1009 g_free(action_name
);
1015 sipe_set_idle(PurpleConnection
* gc
,
1018 SIPE_DEBUG_INFO("sipe_set_idle: interval=%d", interval
);
1021 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
1022 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
1025 sip
->idle_switch
= time(NULL
);
1026 SIPE_DEBUG_INFO("sipe_set_idle: sip->idle_switch : %s", asctime(localtime(&(sip
->idle_switch
))));
1032 sipe_group_buddy(PurpleConnection
*gc
,
1034 const char *old_group_name
,
1035 const char *new_group_name
)
1037 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
1038 struct sipe_buddy
* buddy
= g_hash_table_lookup(sipe_private
->buddies
, who
);
1039 struct sipe_group
* old_group
= NULL
;
1040 struct sipe_group
* new_group
;
1042 SIPE_DEBUG_INFO("sipe_group_buddy[CB]: who:%s old_group_name:%s new_group_name:%s",
1043 who
? who
: "", old_group_name
? old_group_name
: "", new_group_name
? new_group_name
: "");
1045 if(!buddy
) { // buddy not in roaming list
1049 if (old_group_name
) {
1050 old_group
= sipe_group_find_by_name(sipe_private
, old_group_name
);
1052 new_group
= sipe_group_find_by_name(sipe_private
, new_group_name
);
1055 buddy
->groups
= g_slist_remove(buddy
->groups
, old_group
);
1056 SIPE_DEBUG_INFO("buddy %s removed from old group %s", who
, old_group_name
);
1060 sipe_group_create(sipe_private
, new_group_name
, who
);
1062 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, new_group
, (GCompareFunc
)sipe_group_compare
);
1063 sipe_core_group_set_user(SIPE_CORE_PUBLIC
, who
);
1067 void sipe_add_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
1069 SIPE_DEBUG_INFO("sipe_add_buddy[CB]: buddy:%s group:%s", buddy
? buddy
->name
: "", group
? group
->name
: "");
1071 /* libpurple can call us with undefined buddy or group */
1072 if (buddy
&& group
) {
1073 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
1075 /* Buddy name must be lower case as we use purple_normalize_nocase() to compare */
1076 gchar
*buddy_name
= g_ascii_strdown(buddy
->name
, -1);
1077 purple_blist_rename_buddy(buddy
, buddy_name
);
1080 /* Prepend sip: if needed */
1081 if (!g_str_has_prefix(buddy
->name
, "sip:")) {
1082 gchar
*buf
= sip_uri_from_name(buddy
->name
);
1083 purple_blist_rename_buddy(buddy
, buf
);
1087 if (!g_hash_table_lookup(sipe_private
->buddies
, buddy
->name
)) {
1088 struct sipe_buddy
*b
= g_new0(struct sipe_buddy
, 1);
1089 SIPE_DEBUG_INFO("sipe_add_buddy: adding %s", buddy
->name
);
1090 b
->name
= g_strdup(buddy
->name
);
1091 b
->just_added
= TRUE
;
1092 g_hash_table_insert(sipe_private
->buddies
, b
->name
, b
);
1093 sipe_group_buddy(gc
, b
->name
, NULL
, group
->name
);
1094 /* @TODO should go to callback */
1095 sipe_subscribe_presence_single(sipe_private
,
1098 SIPE_DEBUG_INFO("sipe_add_buddy: buddy %s already in internal list", buddy
->name
);
1103 static void sipe_free_buddy(struct sipe_buddy
*buddy
)
1107 * We are calling g_hash_table_foreach_steal(). That means that no
1108 * key/value deallocation functions are called. Therefore the glib
1109 * hash code does not touch the key (buddy->name) or value (buddy)
1110 * of the to-be-deleted hash node at all. It follows that we
1112 * - MUST free the memory for the key ourselves and
1113 * - ARE allowed to do it in this function
1115 * Conclusion: glib must be broken on the Windows platform if sipe
1116 * crashes with SIGTRAP when closing. You'll have to live
1117 * with the memory leak until this is fixed.
1119 g_free(buddy
->name
);
1121 g_free(buddy
->activity
);
1122 g_free(buddy
->meeting_subject
);
1123 g_free(buddy
->meeting_location
);
1124 g_free(buddy
->note
);
1126 g_free(buddy
->cal_start_time
);
1127 g_free(buddy
->cal_free_busy_base64
);
1128 g_free(buddy
->cal_free_busy
);
1129 g_free(buddy
->last_non_cal_activity
);
1131 sipe_cal_free_working_hours(buddy
->cal_working_hours
);
1133 g_free(buddy
->device_name
);
1134 g_slist_free(buddy
->groups
);
1139 * Unassociates buddy from group first.
1140 * Then see if no groups left, removes buddy completely.
1141 * Otherwise updates buddy groups on server.
1143 void sipe_remove_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
1145 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
1146 struct sipe_buddy
*b
;
1147 struct sipe_group
*g
= NULL
;
1149 SIPE_DEBUG_INFO("sipe_remove_buddy[CB]: buddy:%s group:%s", buddy
? buddy
->name
: "", group
? group
->name
: "");
1152 b
= g_hash_table_lookup(sipe_private
->buddies
, buddy
->name
);
1156 g
= sipe_group_find_by_name(sipe_private
, group
->name
);
1160 b
->groups
= g_slist_remove(b
->groups
, g
);
1161 SIPE_DEBUG_INFO("buddy %s removed from group %s", buddy
->name
, g
->name
);
1164 if (g_slist_length(b
->groups
) < 1) {
1165 gchar
*action_name
= sipe_utils_presence_key(buddy
->name
);
1166 sipe_schedule_cancel(sipe_private
, action_name
);
1167 g_free(action_name
);
1169 g_hash_table_remove(sipe_private
->buddies
, buddy
->name
);
1172 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
1173 gchar
* body
= g_strdup_printf(SIPE_SOAP_DEL_CONTACT
, b
->name
, sip
->contacts_delta
++);
1174 send_soap_request(sipe_private
, body
);
1180 //updates groups on server
1181 sipe_core_group_set_user(SIPE_CORE_PUBLIC
, b
->name
);
1187 sipe_rename_group(PurpleConnection
*gc
,
1188 const char *old_name
,
1190 SIPE_UNUSED_PARAMETER GList
*moved_buddies
)
1192 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
1193 struct sipe_group
* s_group
= sipe_group_find_by_name(sipe_private
, old_name
);
1195 sipe_group_rename(sipe_private
, s_group
, group
->name
);
1197 SIPE_DEBUG_INFO("Cannot find group %s to rename", old_name
);
1202 sipe_remove_group(PurpleConnection
*gc
, PurpleGroup
*group
)
1204 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
1205 struct sipe_group
* s_group
= sipe_group_find_by_name(sipe_private
, group
->name
);
1207 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
1209 SIPE_DEBUG_INFO("Deleting group %s", group
->name
);
1210 body
= g_strdup_printf(SIPE_SOAP_DEL_GROUP
, s_group
->id
, sip
->contacts_delta
++);
1211 send_soap_request(sipe_private
, body
);
1214 sip
->groups
= g_slist_remove(sip
->groups
, s_group
);
1215 g_free(s_group
->name
);
1218 SIPE_DEBUG_INFO("Cannot find group %s to delete", group
->name
);
1223 * A callback for g_hash_table_foreach
1226 sipe_buddy_subscribe_cb(char *buddy_name
,
1227 SIPE_UNUSED_PARAMETER
struct sipe_buddy
*buddy
,
1228 struct sipe_core_private
*sipe_private
)
1230 gchar
*action_name
= sipe_utils_presence_key(buddy_name
);
1231 /* g_hash_table_size() can never return 0, otherwise this function wouldn't be called :-) */
1232 guint time_range
= (g_hash_table_size(sipe_private
->buddies
) * 1000) / 25; /* time interval for 25 requests per sec. In msec. */
1233 guint timeout
= ((guint
) rand()) / (RAND_MAX
/ time_range
) + 1; /* random period within the range but never 0! */
1235 sipe_schedule_mseconds(sipe_private
,
1237 g_strdup(buddy_name
),
1239 sipe_subscribe_presence_single
,
1241 g_free(action_name
);
1245 * Removes entries from purple buddy list
1246 * that does not correspond ones in the roaming contact list.
1248 static void sipe_cleanup_local_blist(struct sipe_core_private
*sipe_private
) {
1249 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
1250 GSList
*buddies
= purple_find_buddies(sip
->account
, NULL
);
1251 GSList
*entry
= buddies
;
1252 struct sipe_buddy
*buddy
;
1256 SIPE_DEBUG_INFO("sipe_cleanup_local_blist: overall %d Purple buddies (including clones)", g_slist_length(buddies
));
1257 SIPE_DEBUG_INFO("sipe_cleanup_local_blist: %d sipe buddies (unique)", g_hash_table_size(sipe_private
->buddies
));
1260 g
= purple_buddy_get_group(b
);
1261 buddy
= g_hash_table_lookup(sipe_private
->buddies
, b
->name
);
1263 gboolean in_sipe_groups
= FALSE
;
1264 GSList
*entry2
= buddy
->groups
;
1266 struct sipe_group
*group
= entry2
->data
;
1267 if (sipe_strequal(group
->name
, g
->name
)) {
1268 in_sipe_groups
= TRUE
;
1271 entry2
= entry2
->next
;
1273 if(!in_sipe_groups
) {
1274 SIPE_DEBUG_INFO("*** REMOVING %s from Purple group: %s as not having this group in roaming list", b
->name
, g
->name
);
1275 purple_blist_remove_buddy(b
);
1278 SIPE_DEBUG_INFO("*** REMOVING %s from Purple group: %s as this buddy not in roaming list", b
->name
, g
->name
);
1279 purple_blist_remove_buddy(b
);
1281 entry
= entry
->next
;
1283 g_slist_free(buddies
);
1287 sipe_find_access_level(struct sipe_core_private
*sipe_private
,
1290 gboolean
*is_group_access
);
1293 sipe_refresh_blocked_status_cb(char *buddy_name
,
1294 SIPE_UNUSED_PARAMETER
struct sipe_buddy
*buddy
,
1295 struct sipe_core_private
*sipe_private
)
1297 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
1298 int container_id
= sipe_find_access_level(sipe_private
, "user", buddy_name
, NULL
);
1299 gboolean blocked
= (container_id
== 32000);
1300 gboolean blocked_in_blist
= !purple_privacy_check(sip
->account
, buddy_name
);
1302 /* SIPE_DEBUG_INFO("sipe_refresh_blocked_status_cb: buddy_name=%s, blocked=%s, blocked_in_blist=%s",
1303 buddy_name, blocked ? "T" : "F", blocked_in_blist ? "T" : "F"); */
1305 if (blocked
!= blocked_in_blist
) {
1307 purple_privacy_deny_add(sip
->account
, buddy_name
, TRUE
);
1309 purple_privacy_deny_remove(sip
->account
, buddy_name
, TRUE
);
1312 /* stupid workaround to make pidgin re-render screen to reflect our changes */
1314 PurpleBuddy
*pbuddy
= purple_find_buddy(sip
->account
, buddy_name
);
1315 const PurplePresence
*presence
= purple_buddy_get_presence(pbuddy
);
1316 const PurpleStatus
*pstatus
= purple_presence_get_active_status(presence
);
1318 SIPE_DEBUG_INFO_NOFORMAT("sipe_refresh_blocked_status_cb: forcefully refreshing screen.");
1319 sipe_got_user_status(sipe_private
, buddy_name
, purple_status_get_id(pstatus
));
1326 sipe_refresh_blocked_status(struct sipe_core_private
*sipe_private
)
1328 g_hash_table_foreach(sipe_private
->buddies
,
1329 (GHFunc
) sipe_refresh_blocked_status_cb
,
1333 static gboolean
sipe_process_roaming_contacts(struct sipe_core_private
*sipe_private
,
1336 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
1337 int len
= msg
->bodylen
;
1339 const gchar
*tmp
= sipmsg_find_header(msg
, "Event");
1340 const sipe_xml
*item
;
1342 const gchar
*contacts_delta
;
1343 const sipe_xml
*group_node
;
1344 if (!g_str_has_prefix(tmp
, "vnd-microsoft-roaming-contacts")) {
1348 /* Convert the contact from XML to Purple Buddies */
1349 isc
= sipe_xml_parse(msg
->body
, len
);
1354 contacts_delta
= sipe_xml_attribute(isc
, "deltaNum");
1355 if (contacts_delta
) {
1356 sip
->contacts_delta
= (int)g_ascii_strtod(contacts_delta
, NULL
);
1359 if (sipe_strequal(sipe_xml_name(isc
), "contactList")) {
1362 for (group_node
= sipe_xml_child(isc
, "group"); group_node
; group_node
= sipe_xml_twin(group_node
)) {
1363 struct sipe_group
* group
= g_new0(struct sipe_group
, 1);
1364 const char *name
= sipe_xml_attribute(group_node
, "name");
1366 if (g_str_has_prefix(name
, "~")) {
1367 name
= _("Other Contacts");
1369 group
->name
= g_strdup(name
);
1370 group
->id
= (int)g_ascii_strtod(sipe_xml_attribute(group_node
, "id"), NULL
);
1372 sipe_group_add(sipe_private
, group
);
1375 // Make sure we have at least one group
1376 if (g_slist_length(sip
->groups
) == 0) {
1377 sipe_group_create(sipe_private
, _("Other Contacts"), NULL
);
1380 /* Parse contacts */
1381 for (item
= sipe_xml_child(isc
, "contact"); item
; item
= sipe_xml_twin(item
)) {
1382 const gchar
*uri
= sipe_xml_attribute(item
, "uri");
1383 const gchar
*name
= sipe_xml_attribute(item
, "name");
1385 struct sipe_buddy
*buddy
= NULL
;
1387 gchar
**item_groups
;
1390 /* Buddy name must be lower case as we use purple_normalize_nocase() to compare */
1391 tmp
= sip_uri_from_name(uri
);
1392 buddy_name
= g_ascii_strdown(tmp
, -1);
1395 /* assign to group Other Contacts if nothing else received */
1396 tmp
= g_strdup(sipe_xml_attribute(item
, "groups"));
1398 struct sipe_group
*group
= sipe_group_find_by_name(sipe_private
, _("Other Contacts"));
1400 tmp
= group
? g_strdup_printf("%d", group
->id
) : g_strdup("1");
1402 item_groups
= g_strsplit(tmp
, " ", 0);
1405 while (item_groups
[i
]) {
1406 struct sipe_group
*group
= sipe_group_find_by_id(sipe_private
, g_ascii_strtod(item_groups
[i
], NULL
));
1408 // If couldn't find the right group for this contact, just put them in the first group we have
1409 if (group
== NULL
&& g_slist_length(sip
->groups
) > 0) {
1410 group
= sip
->groups
->data
;
1413 if (group
!= NULL
) {
1414 PurpleBuddy
*b
= purple_find_buddy_in_group(sip
->account
, buddy_name
, group
->purple_group
);
1416 b
= purple_buddy_new(sip
->account
, buddy_name
, uri
);
1417 purple_blist_add_buddy(b
, NULL
, group
->purple_group
, NULL
);
1419 SIPE_DEBUG_INFO("Created new buddy %s with alias %s", buddy_name
, uri
);
1422 if (sipe_strcase_equal(uri
, purple_buddy_get_alias(b
))) {
1423 if (name
!= NULL
&& strlen(name
) != 0) {
1424 purple_blist_alias_buddy(b
, name
);
1426 SIPE_DEBUG_INFO("Replaced buddy %s alias with %s", buddy_name
, name
);
1431 buddy
= g_new0(struct sipe_buddy
, 1);
1432 buddy
->name
= g_strdup(b
->name
);
1433 g_hash_table_insert(sipe_private
->buddies
, buddy
->name
, buddy
);
1435 SIPE_DEBUG_INFO("Added SIPE buddy %s", buddy
->name
);
1438 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, group
, (GCompareFunc
)sipe_group_compare
);
1440 SIPE_DEBUG_INFO("Added buddy %s to group %s", b
->name
, group
->name
);
1442 SIPE_DEBUG_INFO("No group found for contact %s! Unable to add to buddy list",
1447 } // while, contact groups
1448 g_strfreev(item_groups
);
1453 sipe_cleanup_local_blist(sipe_private
);
1455 /* Add self-contact if not there yet. 2005 systems. */
1456 /* This will resemble subscription to roaming_self in 2007 systems */
1457 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
1458 gchar
*self_uri
= sip_uri_self(sipe_private
);
1459 struct sipe_buddy
*buddy
= g_hash_table_lookup(sipe_private
->buddies
, self_uri
);
1462 buddy
= g_new0(struct sipe_buddy
, 1);
1463 buddy
->name
= g_strdup(self_uri
);
1464 g_hash_table_insert(sipe_private
->buddies
, buddy
->name
, buddy
);
1471 /* subscribe to buddies */
1472 if (!sip
->subscribed_buddies
) { //do it once, then count Expire field to schedule resubscribe.
1473 if (sip
->batched_support
) {
1474 sipe_subscribe_presence_batched(sipe_private
, NULL
);
1476 g_hash_table_foreach(sipe_private
->buddies
,
1477 (GHFunc
)sipe_buddy_subscribe_cb
,
1480 sip
->subscribed_buddies
= TRUE
;
1482 /* for 2005 systems schedule contacts' status update
1483 * based on their calendar information
1485 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
1486 sipe_sched_calendar_status_update(sipe_private
, time(NULL
));
1493 * Fires on deregistration event initiated by server.
1494 * [MS-SIPREGE] SIP extension.
1499 // Content-Type: text/registration-event
1500 // subscription-state: terminated;expires=0
1501 // ms-diagnostics-public: 4141;reason="User disabled"
1503 // deregistered;event=rejected
1505 static void sipe_process_registration_notify(struct sipe_core_private
*sipe_private
,
1508 const gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
1509 gchar
*event
= NULL
;
1510 gchar
*reason
= NULL
;
1511 const gchar
*diagnostics
= sipmsg_find_header(msg
, "ms-diagnostics");
1514 diagnostics
= diagnostics
? diagnostics
: sipmsg_find_header(msg
, "ms-diagnostics-public");
1515 SIPE_DEBUG_INFO_NOFORMAT("sipe_process_registration_notify: deregistration received.");
1517 if (!g_ascii_strncasecmp(contenttype
, "text/registration-event", 23)) {
1518 event
= sipmsg_find_part_of_header(msg
->body
, "event=", NULL
, NULL
);
1519 //@TODO have proper parameter extraction _by_name_ func, case insesitive.
1520 event
= event
? event
: sipmsg_find_part_of_header(msg
->body
, "event=", ";", NULL
);
1522 SIPE_DEBUG_INFO_NOFORMAT("sipe_process_registration_notify: unknown content type, exiting.");
1526 if (diagnostics
!= NULL
) {
1527 reason
= sipmsg_find_part_of_header(diagnostics
, "reason=\"", "\"", NULL
);
1528 } else { // for LCS2005
1530 if (event
&& sipe_strcase_equal(event
, "unregistered")) {
1531 error_id
= 4140; // [MS-SIPREGE]
1532 //reason = g_strdup(_("User logged out")); // [MS-OCER]
1533 reason
= g_strdup(_("you are already signed in at another location"));
1534 } else if (event
&& sipe_strcase_equal(event
, "rejected")) {
1536 reason
= g_strdup(_("user disabled")); // [MS-OCER]
1537 } else if (event
&& sipe_strcase_equal(event
, "deactivated")) {
1539 reason
= g_strdup(_("user moved")); // [MS-OCER]
1543 warning
= g_strdup_printf(_("You have been rejected by the server: %s"), reason
? reason
: _("no reason given"));
1546 sipe_backend_connection_error(SIPE_CORE_PUBLIC
,
1547 SIPE_CONNECTION_ERROR_INVALID_USERNAME
,
1553 static void sipe_process_provisioning_v2(struct sipe_core_private
*sipe_private
,
1556 sipe_xml
*xn_provision_group_list
;
1557 const sipe_xml
*node
;
1559 xn_provision_group_list
= sipe_xml_parse(msg
->body
, msg
->bodylen
);
1561 /* provisionGroup */
1562 for (node
= sipe_xml_child(xn_provision_group_list
, "provisionGroup"); node
; node
= sipe_xml_twin(node
)) {
1563 if (sipe_strequal("ServerConfiguration", sipe_xml_attribute(node
, "name"))) {
1564 g_free(sipe_private
->focus_factory_uri
);
1565 sipe_private
->focus_factory_uri
= sipe_xml_data(sipe_xml_child(node
, "focusFactoryUri"));
1566 SIPE_DEBUG_INFO("sipe_process_provisioning_v2: sipe_private->focus_factory_uri=%s",
1567 sipe_private
->focus_factory_uri
? sipe_private
->focus_factory_uri
: "");
1571 sipe_xml_free(xn_provision_group_list
);
1574 /** for 2005 system */
1576 sipe_process_provisioning(struct sipe_core_private
*sipe_private
,
1579 sipe_xml
*xn_provision
;
1580 const sipe_xml
*node
;
1582 xn_provision
= sipe_xml_parse(msg
->body
, msg
->bodylen
);
1583 if ((node
= sipe_xml_child(xn_provision
, "user"))) {
1584 SIPE_DEBUG_INFO("sipe_process_provisioning: uri=%s", sipe_xml_attribute(node
, "uri"));
1585 if ((node
= sipe_xml_child(node
, "line"))) {
1586 const gchar
*line_uri
= sipe_xml_attribute(node
, "uri");
1587 const gchar
*server
= sipe_xml_attribute(node
, "server");
1588 SIPE_DEBUG_INFO("sipe_process_provisioning: line_uri=%s server=%s", line_uri
, server
);
1589 sip_csta_open(sipe_private
, line_uri
, server
);
1592 sipe_xml_free(xn_provision
);
1595 static void sipe_process_roaming_acl(struct sipe_core_private
*sipe_private
,
1598 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
1599 const gchar
*contacts_delta
;
1602 xml
= sipe_xml_parse(msg
->body
, msg
->bodylen
);
1608 contacts_delta
= sipe_xml_attribute(xml
, "deltaNum");
1611 sip
->acl_delta
= (int)g_ascii_strtod(contacts_delta
, NULL
);
1617 /** MS-PRES container */
1618 struct sipe_container
{
1623 /** MS-PRES container member */
1624 struct sipe_container_member
{
1625 /** user, domain, sameEnterprise, federated, publicCloud; everyone */
1631 free_container_member(struct sipe_container_member
*member
)
1633 if (!member
) return;
1635 g_free(member
->type
);
1636 g_free(member
->value
);
1641 free_container(struct sipe_container
*container
)
1645 if (!container
) return;
1647 entry
= container
->members
;
1649 void *data
= entry
->data
;
1650 entry
= g_slist_remove(entry
, data
);
1651 free_container_member((struct sipe_container_member
*)data
);
1657 sipe_send_container_members_prepare(const guint container_id
,
1658 const guint container_version
,
1659 const gchar
*action
,
1662 char **container_xmls
)
1664 gchar
*value_str
= value
? g_strdup_printf(" value=\"%s\"", value
) : g_strdup("");
1667 if (!container_xmls
) return;
1669 body
= g_strdup_printf(
1670 "<container id=\"%d\" version=\"%d\"><member action=\"%s\" type=\"%s\"%s/></container>",
1678 if ((*container_xmls
) == NULL
) {
1679 *container_xmls
= body
;
1681 char *tmp
= *container_xmls
;
1683 *container_xmls
= g_strconcat(*container_xmls
, body
, NULL
);
1690 sipe_send_set_container_members(struct sipe_core_private
*sipe_private
,
1691 char *container_xmls
)
1698 if (!container_xmls
) return;
1700 self
= sip_uri_self(sipe_private
);
1701 body
= g_strdup_printf(
1702 "<setContainerMembers xmlns=\"http://schemas.microsoft.com/2006/09/sip/container-management\">"
1704 "</setContainerMembers>",
1707 contact
= get_contact(sipe_private
);
1708 hdr
= g_strdup_printf("Contact: %s\r\n"
1709 "Content-Type: application/msrtc-setcontainermembers+xml\r\n", contact
);
1712 sip_transport_service(sipe_private
,
1724 * Finds locally stored MS-PRES container member
1726 static struct sipe_container_member
*
1727 sipe_find_container_member(struct sipe_container
*container
,
1731 struct sipe_container_member
*member
;
1734 if (container
== NULL
|| type
== NULL
) {
1738 entry
= container
->members
;
1740 member
= entry
->data
;
1741 if (sipe_strcase_equal(member
->type
, type
) &&
1742 sipe_strcase_equal(member
->value
, value
))
1746 entry
= entry
->next
;
1752 * Finds locally stored MS-PRES container by id
1754 static struct sipe_container
*
1755 sipe_find_container(struct sipe_core_private
*sipe_private
,
1758 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
1759 struct sipe_container
*container
;
1766 entry
= sip
->containers
;
1768 container
= entry
->data
;
1769 if (id
== container
->id
) {
1772 entry
= entry
->next
;
1778 sipe_get_access_domains(struct sipe_core_private
*sipe_private
)
1780 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
1781 struct sipe_container
*container
;
1782 struct sipe_container_member
*member
;
1787 if (!sip
) return NULL
;
1789 entry
= sip
->containers
;
1791 container
= entry
->data
;
1793 entry2
= container
->members
;
1795 member
= entry2
->data
;
1796 if (sipe_strcase_equal(member
->type
, "domain"))
1798 res
= slist_insert_unique_sorted(res
, g_strdup(member
->value
), (GCompareFunc
)g_ascii_strcasecmp
);
1800 entry2
= entry2
->next
;
1802 entry
= entry
->next
;
1808 * Returns pointer to domain part in provided Email URL
1810 * @param email an email URL. Example: first.last@hq.company.com
1811 * @return pointer to domain part of email URL. Coresponding example: hq.company.com
1813 * Doesn't allocate memory
1816 sipe_get_domain(const char *email
)
1820 if (!email
) return NULL
;
1822 tmp
= strstr(email
, "@");
1824 if (tmp
&& ((tmp
+1) < (email
+ strlen(email
)))) {
1832 /* @TODO: replace with binary search for faster access? */
1833 /** source: http://support.microsoft.com/kb/897567 */
1834 static const char * const public_domains
[] = {
1835 "aol.com", "icq.com", "love.com", "mac.com", "br.live.com",
1836 "hotmail.co.il", "hotmail.co.jp", "hotmail.co.th", "hotmail.co.uk",
1837 "hotmail.com", "hotmail.com.ar", "hotmail.com.tr", "hotmail.es",
1838 "hotmail.de", "hotmail.fr", "hotmail.it", "live.at", "live.be",
1839 "live.ca", "live.cl", "live.cn", "live.co.in", "live.co.kr",
1840 "live.co.uk", "live.co.za", "live.com", "live.com.ar", "live.com.au",
1841 "live.com.co", "live.com.mx", "live.com.my", "live.com.pe",
1842 "live.com.ph", "live.com.pk", "live.com.pt", "live.com.sg",
1843 "live.com.ve", "live.de", "live.dk", "live.fr", "live.hk", "live.ie",
1844 "live.in", "live.it", "live.jp", "live.nl", "live.no", "live.ph",
1845 "live.ru", "live.se", "livemail.com.br", "livemail.tw",
1846 "messengeruser.com", "msn.com", "passport.com", "sympatico.ca",
1847 "tw.live.com", "webtv.net", "windowslive.com", "windowslive.es",
1852 sipe_is_public_domain(const char *domain
)
1855 while (public_domains
[i
]) {
1856 if (sipe_strcase_equal(public_domains
[i
], domain
)) {
1873 sipe_get_access_level_name(int container_id
)
1875 switch(container_id
) {
1876 case 32000: return _("Blocked");
1877 case 400: return _("Personal");
1878 case 300: return _("Team");
1879 case 200: return _("Company");
1880 case 100: return _("Public");
1882 return _("Unknown");
1885 static const guint containers
[] = {32000, 400, 300, 200, 100};
1886 #define CONTAINERS_LEN (sizeof(containers) / sizeof(guint))
1890 sipe_find_member_access_level(struct sipe_core_private
*sipe_private
,
1895 const gchar
*value_mod
= value
;
1897 if (!type
) return -1;
1899 if (sipe_strequal("user", type
)) {
1900 value_mod
= sipe_get_no_sip_uri(value
);
1903 for (i
= 0; i
< CONTAINERS_LEN
; i
++) {
1904 struct sipe_container_member
*member
;
1905 struct sipe_container
*container
= sipe_find_container(sipe_private
, containers
[i
]);
1906 if (!container
) continue;
1908 member
= sipe_find_container_member(container
, type
, value_mod
);
1909 if (member
) return containers
[i
];
1915 /** Member type: user, domain, sameEnterprise, federated, publicCloud; everyone */
1917 sipe_find_access_level(struct sipe_core_private
*sipe_private
,
1920 gboolean
*is_group_access
)
1922 int container_id
= -1;
1924 if (sipe_strequal("user", type
)) {
1926 const char *no_sip_uri
= sipe_get_no_sip_uri(value
);
1928 container_id
= sipe_find_member_access_level(sipe_private
, "user", no_sip_uri
);
1929 if (container_id
>= 0) {
1930 if (is_group_access
) *is_group_access
= FALSE
;
1931 return container_id
;
1934 domain
= sipe_get_domain(no_sip_uri
);
1935 container_id
= sipe_find_member_access_level(sipe_private
, "domain", domain
);
1936 if (container_id
>= 0) {
1937 if (is_group_access
) *is_group_access
= TRUE
;
1938 return container_id
;
1941 container_id
= sipe_find_member_access_level(sipe_private
, "sameEnterprise", NULL
);
1942 if ((container_id
>= 0) && sipe_strcase_equal(sipe_private
->public.sip_domain
, domain
)) {
1943 if (is_group_access
) *is_group_access
= TRUE
;
1944 return container_id
;
1947 container_id
= sipe_find_member_access_level(sipe_private
, "publicCloud", NULL
);
1948 if ((container_id
>= 0) && sipe_is_public_domain(domain
)) {
1949 if (is_group_access
) *is_group_access
= TRUE
;
1950 return container_id
;
1953 container_id
= sipe_find_member_access_level(sipe_private
, "everyone", NULL
);
1954 if ((container_id
>= 0)) {
1955 if (is_group_access
) *is_group_access
= TRUE
;
1956 return container_id
;
1959 container_id
= sipe_find_member_access_level(sipe_private
, type
, value
);
1960 if (is_group_access
) *is_group_access
= FALSE
;
1963 return container_id
;
1967 * @param container_id a new access level. If -1 then current access level
1968 * is just removed (I.e. the member is removed from all containers).
1969 * @param type a type of member. E.g. "user", "sameEnterprise", etc.
1970 * @param value a value for member. E.g. SIP URI for "user" member type.
1973 sipe_change_access_level(struct sipe_core_private
*sipe_private
,
1974 const int container_id
,
1979 int current_container_id
= -1;
1980 char *container_xmls
= NULL
;
1982 /* for each container: find/delete */
1983 for (i
= 0; i
< CONTAINERS_LEN
; i
++) {
1984 struct sipe_container_member
*member
;
1985 struct sipe_container
*container
= sipe_find_container(sipe_private
, containers
[i
]);
1987 if (!container
) continue;
1989 member
= sipe_find_container_member(container
, type
, value
);
1991 current_container_id
= containers
[i
];
1992 /* delete/publish current access level */
1993 if (container_id
< 0 || container_id
!= current_container_id
) {
1994 sipe_send_container_members_prepare(current_container_id
, container
->version
, "remove", type
, value
, &container_xmls
);
1995 /* remove member from our cache, to be able to recalculate AL below */
1996 container
->members
= g_slist_remove(container
->members
, member
);
1997 current_container_id
= -1;
2002 /* recalculate AL below */
2003 current_container_id
= sipe_find_access_level(sipe_private
, type
, value
, NULL
);
2005 /* assign/publish new access level */
2006 if (container_id
!= current_container_id
&& container_id
>= 0) {
2007 struct sipe_container
*container
= sipe_find_container(sipe_private
, container_id
);
2008 guint version
= container
? container
->version
: 0;
2010 sipe_send_container_members_prepare(container_id
, version
, "add", type
, value
, &container_xmls
);
2013 if (container_xmls
) {
2014 sipe_send_set_container_members(sipe_private
, container_xmls
);
2016 g_free(container_xmls
);
2020 free_publication(struct sipe_publication
*publication
)
2022 g_free(publication
->category
);
2023 g_free(publication
->cal_event_hash
);
2024 g_free(publication
->note
);
2026 g_free(publication
->working_hours_xml_str
);
2027 g_free(publication
->fb_start_str
);
2028 g_free(publication
->free_busy_base64
);
2030 g_free(publication
);
2033 /* key is <category><instance><container> */
2035 sipe_is_our_publication(struct sipe_core_private
*sipe_private
,
2038 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
2041 /* filling keys for our publications if not yet cached */
2042 if (!sip
->our_publication_keys
) {
2043 guint device_instance
= sipe_get_pub_instance(sipe_private
, SIPE_PUB_DEVICE
);
2044 guint machine_instance
= sipe_get_pub_instance(sipe_private
, SIPE_PUB_STATE_MACHINE
);
2045 guint user_instance
= sipe_get_pub_instance(sipe_private
, SIPE_PUB_STATE_USER
);
2046 guint calendar_instance
= sipe_get_pub_instance(sipe_private
, SIPE_PUB_STATE_CALENDAR
);
2047 guint cal_oof_instance
= sipe_get_pub_instance(sipe_private
, SIPE_PUB_STATE_CALENDAR_OOF
);
2048 guint cal_data_instance
= sipe_get_pub_instance(sipe_private
, SIPE_PUB_CALENDAR_DATA
);
2049 guint note_oof_instance
= sipe_get_pub_instance(sipe_private
, SIPE_PUB_NOTE_OOF
);
2051 SIPE_DEBUG_INFO_NOFORMAT("* Our Publication Instances *");
2052 SIPE_DEBUG_INFO("\tDevice : %u\t0x%08X", device_instance
, device_instance
);
2053 SIPE_DEBUG_INFO("\tMachine State : %u\t0x%08X", machine_instance
, machine_instance
);
2054 SIPE_DEBUG_INFO("\tUser Stare : %u\t0x%08X", user_instance
, user_instance
);
2055 SIPE_DEBUG_INFO("\tCalendar State : %u\t0x%08X", calendar_instance
, calendar_instance
);
2056 SIPE_DEBUG_INFO("\tCalendar OOF State : %u\t0x%08X", cal_oof_instance
, cal_oof_instance
);
2057 SIPE_DEBUG_INFO("\tCalendar FreeBusy : %u\t0x%08X", cal_data_instance
, cal_data_instance
);
2058 SIPE_DEBUG_INFO("\tOOF Note : %u\t0x%08X", note_oof_instance
, note_oof_instance
);
2059 SIPE_DEBUG_INFO("\tNote : %u", 0);
2060 SIPE_DEBUG_INFO("\tCalendar WorkingHours: %u", 0);
2063 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2064 g_strdup_printf("<%s><%u><%u>", "device", device_instance
, 2));
2066 /* state:machineState */
2067 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2068 g_strdup_printf("<%s><%u><%u>", "state", machine_instance
, 2));
2069 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2070 g_strdup_printf("<%s><%u><%u>", "state", machine_instance
, 3));
2072 /* state:userState */
2073 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2074 g_strdup_printf("<%s><%u><%u>", "state", user_instance
, 2));
2075 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2076 g_strdup_printf("<%s><%u><%u>", "state", user_instance
, 3));
2078 /* state:calendarState */
2079 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2080 g_strdup_printf("<%s><%u><%u>", "state", calendar_instance
, 2));
2081 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2082 g_strdup_printf("<%s><%u><%u>", "state", calendar_instance
, 3));
2084 /* state:calendarState OOF */
2085 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2086 g_strdup_printf("<%s><%u><%u>", "state", cal_oof_instance
, 2));
2087 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2088 g_strdup_printf("<%s><%u><%u>", "state", cal_oof_instance
, 3));
2091 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2092 g_strdup_printf("<%s><%u><%u>", "note", 0, 200));
2093 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2094 g_strdup_printf("<%s><%u><%u>", "note", 0, 300));
2095 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2096 g_strdup_printf("<%s><%u><%u>", "note", 0, 400));
2099 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2100 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance
, 200));
2101 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2102 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance
, 300));
2103 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2104 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance
, 400));
2106 /* calendarData:WorkingHours */
2107 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2108 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 1));
2109 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2110 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 100));
2111 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2112 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 200));
2113 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2114 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 300));
2115 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2116 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 400));
2117 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2118 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 32000));
2120 /* calendarData:FreeBusy */
2121 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2122 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 1));
2123 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2124 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 100));
2125 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2126 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 200));
2127 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2128 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 300));
2129 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2130 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 400));
2131 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2132 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 32000));
2134 //SIPE_DEBUG_INFO("sipe_is_our_publication: sip->our_publication_keys length=%d",
2135 // sip->our_publication_keys ? (int) g_slist_length(sip->our_publication_keys) : -1);
2138 //SIPE_DEBUG_INFO("sipe_is_our_publication: key=%s", key);
2140 entry
= sip
->our_publication_keys
;
2142 //SIPE_DEBUG_INFO(" sipe_is_our_publication: entry->data=%s", entry->data);
2143 if (sipe_strequal(entry
->data
, key
)) {
2146 entry
= entry
->next
;
2151 /** Property names to store in blist.xml */
2152 #define ALIAS_PROP "alias"
2153 #define EMAIL_PROP "email"
2154 #define PHONE_PROP "phone"
2155 #define PHONE_DISPLAY_PROP "phone-display"
2156 #define PHONE_MOBILE_PROP "phone-mobile"
2157 #define PHONE_MOBILE_DISPLAY_PROP "phone-mobile-display"
2158 #define PHONE_HOME_PROP "phone-home"
2159 #define PHONE_HOME_DISPLAY_PROP "phone-home-display"
2160 #define PHONE_OTHER_PROP "phone-other"
2161 #define PHONE_OTHER_DISPLAY_PROP "phone-other-display"
2162 #define PHONE_CUSTOM1_PROP "phone-custom1"
2163 #define PHONE_CUSTOM1_DISPLAY_PROP "phone-custom1-display"
2164 #define SITE_PROP "site"
2165 #define COMPANY_PROP "company"
2166 #define DEPARTMENT_PROP "department"
2167 #define TITLE_PROP "title"
2168 #define OFFICE_PROP "office"
2169 /** implies work address */
2170 #define ADDRESS_STREET_PROP "address-street"
2171 #define ADDRESS_CITY_PROP "address-city"
2172 #define ADDRESS_STATE_PROP "address-state"
2173 #define ADDRESS_ZIPCODE_PROP "address-zipcode"
2174 #define ADDRESS_COUNTRYCODE_PROP "address-country-code"
2177 * Tries to figure out user first and last name
2178 * based on Display Name and email properties.
2180 * Allocates memory - must be g_free()'d
2182 * Examples to parse:
2184 * First Last - Company Name
2187 * Last, First (C)(STP) (Company)
2188 * first.last@company.com (preprocessed as "first last")
2189 * first.last.company.com@reuters.net (preprocessed as "first last company com")
2191 * Unusable examples:
2192 * user@company.com (preprocessed as "user")
2193 * first.m.last@company.com (preprocessed as "first m last")
2194 * user.company.com@reuters.net (preprocessed as "user company com")
2197 sipe_get_first_last_names(struct sipe_core_private
*sipe_private
,
2202 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
2203 PurpleBuddy
*p_buddy
;
2206 const char *first
, *last
;
2209 gboolean has_comma
= FALSE
;
2211 if (!sip
|| !uri
) return;
2213 p_buddy
= purple_find_buddy(sip
->account
, uri
);
2215 if (!p_buddy
) return;
2217 display_name
= g_strdup(purple_buddy_get_alias(p_buddy
));
2218 email
= purple_blist_node_get_string(&p_buddy
->node
, EMAIL_PROP
);
2220 if (!display_name
&& !email
) return;
2222 /* if no display name, make "first last anything_else" out of email */
2223 if (email
&& !display_name
) {
2224 display_name
= g_strndup(email
, strstr(email
, "@") - email
);
2225 display_name
= sipe_utils_str_replace((tmp
= display_name
), ".", " ");
2230 has_comma
= (strstr(display_name
, ",") != NULL
);
2231 display_name
= sipe_utils_str_replace((tmp
= display_name
), ", ", " ");
2233 display_name
= sipe_utils_str_replace((tmp
= display_name
), ",", " ");
2237 parts
= g_strsplit(display_name
, " ", 0);
2239 if (!parts
[0] || !parts
[1]) {
2240 g_free(display_name
);
2254 *first_name
= g_strstrip(g_strdup(first
));
2258 *last_name
= g_strstrip(g_strdup(last
));
2261 g_free(display_name
);
2266 * Update user information
2268 * @param uri buddy SIP URI with 'sip:' prefix whose info we want to change.
2269 * @param property_name
2270 * @param property_value may be modified to strip white space
2273 sipe_update_user_info(struct sipe_core_private
*sipe_private
,
2275 const char *property_name
,
2276 char *property_value
)
2278 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
2279 GSList
*buddies
, *entry
;
2281 if (!property_name
|| strlen(property_name
) == 0) return;
2284 property_value
= g_strstrip(property_value
);
2286 entry
= buddies
= purple_find_buddies(sip
->account
, uri
); /* all buddies in different groups */
2288 const char *prop_str
;
2289 const char *server_alias
;
2290 PurpleBuddy
*p_buddy
= entry
->data
;
2292 /* for Display Name */
2293 if (sipe_strequal(property_name
, ALIAS_PROP
)) {
2294 if (property_value
&& sipe_is_bad_alias(uri
, purple_buddy_get_alias(p_buddy
))) {
2295 SIPE_DEBUG_INFO("Replacing alias for %s with %s", uri
, property_value
);
2296 purple_blist_alias_buddy(p_buddy
, property_value
);
2299 server_alias
= purple_buddy_get_server_alias(p_buddy
);
2300 if (!is_empty(property_value
) &&
2301 (!sipe_strequal(property_value
, server_alias
) || is_empty(server_alias
)) )
2303 purple_blist_server_alias_buddy(p_buddy
, property_value
);
2306 /* for other properties */
2308 if (!is_empty(property_value
)) {
2309 prop_str
= purple_blist_node_get_string(&p_buddy
->node
, property_name
);
2310 if (!prop_str
|| !sipe_strcase_equal(prop_str
, property_value
)) {
2311 purple_blist_node_set_string(&p_buddy
->node
, property_name
, property_value
);
2316 entry
= entry
->next
;
2318 g_slist_free(buddies
);
2323 * Suitable for both 2005 and 2007 systems.
2325 * @param uri buddy SIP URI with 'sip:' prefix whose info we want to change.
2327 * @param phone may be modified to strip white space
2328 * @param phone_display_string may be modified to strip white space
2331 sipe_update_user_phone(struct sipe_core_private
*sipe_private
,
2333 const gchar
*phone_type
,
2335 gchar
*phone_display_string
)
2337 const char *phone_node
= PHONE_PROP
; /* work phone by default */
2338 const char *phone_display_node
= PHONE_DISPLAY_PROP
; /* work phone by default */
2340 if(!phone
|| strlen(phone
) == 0) return;
2342 if ((sipe_strequal(phone_type
, "mobile") || sipe_strequal(phone_type
, "cell"))) {
2343 phone_node
= PHONE_MOBILE_PROP
;
2344 phone_display_node
= PHONE_MOBILE_DISPLAY_PROP
;
2345 } else if (sipe_strequal(phone_type
, "home")) {
2346 phone_node
= PHONE_HOME_PROP
;
2347 phone_display_node
= PHONE_HOME_DISPLAY_PROP
;
2348 } else if (sipe_strequal(phone_type
, "other")) {
2349 phone_node
= PHONE_OTHER_PROP
;
2350 phone_display_node
= PHONE_OTHER_DISPLAY_PROP
;
2351 } else if (sipe_strequal(phone_type
, "custom1")) {
2352 phone_node
= PHONE_CUSTOM1_PROP
;
2353 phone_display_node
= PHONE_CUSTOM1_DISPLAY_PROP
;
2356 sipe_update_user_info(sipe_private
, uri
, phone_node
, phone
);
2357 if (phone_display_string
) {
2358 sipe_update_user_info(sipe_private
, uri
, phone_display_node
, phone_display_string
);
2363 sipe_core_update_calendar(struct sipe_core_public
*sipe_public
)
2365 SIPE_DEBUG_INFO_NOFORMAT("sipe_core_update_calendar: started.");
2368 * If failed, the branch will be disabled for subsequent calls.
2369 * Can't rely that user turned the functionality on in account settings.
2371 sipe_ews_update_calendar(SIPE_CORE_PRIVATE
);
2372 sipe_domino_update_calendar(SIPE_CORE_PRIVATE
);
2374 /* schedule repeat */
2375 sipe_schedule_seconds(SIPE_CORE_PRIVATE
,
2376 "<+update-calendar>",
2378 UPDATE_CALENDAR_INTERVAL
,
2379 (sipe_schedule_action
)sipe_core_update_calendar
,
2382 SIPE_DEBUG_INFO_NOFORMAT("sipe_core_update_calendar: finished.");
2386 * This method motivates Purple's Host (e.g. Pidgin) to update its UI
2387 * by using standard Purple's means of signals and saved statuses.
2389 * Thus all UI elements get updated: Status Button with Note, docklet.
2390 * This is ablolutely important as both our status and note can come
2391 * inbound (roaming) or be updated programmatically (e.g. based on our
2395 sipe_set_purple_account_status_and_note(const PurpleAccount
*account
,
2396 const char *status_id
,
2397 const char *message
,
2398 time_t do_not_publish
[])
2400 PurpleStatus
*status
= purple_account_get_active_status(account
);
2401 gboolean changed
= TRUE
;
2403 if (g_str_equal(status_id
, purple_status_get_id(status
)) &&
2404 sipe_strequal(message
, purple_status_get_attr_string(status
, SIPE_STATUS_ATTR_ID_MESSAGE
)))
2409 if (purple_savedstatus_is_idleaway()) {
2414 PurpleSavedStatus
*saved_status
;
2415 const PurpleStatusType
*acct_status_type
=
2416 purple_status_type_find_with_id(account
->status_types
, status_id
);
2417 PurpleStatusPrimitive primitive
= purple_status_type_get_primitive(acct_status_type
);
2418 sipe_activity activity
= sipe_get_activity_by_token(status_id
);
2420 saved_status
= purple_savedstatus_find_transient_by_type_and_message(primitive
, message
);
2422 purple_savedstatus_set_substatus(saved_status
, account
, acct_status_type
, message
);
2425 /* If this type+message is unique then create a new transient saved status
2426 * Ref: gtkstatusbox.c
2428 if (!saved_status
) {
2430 GList
*active_accts
= purple_accounts_get_all_active();
2432 saved_status
= purple_savedstatus_new(NULL
, primitive
);
2433 purple_savedstatus_set_message(saved_status
, message
);
2435 for (tmp
= active_accts
; tmp
!= NULL
; tmp
= tmp
->next
) {
2436 purple_savedstatus_set_substatus(saved_status
,
2437 (PurpleAccount
*)tmp
->data
, acct_status_type
, message
);
2439 g_list_free(active_accts
);
2442 do_not_publish
[activity
] = time(NULL
);
2443 SIPE_DEBUG_INFO("sipe_set_purple_account_status_and_note: do_not_publish[%s]=%d [now]",
2444 status_id
, (int)do_not_publish
[activity
]);
2446 /* Set the status for each account */
2447 purple_savedstatus_activate(saved_status
);
2451 struct hash_table_delete_payload
{
2452 GHashTable
*hash_table
;
2457 sipe_remove_category_container_publications_cb(const char *name
,
2458 struct sipe_publication
*publication
,
2459 struct hash_table_delete_payload
*payload
)
2461 if (publication
->container
== payload
->container
) {
2462 g_hash_table_remove(payload
->hash_table
, name
);
2466 sipe_remove_category_container_publications(GHashTable
*our_publications
,
2467 const char *category
,
2470 struct hash_table_delete_payload payload
;
2471 payload
.hash_table
= g_hash_table_lookup(our_publications
, category
);
2473 if (!payload
.hash_table
) return;
2475 payload
.container
= container
;
2476 g_hash_table_foreach(payload
.hash_table
, (GHFunc
)sipe_remove_category_container_publications_cb
, &payload
);
2480 send_publish_category_initial(struct sipe_core_private
*sipe_private
);
2483 * When we receive some self (BE) NOTIFY with a new subscriber
2484 * we sends a setSubscribers request to him [SIP-PRES] 4.8
2487 static void sipe_process_roaming_self(struct sipe_core_private
*sipe_private
,
2490 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
2494 const sipe_xml
*node
;
2495 const sipe_xml
*node2
;
2496 char *display_name
= NULL
;
2498 GSList
*category_names
= NULL
;
2499 int aggreg_avail
= 0;
2500 gboolean do_update_status
= FALSE
;
2501 gboolean has_note_cleaned
= FALSE
;
2503 SIPE_DEBUG_INFO_NOFORMAT("sipe_process_roaming_self");
2505 xml
= sipe_xml_parse(msg
->body
, msg
->bodylen
);
2508 contact
= get_contact(sipe_private
);
2509 to
= sip_uri_self(sipe_private
);
2513 /* set list of categories participating in this XML */
2514 for (node
= sipe_xml_child(xml
, "categories/category"); node
; node
= sipe_xml_twin(node
)) {
2515 const gchar
*name
= sipe_xml_attribute(node
, "name");
2516 category_names
= slist_insert_unique_sorted(category_names
, (gchar
*)name
, (GCompareFunc
)strcmp
);
2518 SIPE_DEBUG_INFO("sipe_process_roaming_self: category_names length=%d",
2519 category_names
? (int) g_slist_length(category_names
) : -1);
2520 /* drop category information */
2521 if (category_names
) {
2522 GSList
*entry
= category_names
;
2524 GHashTable
*cat_publications
;
2525 const gchar
*category
= entry
->data
;
2526 entry
= entry
->next
;
2527 SIPE_DEBUG_INFO("sipe_process_roaming_self: dropping category: %s", category
);
2528 cat_publications
= g_hash_table_lookup(sip
->our_publications
, category
);
2529 if (cat_publications
) {
2530 g_hash_table_remove(sip
->our_publications
, category
);
2531 SIPE_DEBUG_INFO("sipe_process_roaming_self: dropped category: %s", category
);
2535 g_slist_free(category_names
);
2536 /* filling our categories reflected in roaming data */
2537 for (node
= sipe_xml_child(xml
, "categories/category"); node
; node
= sipe_xml_twin(node
)) {
2539 const gchar
*name
= sipe_xml_attribute(node
, "name");
2540 guint container
= sipe_xml_int_attribute(node
, "container", -1);
2541 guint instance
= sipe_xml_int_attribute(node
, "instance", -1);
2542 guint version
= sipe_xml_int_attribute(node
, "version", 0);
2543 time_t publish_time
= (tmp
= sipe_xml_attribute(node
, "publishTime")) ?
2544 sipe_utils_str_to_time(tmp
) : 0;
2546 GHashTable
*cat_publications
= g_hash_table_lookup(sip
->our_publications
, name
);
2548 /* Ex. clear note: <category name="note"/> */
2549 if (container
== (guint
)-1) {
2552 do_update_status
= TRUE
;
2556 /* Ex. clear note: <category name="note" container="200"/> */
2557 if (instance
== (guint
)-1) {
2558 if (container
== 200) {
2561 do_update_status
= TRUE
;
2563 SIPE_DEBUG_INFO("sipe_process_roaming_self: removing publications for: %s/%u", name
, container
);
2564 sipe_remove_category_container_publications(
2565 sip
->our_publications
, name
, container
);
2569 /* key is <category><instance><container> */
2570 key
= g_strdup_printf("<%s><%u><%u>", name
, instance
, container
);
2571 SIPE_DEBUG_INFO("sipe_process_roaming_self: key=%s version=%d", key
, version
);
2573 /* capture all userState publication for later clean up if required */
2574 if (sipe_strequal(name
, "state") && (container
== 2 || container
== 3)) {
2575 const sipe_xml
*xn_state
= sipe_xml_child(node
, "state");
2577 if (xn_state
&& sipe_strequal(sipe_xml_attribute(xn_state
, "type"), "userState")) {
2578 struct sipe_publication
*publication
= g_new0(struct sipe_publication
, 1);
2579 publication
->category
= g_strdup(name
);
2580 publication
->instance
= instance
;
2581 publication
->container
= container
;
2582 publication
->version
= version
;
2584 if (!sip
->user_state_publications
) {
2585 sip
->user_state_publications
= g_hash_table_new_full(
2586 g_str_hash
, g_str_equal
,
2587 g_free
, (GDestroyNotify
)free_publication
);
2589 g_hash_table_insert(sip
->user_state_publications
, g_strdup(key
), publication
);
2590 SIPE_DEBUG_INFO("sipe_process_roaming_self: added to user_state_publications key=%s version=%d",
2595 if (sipe_is_our_publication(sipe_private
, key
)) {
2596 struct sipe_publication
*publication
= g_new0(struct sipe_publication
, 1);
2598 publication
->category
= g_strdup(name
);
2599 publication
->instance
= instance
;
2600 publication
->container
= container
;
2601 publication
->version
= version
;
2603 /* filling publication->availability */
2604 if (sipe_strequal(name
, "state")) {
2605 const sipe_xml
*xn_state
= sipe_xml_child(node
, "state");
2606 const sipe_xml
*xn_avail
= sipe_xml_child(xn_state
, "availability");
2609 gchar
*avail_str
= sipe_xml_data(xn_avail
);
2611 publication
->availability
= atoi(avail_str
);
2615 /* for calendarState */
2616 if (xn_state
&& sipe_strequal(sipe_xml_attribute(xn_state
, "type"), "calendarState")) {
2617 const sipe_xml
*xn_activity
= sipe_xml_child(xn_state
, "activity");
2618 struct sipe_cal_event
*event
= g_new0(struct sipe_cal_event
, 1);
2620 event
->start_time
= sipe_utils_str_to_time(sipe_xml_attribute(xn_state
, "startTime"));
2622 if (sipe_strequal(sipe_xml_attribute(xn_activity
, "token"),
2623 sipe_activity_map
[SIPE_ACTIVITY_IN_MEETING
].token
))
2625 event
->is_meeting
= TRUE
;
2628 event
->subject
= sipe_xml_data(sipe_xml_child(xn_state
, "meetingSubject"));
2629 event
->location
= sipe_xml_data(sipe_xml_child(xn_state
, "meetingLocation"));
2631 publication
->cal_event_hash
= sipe_cal_event_hash(event
);
2632 SIPE_DEBUG_INFO("sipe_process_roaming_self: hash=%s",
2633 publication
->cal_event_hash
);
2634 sipe_cal_event_free(event
);
2637 /* filling publication->note */
2638 if (sipe_strequal(name
, "note")) {
2639 const sipe_xml
*xn_body
= sipe_xml_child(node
, "note/body");
2641 if (!has_note_cleaned
) {
2642 has_note_cleaned
= TRUE
;
2646 sip
->note_since
= publish_time
;
2648 do_update_status
= TRUE
;
2651 g_free(publication
->note
);
2652 publication
->note
= NULL
;
2656 publication
->note
= g_markup_escape_text((tmp
= sipe_xml_data(xn_body
)), -1);
2658 if (publish_time
>= sip
->note_since
) {
2660 sip
->note
= g_strdup(publication
->note
);
2661 sip
->note_since
= publish_time
;
2662 sip
->is_oof_note
= sipe_strequal(sipe_xml_attribute(xn_body
, "type"), "OOF");
2664 do_update_status
= TRUE
;
2669 /* filling publication->fb_start_str, free_busy_base64, working_hours_xml_str */
2670 if (sipe_strequal(name
, "calendarData") && (publication
->container
== 300)) {
2671 const sipe_xml
*xn_free_busy
= sipe_xml_child(node
, "calendarData/freeBusy");
2672 const sipe_xml
*xn_working_hours
= sipe_xml_child(node
, "calendarData/WorkingHours");
2674 publication
->fb_start_str
= g_strdup(sipe_xml_attribute(xn_free_busy
, "startTime"));
2675 publication
->free_busy_base64
= sipe_xml_data(xn_free_busy
);
2677 if (xn_working_hours
) {
2678 publication
->working_hours_xml_str
= sipe_xml_stringify(xn_working_hours
);
2682 if (!cat_publications
) {
2683 cat_publications
= g_hash_table_new_full(
2684 g_str_hash
, g_str_equal
,
2685 g_free
, (GDestroyNotify
)free_publication
);
2686 g_hash_table_insert(sip
->our_publications
, g_strdup(name
), cat_publications
);
2687 SIPE_DEBUG_INFO("sipe_process_roaming_self: added GHashTable cat=%s", name
);
2689 g_hash_table_insert(cat_publications
, g_strdup(key
), publication
);
2690 SIPE_DEBUG_INFO("sipe_process_roaming_self: added key=%s version=%d", key
, version
);
2694 /* aggregateState (not an our publication) from 2-nd container */
2695 if (sipe_strequal(name
, "state") && container
== 2) {
2696 const sipe_xml
*xn_state
= sipe_xml_child(node
, "state");
2698 if (xn_state
&& sipe_strequal(sipe_xml_attribute(xn_state
, "type"), "aggregateState")) {
2699 const sipe_xml
*xn_avail
= sipe_xml_child(xn_state
, "availability");
2702 gchar
*avail_str
= sipe_xml_data(xn_avail
);
2704 aggreg_avail
= atoi(avail_str
);
2709 do_update_status
= TRUE
;
2713 /* userProperties published by server from AD */
2714 if (!sip
->csta
&& sipe_strequal(name
, "userProperties")) {
2715 const sipe_xml
*line
;
2716 /* line, for Remote Call Control (RCC) */
2717 for (line
= sipe_xml_child(node
, "userProperties/lines/line"); line
; line
= sipe_xml_twin(line
)) {
2718 const gchar
*line_server
= sipe_xml_attribute(line
, "lineServer");
2719 const gchar
*line_type
= sipe_xml_attribute(line
, "lineType");
2722 if (!line_server
|| !(sipe_strequal(line_type
, "Rcc") || sipe_strequal(line_type
, "Dual"))) continue;
2724 line_uri
= sipe_xml_data(line
);
2726 SIPE_DEBUG_INFO("sipe_process_roaming_self: line_uri=%s server=%s", line_uri
, line_server
);
2727 sip_csta_open(sipe_private
, line_uri
, line_server
);
2735 SIPE_DEBUG_INFO("sipe_process_roaming_self: sip->our_publications size=%d",
2736 sip
->our_publications
? (int) g_hash_table_size(sip
->our_publications
) : -1);
2739 for (node
= sipe_xml_child(xml
, "containers/container"); node
; node
= sipe_xml_twin(node
)) {
2740 guint id
= sipe_xml_int_attribute(node
, "id", 0);
2741 struct sipe_container
*container
= sipe_find_container(sipe_private
, id
);
2744 sip
->containers
= g_slist_remove(sip
->containers
, container
);
2745 SIPE_DEBUG_INFO("sipe_process_roaming_self: removed existing container id=%d v%d", container
->id
, container
->version
);
2746 free_container(container
);
2748 container
= g_new0(struct sipe_container
, 1);
2750 container
->version
= sipe_xml_int_attribute(node
, "version", 0);
2751 sip
->containers
= g_slist_append(sip
->containers
, container
);
2752 SIPE_DEBUG_INFO("sipe_process_roaming_self: added container id=%d v%d", container
->id
, container
->version
);
2754 for (node2
= sipe_xml_child(node
, "member"); node2
; node2
= sipe_xml_twin(node2
)) {
2755 struct sipe_container_member
*member
= g_new0(struct sipe_container_member
, 1);
2756 member
->type
= g_strdup(sipe_xml_attribute(node2
, "type"));
2757 member
->value
= g_strdup(sipe_xml_attribute(node2
, "value"));
2758 container
->members
= g_slist_append(container
->members
, member
);
2759 SIPE_DEBUG_INFO("sipe_process_roaming_self: added container member type=%s value=%s",
2760 member
->type
, member
->value
? member
->value
: "");
2764 SIPE_DEBUG_INFO("sipe_process_roaming_self: sip->access_level_set=%s", sip
->access_level_set
? "TRUE" : "FALSE");
2765 if (!sip
->access_level_set
&& sipe_xml_child(xml
, "containers")) {
2766 char *container_xmls
= NULL
;
2767 int sameEnterpriseAL
= sipe_find_access_level(sipe_private
, "sameEnterprise", NULL
, NULL
);
2768 int federatedAL
= sipe_find_access_level(sipe_private
, "federated", NULL
, NULL
);
2770 SIPE_DEBUG_INFO("sipe_process_roaming_self: sameEnterpriseAL=%d", sameEnterpriseAL
);
2771 SIPE_DEBUG_INFO("sipe_process_roaming_self: federatedAL=%d", federatedAL
);
2772 /* initial set-up to let counterparties see your status */
2773 if (sameEnterpriseAL
< 0) {
2774 struct sipe_container
*container
= sipe_find_container(sipe_private
, 200);
2775 guint version
= container
? container
->version
: 0;
2776 sipe_send_container_members_prepare(200, version
, "add", "sameEnterprise", NULL
, &container_xmls
);
2778 if (federatedAL
< 0) {
2779 struct sipe_container
*container
= sipe_find_container(sipe_private
, 100);
2780 guint version
= container
? container
->version
: 0;
2781 sipe_send_container_members_prepare(100, version
, "add", "federated", NULL
, &container_xmls
);
2783 sip
->access_level_set
= TRUE
;
2785 if (container_xmls
) {
2786 sipe_send_set_container_members(sipe_private
, container_xmls
);
2788 g_free(container_xmls
);
2791 /* Refresh contacts' blocked status */
2792 sipe_refresh_blocked_status(sipe_private
);
2795 for (node
= sipe_xml_child(xml
, "subscribers/subscriber"); node
; node
= sipe_xml_twin(node
)) {
2797 const char *acknowledged
;
2801 user
= sipe_xml_attribute(node
, "user"); /* without 'sip:' prefix */
2802 if (!user
) continue;
2803 SIPE_DEBUG_INFO("sipe_process_roaming_self: user %s", user
);
2804 display_name
= g_strdup(sipe_xml_attribute(node
, "displayName"));
2805 uri
= sip_uri_from_name(user
);
2807 sipe_update_user_info(sipe_private
, uri
, ALIAS_PROP
, display_name
);
2809 acknowledged
= sipe_xml_attribute(node
, "acknowledged");
2810 if(sipe_strcase_equal(acknowledged
,"false")){
2811 SIPE_DEBUG_INFO("sipe_process_roaming_self: user added you %s", user
);
2812 if (!purple_find_buddy(sip
->account
, uri
)) {
2813 purple_account_request_add(sip
->account
, uri
, _("you"), display_name
, NULL
);
2816 hdr
= g_strdup_printf(
2818 "Content-Type: application/msrtc-presence-setsubscriber+xml\r\n", contact
);
2820 body
= g_strdup_printf(
2821 "<setSubscribers xmlns=\"http://schemas.microsoft.com/2006/09/sip/presence-subscribers\">"
2822 "<subscriber user=\"%s\" acknowledged=\"true\"/>"
2823 "</setSubscribers>", user
);
2825 sip_transport_service(sipe_private
,
2833 g_free(display_name
);
2840 /* Publish initial state if not yet.
2841 * Assuming this happens on initial responce to subscription to roaming-self
2842 * so we've already updated our roaming data in full.
2845 if (!sip
->initial_state_published
) {
2846 send_publish_category_initial(sipe_private
);
2847 sip
->initial_state_published
= TRUE
;
2849 sipe_schedule_seconds(sipe_private
,
2850 "<+update-calendar>",
2852 UPDATE_CALENDAR_DELAY
,
2853 (sipe_schedule_action
)sipe_core_update_calendar
,
2855 do_update_status
= FALSE
;
2856 } else if (aggreg_avail
) {
2858 g_free(sip
->status
);
2859 if (aggreg_avail
&& aggreg_avail
< 18000) { /* not offline */
2860 sip
->status
= g_strdup(sipe_get_status_by_availability(aggreg_avail
, NULL
));
2862 sip
->status
= g_strdup(SIPE_STATUS_ID_INVISIBLE
); /* not not let offline status switch us off */
2866 if (do_update_status
) {
2867 SIPE_DEBUG_INFO("sipe_process_roaming_self: switch to '%s' for the account", sip
->status
);
2868 sipe_set_purple_account_status_and_note(sip
->account
, sip
->status
, sip
->note
, sip
->do_not_publish
);
2874 /* IM Session (INVITE and MESSAGE methods) */
2876 /* EndPoints: "alice alisson" <sip:alice@atlanta.local>, <sip:bob@atlanta.local>;epid=ebca82d94d, <sip:carol@atlanta.local> */
2878 get_end_points (struct sipe_core_private
*sipe_private
,
2879 struct sip_session
*session
)
2883 if (session
== NULL
) {
2887 res
= g_strdup_printf("<sip:%s>", sipe_private
->username
);
2889 SIPE_DIALOG_FOREACH
{
2891 res
= g_strdup_printf("%s, <%s>", res
, dialog
->with
);
2894 if (dialog
->theirepid
) {
2896 res
= g_strdup_printf("%s;epid=%s", res
, dialog
->theirepid
);
2899 } SIPE_DIALOG_FOREACH_END
;
2905 process_options_response(SIPE_UNUSED_PARAMETER
struct sipe_core_private
*sipe_private
,
2907 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
2909 gboolean ret
= TRUE
;
2911 if (msg
->response
!= 200) {
2912 SIPE_DEBUG_INFO("process_options_response: OPTIONS response is %d", msg
->response
);
2916 SIPE_DEBUG_INFO("process_options_response: body:\n%s", msg
->body
? msg
->body
: "");
2922 * Asks UA/proxy about its capabilities.
2924 static void sipe_options_request(struct sipe_core_private
*sipe_private
,
2927 gchar
*to
= sip_uri(who
);
2928 gchar
*contact
= get_contact(sipe_private
);
2929 gchar
*request
= g_strdup_printf(
2930 "Accept: application/sdp\r\n"
2931 "Contact: %s\r\n", contact
);
2934 sip_transport_request(sipe_private
,
2941 process_options_response
);
2948 sipe_notify_user(struct sipe_core_private
*sipe_private
,
2949 struct sip_session
*session
,
2950 PurpleMessageFlags flags
,
2951 const gchar
*message
)
2953 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
2954 PurpleConversation
*conv
;
2956 if (!session
->backend_session
) {
2957 conv
= purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY
, session
->with
, sip
->account
);
2959 /* TEMPORARY HACK!! */
2960 conv
= (PurpleConversation
*) session
->backend_session
;
2962 purple_conversation_write(conv
, NULL
, message
, flags
, time(NULL
));
2966 sipe_present_info(struct sipe_core_private
*sipe_private
,
2967 struct sip_session
*session
,
2968 const gchar
*message
)
2970 sipe_notify_user(sipe_private
, session
, PURPLE_MESSAGE_SYSTEM
, message
);
2974 sipe_present_err(struct sipe_core_private
*sipe_private
,
2975 struct sip_session
*session
,
2976 const gchar
*message
)
2978 sipe_notify_user(sipe_private
, session
, PURPLE_MESSAGE_ERROR
, message
);
2982 sipe_present_message_undelivered_err(struct sipe_core_private
*sipe_private
,
2983 struct sip_session
*session
,
2987 const gchar
*message
)
2989 char *msg
, *msg_tmp
, *msg_tmp2
;
2992 msg_tmp
= message
? sipe_backend_markup_strip_html(message
) : NULL
;
2993 msg
= msg_tmp
? g_strdup_printf("<font color=\"#888888\"></b>%s<b></font>", msg_tmp
) : NULL
;
2995 /* Service unavailable; Server Internal Error; Server Time-out */
2996 if (sip_error
== 606 && sip_warning
== 309) { /* Not acceptable all. */ /* Message contents not allowed by policy */
2997 label
= _("Your message or invitation was not delivered, possibly because it contains a hyperlink or other content that the system administrator has blocked.");
3000 } else if (sip_error
== 503 || sip_error
== 500 || sip_error
== 504) {
3001 label
= _("This message was not delivered to %s because the service is not available");
3002 } else if (sip_error
== 486) { /* Busy Here */
3003 label
= _("This message was not delivered to %s because one or more recipients do not want to be disturbed");
3004 } else if (sip_error
== 415) { /* Unsupported media type */
3005 label
= _("This message was not delivered to %s because one or more recipients don't support this type of message");
3007 label
= _("This message was not delivered to %s because one or more recipients are offline");
3010 msg_tmp
= g_strdup_printf( "%s%s\n%s" ,
3011 msg_tmp2
= g_strdup_printf(label
, who
? who
: ""),
3014 sipe_present_err(sipe_private
, session
, msg_tmp
);
3022 process_message_response(struct sipe_core_private
*sipe_private
,
3024 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
3026 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
3027 gboolean ret
= TRUE
;
3028 gchar
*with
= parse_from(sipmsg_find_header(msg
, "To"));
3029 struct sip_session
*session
= sipe_session_find_im(sipe_private
, with
);
3030 struct sip_dialog
*dialog
;
3033 struct queued_message
*message
;
3036 SIPE_DEBUG_INFO_NOFORMAT("process_message_response: unable to find IM session");
3041 dialog
= sipe_dialog_find(session
, with
);
3043 SIPE_DEBUG_INFO_NOFORMAT("process_message_response: session outgoing dialog is NULL");
3048 cseq
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "CSeq"), NULL
, " ", NULL
);
3049 key
= g_strdup_printf("<%s><%d><MESSAGE><%s>", sipmsg_find_header(msg
, "Call-ID"), atoi(cseq
), with
);
3051 message
= g_hash_table_lookup(session
->unconfirmed_messages
, key
);
3053 if (msg
->response
>= 400) {
3054 PurpleBuddy
*pbuddy
;
3055 const char *alias
= with
;
3056 const char *warn_hdr
= sipmsg_find_header(msg
, "Warning");
3059 SIPE_DEBUG_INFO_NOFORMAT("process_message_response: MESSAGE response >= 400");
3062 gchar
**parts
= g_strsplit(warn_hdr
, " ", 2);
3064 warning
= atoi(parts
[0]);
3069 /* cancel file transfer as rejected by server */
3070 if (msg
->response
== 606 && /* Not acceptable all. */
3071 warning
== 309 && /* Message contents not allowed by policy */
3072 message
&& g_str_has_prefix(message
->content_type
, "text/x-msmsgsinvite"))
3074 GSList
*parsed_body
= sipe_ft_parse_msg_body(msg
->body
);
3075 sipe_ft_incoming_cancel(dialog
, parsed_body
);
3076 sipe_utils_nameval_free(parsed_body
);
3079 if ((pbuddy
= purple_find_buddy(sip
->account
, with
))) {
3080 alias
= purple_buddy_get_alias(pbuddy
);
3083 sipe_present_message_undelivered_err(sipe_private
, session
, msg
->response
, warning
, alias
, (message
? message
->body
: NULL
));
3085 /* drop dangling IM sessions: assume that BYE from remote never reached us */
3086 if (msg
->response
== 408 || /* Request timeout */
3087 msg
->response
== 480 || /* Temporarily Unavailable */
3088 msg
->response
== 481) { /* Call/Transaction Does Not Exist */
3089 SIPE_DEBUG_INFO_NOFORMAT("process_message_response: assuming dangling IM session, dropping it.");
3090 sip_transport_bye(sipe_private
, dialog
);
3092 /* We might not get a valid reply to our BYE,
3093 so make sure the dialog is removed for sure. */
3094 sipe_dialog_remove(session
, with
);
3100 const gchar
*message_id
= sipmsg_find_header(msg
, "Message-Id");
3102 g_hash_table_insert(session
->conf_unconfirmed_messages
, g_strdup(message_id
), g_strdup(message
->body
));
3103 SIPE_DEBUG_INFO("process_message_response: added message with id %s to conf_unconfirmed_messages(count=%d)",
3104 message_id
, g_hash_table_size(session
->conf_unconfirmed_messages
));
3107 g_hash_table_remove(session
->unconfirmed_messages
, key
);
3108 SIPE_DEBUG_INFO("process_message_response: removed message %s from unconfirmed_messages(count=%d)",
3109 key
, g_hash_table_size(session
->unconfirmed_messages
));
3115 if (ret
) sipe_im_process_queue(sipe_private
, session
);
3119 static void sipe_send_message(struct sipe_core_private
*sipe_private
,
3120 struct sip_dialog
*dialog
,
3121 const char *msg
, const char *content_type
)
3125 char *msgtext
= NULL
;
3126 const gchar
*msgr
= "";
3129 if (!g_str_has_prefix(content_type
, "text/x-msmsgsinvite")) {
3133 sipe_parse_html(msg
, &msgformat
, &msgtext
);
3134 SIPE_DEBUG_INFO("sipe_send_message: msgformat=%s", msgformat
);
3136 msgr_value
= sipmsg_get_msgr_string(msgformat
);
3139 msgr
= tmp2
= g_strdup_printf(";msgr=%s", msgr_value
);
3143 msgtext
= g_strdup(msg
);
3146 tmp
= get_contact(sipe_private
);
3147 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8\r\n");
3148 //hdr = g_strdup("Content-Type: text/rtf\r\n");
3149 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8;msgr=WAAtAE0ATQBTAC....AoADQA\r\nSupported: timer\r\n");
3150 if (content_type
== NULL
)
3151 content_type
= "text/plain";
3153 hdr
= g_strdup_printf("Contact: %s\r\nContent-Type: %s; charset=UTF-8%s\r\n", tmp
, content_type
, msgr
);
3157 sip_transport_request(sipe_private
,
3164 process_message_response
);
3171 sipe_im_process_queue (struct sipe_core_private
*sipe_private
,
3172 struct sip_session
* session
)
3174 GSList
*entry2
= session
->outgoing_message_queue
;
3176 struct queued_message
*msg
= entry2
->data
;
3178 /* for multiparty chat or conference */
3179 if (session
->is_multiparty
|| session
->focus_uri
) {
3180 gchar
*who
= sip_uri_self(sipe_private
);
3181 sipe_backend_chat_message(SIPE_CORE_PUBLIC
,
3188 SIPE_DIALOG_FOREACH
{
3190 struct queued_message
*message
;
3192 if (dialog
->outgoing_invite
) continue; /* do not send messages as INVITE is not responded. */
3194 message
= g_new0(struct queued_message
,1);
3195 message
->body
= g_strdup(msg
->body
);
3196 if (msg
->content_type
!= NULL
)
3197 message
->content_type
= g_strdup(msg
->content_type
);
3199 key
= g_strdup_printf("<%s><%d><MESSAGE><%s>", dialog
->callid
, (dialog
->cseq
) + 1, dialog
->with
);
3200 g_hash_table_insert(session
->unconfirmed_messages
, g_strdup(key
), message
);
3201 SIPE_DEBUG_INFO("sipe_im_process_queue: added message %s to unconfirmed_messages(count=%d)",
3202 key
, g_hash_table_size(session
->unconfirmed_messages
));
3205 sipe_send_message(sipe_private
, dialog
, msg
->body
, msg
->content_type
);
3206 } SIPE_DIALOG_FOREACH_END
;
3208 entry2
= sipe_session_dequeue_message(session
);
3213 sipe_refer_notify(struct sipe_core_private
*sipe_private
,
3214 struct sip_session
*session
,
3221 struct sip_dialog
*dialog
= sipe_dialog_find(session
, who
);
3223 hdr
= g_strdup_printf(
3225 "Subscription-State: %s\r\n"
3226 "Content-Type: message/sipfrag\r\n",
3227 status
>= 200 ? "terminated" : "active");
3229 body
= g_strdup_printf(
3230 "SIP/2.0 %d %s\r\n",
3233 sip_transport_request(sipe_private
,
3247 process_invite_response(struct sipe_core_private
*sipe_private
,
3248 struct sipmsg
*msg
, struct transaction
*trans
)
3250 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
3251 gchar
*with
= parse_from(sipmsg_find_header(msg
, "To"));
3252 struct sip_session
*session
;
3253 struct sip_dialog
*dialog
;
3256 struct queued_message
*message
;
3257 struct sipmsg
*request_msg
= trans
->msg
;
3259 const gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
3262 session
= sipe_session_find_chat_or_im(sipe_private
, callid
, with
);
3264 SIPE_DEBUG_INFO_NOFORMAT("process_invite_response: unable to find IM session");
3269 dialog
= sipe_dialog_find(session
, with
);
3271 SIPE_DEBUG_INFO_NOFORMAT("process_invite_response: session outgoing dialog is NULL");
3276 sipe_dialog_parse(dialog
, msg
, TRUE
);
3278 cseq
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "CSeq"), NULL
, " ", NULL
);
3279 key
= g_strdup_printf("<%s><%d><INVITE>", dialog
->callid
, atoi(cseq
));
3281 message
= g_hash_table_lookup(session
->unconfirmed_messages
, key
);
3283 if (msg
->response
!= 200) {
3284 PurpleBuddy
*pbuddy
;
3285 const char *alias
= with
;
3286 const char *warn_hdr
= sipmsg_find_header(msg
, "Warning");
3289 SIPE_DEBUG_INFO_NOFORMAT("process_invite_response: INVITE response not 200");
3292 gchar
**parts
= g_strsplit(warn_hdr
, " ", 2);
3294 warning
= atoi(parts
[0]);
3299 /* cancel file transfer as rejected by server */
3300 if (msg
->response
== 606 && /* Not acceptable all. */
3301 warning
== 309 && /* Message contents not allowed by policy */
3302 message
&& g_str_has_prefix(message
->content_type
, "text/x-msmsgsinvite"))
3304 GSList
*parsed_body
= sipe_ft_parse_msg_body(message
->body
);
3305 sipe_ft_incoming_cancel(dialog
, parsed_body
);
3306 sipe_utils_nameval_free(parsed_body
);
3309 if ((pbuddy
= purple_find_buddy(sip
->account
, with
))) {
3310 alias
= purple_buddy_get_alias(pbuddy
);
3314 sipe_present_message_undelivered_err(sipe_private
, session
, msg
->response
, warning
, alias
, message
->body
);
3316 gchar
*tmp_msg
= g_strdup_printf(_("Failed to invite %s"), alias
);
3317 sipe_present_err(sipe_private
, session
, tmp_msg
);
3321 sipe_dialog_remove(session
, with
);
3329 sip_transport_ack(sipe_private
, dialog
);
3330 dialog
->outgoing_invite
= NULL
;
3331 dialog
->is_established
= TRUE
;
3333 referred_by
= parse_from(sipmsg_find_header(request_msg
, "Referred-By"));
3335 sipe_refer_notify(sipe_private
, session
, referred_by
, 200, "OK");
3336 g_free(referred_by
);
3339 /* add user to chat if it is a multiparty session */
3340 if (session
->is_multiparty
) {
3341 sipe_backend_chat_add(session
->backend_session
,
3346 if(g_slist_find_custom(dialog
->supported
, "ms-text-format", (GCompareFunc
)g_ascii_strcasecmp
)) {
3347 SIPE_DEBUG_INFO_NOFORMAT("process_invite_response: remote system accepted message in INVITE");
3348 sipe_session_dequeue_message(session
);
3351 sipe_im_process_queue(sipe_private
, session
);
3353 g_hash_table_remove(session
->unconfirmed_messages
, key
);
3354 SIPE_DEBUG_INFO("process_invite_response: removed message %s from unconfirmed_messages(count=%d)",
3355 key
, g_hash_table_size(session
->unconfirmed_messages
));
3364 sipe_invite(struct sipe_core_private
*sipe_private
,
3365 struct sip_session
*session
,
3367 const gchar
*msg_body
,
3368 const gchar
*msg_content_type
,
3369 const gchar
*referred_by
,
3370 const gboolean is_triggered
)
3377 char *ms_text_format
= NULL
;
3378 gchar
*roster_manager
;
3380 gchar
*referred_by_str
;
3381 struct sip_dialog
*dialog
= sipe_dialog_find(session
, who
);
3383 if (dialog
&& dialog
->is_established
) {
3384 SIPE_DEBUG_INFO("session with %s already has a dialog open", who
);
3389 dialog
= sipe_dialog_add(session
);
3390 dialog
->callid
= session
->callid
? g_strdup(session
->callid
) : gencallid();
3391 dialog
->with
= g_strdup(who
);
3394 if (!(dialog
->ourtag
)) {
3395 dialog
->ourtag
= gentag();
3401 char *msgtext
= NULL
;
3403 const gchar
*msgr
= "";
3405 struct queued_message
*message
;
3408 if (!g_str_has_prefix(msg_content_type
, "text/x-msmsgsinvite")) {
3412 sipe_parse_html(msg_body
, &msgformat
, &msgtext
);
3413 SIPE_DEBUG_INFO("sipe_invite: msgformat=%s", msgformat
);
3415 msgr_value
= sipmsg_get_msgr_string(msgformat
);
3418 msgr
= tmp
= g_strdup_printf(";msgr=%s", msgr_value
);
3422 msgtext
= g_strdup(msg_body
);
3425 base64_msg
= g_base64_encode((guchar
*) msgtext
, strlen(msgtext
));
3426 ms_text_format
= g_strdup_printf(SIPE_INVITE_TEXT
,
3427 msg_content_type
? msg_content_type
: "text/plain",
3434 message
= g_new0(struct queued_message
,1);
3435 message
->body
= g_strdup(msg_body
);
3436 if (msg_content_type
!= NULL
)
3437 message
->content_type
= g_strdup(msg_content_type
);
3439 key
= g_strdup_printf("<%s><%d><INVITE>", dialog
->callid
, (dialog
->cseq
) + 1);
3440 g_hash_table_insert(session
->unconfirmed_messages
, g_strdup(key
), message
);
3441 SIPE_DEBUG_INFO("sipe_invite: added message %s to unconfirmed_messages(count=%d)",
3442 key
, g_hash_table_size(session
->unconfirmed_messages
));
3446 contact
= get_contact(sipe_private
);
3447 end_points
= get_end_points(sipe_private
, session
);
3448 self
= sip_uri_self(sipe_private
);
3449 roster_manager
= g_strdup_printf(
3450 "Roster-Manager: %s\r\n"
3451 "EndPoints: %s\r\n",
3454 referred_by_str
= referred_by
?
3456 "Referred-By: %s\r\n",
3459 hdr
= g_strdup_printf(
3460 "Supported: ms-sender\r\n"
3466 "Content-Type: application/sdp\r\n",
3467 sipe_strcase_equal(session
->roster_manager
, self
) ? roster_manager
: "",
3469 is_triggered
? "TriggeredInvite: TRUE\r\n" : "",
3470 is_triggered
|| session
->is_multiparty
? "Require: com.microsoft.rtc-multiparty\r\n" : "",
3472 ms_text_format
? ms_text_format
: "");
3473 g_free(ms_text_format
);
3476 body
= g_strdup_printf(
3478 "o=- 0 0 IN IP4 %s\r\n"
3482 "m=%s %d sip null\r\n"
3483 "a=accept-types:" SDP_ACCEPT_TYPES
"\r\n",
3484 sipe_backend_network_ip_address(),
3485 sipe_backend_network_ip_address(),
3486 SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
) ? "message" : "x-ms-message",
3487 sip_transport_port(sipe_private
));
3489 dialog
->outgoing_invite
= sip_transport_request(sipe_private
,
3496 process_invite_response
);
3499 g_free(roster_manager
);
3501 g_free(referred_by_str
);
3508 sipe_convo_closed(PurpleConnection
* gc
, const char *who
)
3510 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
3512 SIPE_DEBUG_INFO("conversation with %s closed", who
);
3513 sipe_session_close(sipe_private
,
3514 sipe_session_find_im(sipe_private
, who
));
3518 sipe_chat_leave (PurpleConnection
*gc
, int id
)
3520 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
3521 struct sip_session
*session
= sipe_session_find_chat_by_id(sipe_private
,
3524 sipe_session_close(sipe_private
, session
);
3527 int sipe_im_send(PurpleConnection
*gc
, const char *who
, const char *what
,
3528 SIPE_UNUSED_PARAMETER PurpleMessageFlags flags
)
3530 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
3531 struct sip_session
*session
;
3532 struct sip_dialog
*dialog
;
3533 gchar
*uri
= sip_uri(who
);
3535 SIPE_DEBUG_INFO("sipe_im_send what='%s'", what
);
3537 session
= sipe_session_find_or_add_im(sipe_private
, uri
);
3538 dialog
= sipe_dialog_find(session
, uri
);
3540 // Queue the message
3541 sipe_session_enqueue_message(session
, what
, NULL
);
3543 if (dialog
&& !dialog
->outgoing_invite
) {
3544 sipe_im_process_queue(sipe_private
, session
);
3545 } else if (!dialog
|| !dialog
->outgoing_invite
) {
3546 // Need to send the INVITE to get the outgoing dialog setup
3547 sipe_invite(sipe_private
, session
, uri
, what
, NULL
, NULL
, FALSE
);
3554 int sipe_chat_send(PurpleConnection
*gc
, int id
, const char *what
,
3555 SIPE_UNUSED_PARAMETER PurpleMessageFlags flags
)
3557 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
3558 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
3559 struct sip_session
*session
;
3561 SIPE_DEBUG_INFO("sipe_chat_send what='%s'", what
);
3563 session
= sipe_session_find_chat_by_id(sipe_private
, id
);
3565 // Queue the message
3566 if (session
&& session
->dialogs
) {
3567 sipe_session_enqueue_message(session
,what
,NULL
);
3568 sipe_im_process_queue(sipe_private
, session
);
3570 gchar
*chat_name
= purple_find_chat(sip
->gc
, id
)->name
;
3571 const gchar
*proto_chat_id
= sipe_chat_find_name(chat_name
);
3573 SIPE_DEBUG_INFO("sipe_chat_send: chat_name='%s'", chat_name
? chat_name
: "NULL");
3574 SIPE_DEBUG_INFO("sipe_chat_send: proto_chat_id='%s'", proto_chat_id
? proto_chat_id
: "NULL");
3576 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
3577 struct sip_session
*session
= sipe_session_add_chat(sipe_private
);
3579 session
->is_multiparty
= FALSE
;
3580 session
->focus_uri
= g_strdup(proto_chat_id
);
3581 sipe_session_enqueue_message(session
, what
, NULL
);
3582 sipe_invite_conf_focus(sipe_private
, session
);
3590 * Returns 2005-style activity and Availability.
3592 * @param status Sipe statis id.
3595 sipe_get_act_avail_by_status_2005(const char *status
,
3599 int avail
= 300; /* online */
3600 int act
= 400; /* Available */
3602 if (sipe_strequal(status
, SIPE_STATUS_ID_AWAY
)) {
3604 //} else if (sipe_strequal(status, SIPE_STATUS_ID_LUNCH)) {
3606 } else if (sipe_strequal(status
, SIPE_STATUS_ID_BRB
)) {
3608 } else if (sipe_strequal(status
, SIPE_STATUS_ID_AVAILABLE
)) {
3610 //} else if (sipe_strequal(status, SIPE_STATUS_ID_ON_PHONE)) {
3612 } else if (sipe_strequal(status
, SIPE_STATUS_ID_BUSY
) ||
3613 sipe_strequal(status
, SIPE_STATUS_ID_DND
)) {
3615 } else if (sipe_strequal(status
, SIPE_STATUS_ID_INVISIBLE
) ||
3616 sipe_strequal(status
, SIPE_STATUS_ID_OFFLINE
)) {
3617 avail
= 0; /* offline */
3620 act
= 400; /* Available */
3623 if (activity
) *activity
= act
;
3624 if (availability
) *availability
= avail
;
3630 * @param activity 2005 aggregated activity. Ex.: 600
3631 * @param availablity 2005 aggregated availablity. Ex.: 300
3634 sipe_get_status_by_act_avail_2005(const int activity
,
3635 const int availablity
,
3636 char **activity_desc
)
3638 const char *status_id
= NULL
;
3639 const char *act
= NULL
;
3641 if (activity
< 150) {
3642 status_id
= SIPE_STATUS_ID_AWAY
;
3643 } else if (activity
< 200) {
3644 //status_id = SIPE_STATUS_ID_LUNCH;
3645 status_id
= SIPE_STATUS_ID_AWAY
;
3646 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_LUNCH
);
3647 } else if (activity
< 300) {
3648 //status_id = SIPE_STATUS_ID_IDLE;
3649 status_id
= SIPE_STATUS_ID_AWAY
;
3650 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_INACTIVE
);
3651 } else if (activity
< 400) {
3652 status_id
= SIPE_STATUS_ID_BRB
;
3653 } else if (activity
< 500) {
3654 status_id
= SIPE_STATUS_ID_AVAILABLE
;
3655 } else if (activity
< 600) {
3656 //status_id = SIPE_STATUS_ID_ON_PHONE;
3657 status_id
= SIPE_STATUS_ID_BUSY
;
3658 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_ON_PHONE
);
3659 } else if (activity
< 700) {
3660 status_id
= SIPE_STATUS_ID_BUSY
;
3661 } else if (activity
< 800) {
3662 status_id
= SIPE_STATUS_ID_AWAY
;
3664 status_id
= SIPE_STATUS_ID_AVAILABLE
;
3667 if (availablity
< 100)
3668 status_id
= SIPE_STATUS_ID_OFFLINE
;
3670 if (activity_desc
&& act
) {
3671 g_free(*activity_desc
);
3672 *activity_desc
= g_strdup(act
);
3679 * [MS-PRES] Table 3: Conversion of legacyInterop elements and attributes to MSRTC elements and attributes.
3682 sipe_get_status_by_availability(int avail
,
3683 char** activity_desc
)
3686 const char *act
= NULL
;
3689 status
= SIPE_STATUS_ID_OFFLINE
;
3690 } else if (avail
< 4500) {
3691 status
= SIPE_STATUS_ID_AVAILABLE
;
3692 } else if (avail
< 6000) {
3693 //status = SIPE_STATUS_ID_IDLE;
3694 status
= SIPE_STATUS_ID_AVAILABLE
;
3695 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_INACTIVE
);
3696 } else if (avail
< 7500) {
3697 status
= SIPE_STATUS_ID_BUSY
;
3698 } else if (avail
< 9000) {
3699 //status = SIPE_STATUS_ID_BUSYIDLE;
3700 status
= SIPE_STATUS_ID_BUSY
;
3701 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_BUSYIDLE
);
3702 } else if (avail
< 12000) {
3703 status
= SIPE_STATUS_ID_DND
;
3704 } else if (avail
< 15000) {
3705 status
= SIPE_STATUS_ID_BRB
;
3706 } else if (avail
< 18000) {
3707 status
= SIPE_STATUS_ID_AWAY
;
3709 status
= SIPE_STATUS_ID_OFFLINE
;
3712 if (activity_desc
&& act
) {
3713 g_free(*activity_desc
);
3714 *activity_desc
= g_strdup(act
);
3721 * Returns 2007-style availability value
3723 * @param sipe_status_id (in)
3724 * @param activity_token (out) Must be g_free()'d after use if consumed.
3727 sipe_get_availability_by_status(const char* sipe_status_id
, char** activity_token
)
3730 sipe_activity activity
= SIPE_ACTIVITY_UNSET
;
3732 if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_AWAY
)) {
3733 availability
= 15500;
3734 if (!activity_token
|| !(*activity_token
)) {
3735 activity
= SIPE_ACTIVITY_AWAY
;
3737 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_BRB
)) {
3738 availability
= 12500;
3739 activity
= SIPE_ACTIVITY_BRB
;
3740 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_DND
)) {
3741 availability
= 9500;
3742 activity
= SIPE_ACTIVITY_DND
;
3743 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_BUSY
)) {
3744 availability
= 6500;
3745 if (!activity_token
|| !(*activity_token
)) {
3746 activity
= SIPE_ACTIVITY_BUSY
;
3748 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_AVAILABLE
)) {
3749 availability
= 3500;
3750 activity
= SIPE_ACTIVITY_ONLINE
;
3751 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_UNKNOWN
)) {
3754 // Offline or invisible
3755 availability
= 18500;
3756 activity
= SIPE_ACTIVITY_OFFLINE
;
3759 if (activity_token
) {
3760 *activity_token
= g_strdup(sipe_activity_map
[activity
].token
);
3762 return availability
;
3765 static void process_incoming_notify_rlmi(struct sipe_core_private
*sipe_private
,
3766 const gchar
*data
, unsigned len
)
3768 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
3770 sipe_xml
*xn_categories
;
3771 const sipe_xml
*xn_category
;
3772 const char *status
= NULL
;
3773 gboolean do_update_status
= FALSE
;
3774 gboolean has_note_cleaned
= FALSE
;
3775 gboolean has_free_busy_cleaned
= FALSE
;
3777 xn_categories
= sipe_xml_parse(data
, len
);
3778 uri
= sipe_xml_attribute(xn_categories
, "uri"); /* with 'sip:' prefix */
3780 for (xn_category
= sipe_xml_child(xn_categories
, "category");
3782 xn_category
= sipe_xml_twin(xn_category
) )
3784 const sipe_xml
*xn_node
;
3786 const char *attrVar
= sipe_xml_attribute(xn_category
, "name");
3787 time_t publish_time
= (tmp
= sipe_xml_attribute(xn_category
, "publishTime")) ?
3788 sipe_utils_str_to_time(tmp
) : 0;
3791 if (sipe_strequal(attrVar
, "contactCard"))
3793 const sipe_xml
*card
= sipe_xml_child(xn_category
, "contactCard");
3796 const sipe_xml
*node
;
3797 /* identity - Display Name and email */
3798 node
= sipe_xml_child(card
, "identity");
3800 char* display_name
= sipe_xml_data(
3801 sipe_xml_child(node
, "name/displayName"));
3802 char* email
= sipe_xml_data(
3803 sipe_xml_child(node
, "email"));
3805 sipe_update_user_info(sipe_private
, uri
, ALIAS_PROP
, display_name
);
3806 sipe_update_user_info(sipe_private
, uri
, EMAIL_PROP
, email
);
3808 g_free(display_name
);
3812 node
= sipe_xml_child(card
, "company");
3814 char* company
= sipe_xml_data(node
);
3815 sipe_update_user_info(sipe_private
, uri
, COMPANY_PROP
, company
);
3819 node
= sipe_xml_child(card
, "department");
3821 char* department
= sipe_xml_data(node
);
3822 sipe_update_user_info(sipe_private
, uri
, DEPARTMENT_PROP
, department
);
3826 node
= sipe_xml_child(card
, "title");
3828 char* title
= sipe_xml_data(node
);
3829 sipe_update_user_info(sipe_private
, uri
, TITLE_PROP
, title
);
3833 node
= sipe_xml_child(card
, "office");
3835 char* office
= sipe_xml_data(node
);
3836 sipe_update_user_info(sipe_private
, uri
, OFFICE_PROP
, office
);
3840 node
= sipe_xml_child(card
, "url");
3842 char* site
= sipe_xml_data(node
);
3843 sipe_update_user_info(sipe_private
, uri
, SITE_PROP
, site
);
3847 for (node
= sipe_xml_child(card
, "phone");
3849 node
= sipe_xml_twin(node
))
3851 const char *phone_type
= sipe_xml_attribute(node
, "type");
3852 char* phone
= sipe_xml_data(sipe_xml_child(node
, "uri"));
3853 char* phone_display_string
= sipe_xml_data(sipe_xml_child(node
, "displayString"));
3855 sipe_update_user_phone(sipe_private
, uri
, phone_type
, phone
, phone_display_string
);
3858 g_free(phone_display_string
);
3861 for (node
= sipe_xml_child(card
, "address");
3863 node
= sipe_xml_twin(node
))
3865 if (sipe_strequal(sipe_xml_attribute(node
, "type"), "work")) {
3866 char* street
= sipe_xml_data(sipe_xml_child(node
, "street"));
3867 char* city
= sipe_xml_data(sipe_xml_child(node
, "city"));
3868 char* state
= sipe_xml_data(sipe_xml_child(node
, "state"));
3869 char* zipcode
= sipe_xml_data(sipe_xml_child(node
, "zipcode"));
3870 char* country_code
= sipe_xml_data(sipe_xml_child(node
, "countryCode"));
3872 sipe_update_user_info(sipe_private
, uri
, ADDRESS_STREET_PROP
, street
);
3873 sipe_update_user_info(sipe_private
, uri
, ADDRESS_CITY_PROP
, city
);
3874 sipe_update_user_info(sipe_private
, uri
, ADDRESS_STATE_PROP
, state
);
3875 sipe_update_user_info(sipe_private
, uri
, ADDRESS_ZIPCODE_PROP
, zipcode
);
3876 sipe_update_user_info(sipe_private
, uri
, ADDRESS_COUNTRYCODE_PROP
, country_code
);
3882 g_free(country_code
);
3890 else if (sipe_strequal(attrVar
, "note"))
3893 struct sipe_buddy
*sbuddy
= g_hash_table_lookup(sipe_private
->buddies
, uri
);
3895 if (!has_note_cleaned
) {
3896 has_note_cleaned
= TRUE
;
3898 g_free(sbuddy
->note
);
3899 sbuddy
->note
= NULL
;
3900 sbuddy
->is_oof_note
= FALSE
;
3901 sbuddy
->note_since
= publish_time
;
3903 do_update_status
= TRUE
;
3905 if (sbuddy
&& (publish_time
>= sbuddy
->note_since
)) {
3906 /* clean up in case no 'note' element is supplied
3907 * which indicate note removal in client
3909 g_free(sbuddy
->note
);
3910 sbuddy
->note
= NULL
;
3911 sbuddy
->is_oof_note
= FALSE
;
3912 sbuddy
->note_since
= publish_time
;
3914 xn_node
= sipe_xml_child(xn_category
, "note/body");
3917 sbuddy
->note
= g_markup_escape_text((tmp
= sipe_xml_data(xn_node
)), -1);
3919 sbuddy
->is_oof_note
= sipe_strequal(sipe_xml_attribute(xn_node
, "type"), "OOF");
3920 sbuddy
->note_since
= publish_time
;
3922 SIPE_DEBUG_INFO("process_incoming_notify_rlmi: uri(%s), note(%s)",
3923 uri
, sbuddy
->note
? sbuddy
->note
: "");
3925 /* to trigger UI refresh in case no status info is supplied in this update */
3926 do_update_status
= TRUE
;
3931 else if(sipe_strequal(attrVar
, "state"))
3935 const sipe_xml
*xn_availability
;
3936 const sipe_xml
*xn_activity
;
3937 const sipe_xml
*xn_meeting_subject
;
3938 const sipe_xml
*xn_meeting_location
;
3939 struct sipe_buddy
*sbuddy
= uri
? g_hash_table_lookup(sipe_private
->buddies
, uri
) : NULL
;
3941 xn_node
= sipe_xml_child(xn_category
, "state");
3942 if (!xn_node
) continue;
3943 xn_availability
= sipe_xml_child(xn_node
, "availability");
3944 if (!xn_availability
) continue;
3945 xn_activity
= sipe_xml_child(xn_node
, "activity");
3946 xn_meeting_subject
= sipe_xml_child(xn_node
, "meetingSubject");
3947 xn_meeting_location
= sipe_xml_child(xn_node
, "meetingLocation");
3949 tmp
= sipe_xml_data(xn_availability
);
3950 availability
= atoi(tmp
);
3953 /* activity, meeting_subject, meeting_location */
3958 g_free(sbuddy
->activity
);
3959 sbuddy
->activity
= NULL
;
3961 const char *token
= sipe_xml_attribute(xn_activity
, "token");
3962 const sipe_xml
*xn_custom
= sipe_xml_child(xn_activity
, "custom");
3965 if (!is_empty(token
)) {
3966 sbuddy
->activity
= g_strdup(sipe_get_activity_desc_by_token(token
));
3968 /* from custom element */
3970 char *custom
= sipe_xml_data(xn_custom
);
3972 if (!is_empty(custom
)) {
3973 sbuddy
->activity
= custom
;
3979 /* meeting_subject */
3980 g_free(sbuddy
->meeting_subject
);
3981 sbuddy
->meeting_subject
= NULL
;
3982 if (xn_meeting_subject
) {
3983 char *meeting_subject
= sipe_xml_data(xn_meeting_subject
);
3985 if (!is_empty(meeting_subject
)) {
3986 sbuddy
->meeting_subject
= meeting_subject
;
3987 meeting_subject
= NULL
;
3989 g_free(meeting_subject
);
3991 /* meeting_location */
3992 g_free(sbuddy
->meeting_location
);
3993 sbuddy
->meeting_location
= NULL
;
3994 if (xn_meeting_location
) {
3995 char *meeting_location
= sipe_xml_data(xn_meeting_location
);
3997 if (!is_empty(meeting_location
)) {
3998 sbuddy
->meeting_location
= meeting_location
;
3999 meeting_location
= NULL
;
4001 g_free(meeting_location
);
4004 status
= sipe_get_status_by_availability(availability
, &tmp
);
4005 if (sbuddy
->activity
&& tmp
) {
4006 char *tmp2
= sbuddy
->activity
;
4008 sbuddy
->activity
= g_strdup_printf("%s, %s", sbuddy
->activity
, tmp
);
4012 sbuddy
->activity
= tmp
;
4016 do_update_status
= TRUE
;
4019 else if(sipe_strequal(attrVar
, "calendarData"))
4021 struct sipe_buddy
*sbuddy
= uri
? g_hash_table_lookup(sipe_private
->buddies
, uri
) : NULL
;
4022 const sipe_xml
*xn_free_busy
= sipe_xml_child(xn_category
, "calendarData/freeBusy");
4023 const sipe_xml
*xn_working_hours
= sipe_xml_child(xn_category
, "calendarData/WorkingHours");
4025 if (sbuddy
&& xn_free_busy
) {
4026 if (!has_free_busy_cleaned
) {
4027 has_free_busy_cleaned
= TRUE
;
4029 g_free(sbuddy
->cal_start_time
);
4030 sbuddy
->cal_start_time
= NULL
;
4032 g_free(sbuddy
->cal_free_busy_base64
);
4033 sbuddy
->cal_free_busy_base64
= NULL
;
4035 g_free(sbuddy
->cal_free_busy
);
4036 sbuddy
->cal_free_busy
= NULL
;
4038 sbuddy
->cal_free_busy_published
= publish_time
;
4041 if (publish_time
>= sbuddy
->cal_free_busy_published
) {
4042 g_free(sbuddy
->cal_start_time
);
4043 sbuddy
->cal_start_time
= g_strdup(sipe_xml_attribute(xn_free_busy
, "startTime"));
4045 sbuddy
->cal_granularity
= sipe_strcase_equal(sipe_xml_attribute(xn_free_busy
, "granularity"), "PT15M") ?
4048 g_free(sbuddy
->cal_free_busy_base64
);
4049 sbuddy
->cal_free_busy_base64
= sipe_xml_data(xn_free_busy
);
4051 g_free(sbuddy
->cal_free_busy
);
4052 sbuddy
->cal_free_busy
= NULL
;
4054 sbuddy
->cal_free_busy_published
= publish_time
;
4056 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
);
4060 if (sbuddy
&& xn_working_hours
) {
4061 sipe_cal_parse_working_hours(xn_working_hours
, sbuddy
);
4066 if (do_update_status
) {
4067 if (!status
) { /* no status category in this update, using contact's current status */
4068 PurpleBuddy
*pbuddy
= purple_find_buddy((PurpleAccount
*)sip
->account
, uri
);
4069 const PurplePresence
*presence
= purple_buddy_get_presence(pbuddy
);
4070 const PurpleStatus
*pstatus
= purple_presence_get_active_status(presence
);
4071 status
= purple_status_get_id(pstatus
);
4074 SIPE_DEBUG_INFO("process_incoming_notify_rlmi: %s", status
);
4075 sipe_got_user_status(sipe_private
, uri
, status
);
4078 sipe_xml_free(xn_categories
);
4081 static void sipe_subscribe_poolfqdn_resource_uri(const char *host
,
4083 struct sipe_core_private
*sipe_private
)
4085 struct presence_batched_routed
*payload
= g_malloc(sizeof(struct presence_batched_routed
));
4086 SIPE_DEBUG_INFO("process_incoming_notify_rlmi_resub: pool(%s)", host
);
4087 payload
->host
= g_strdup(host
);
4088 payload
->buddies
= server
;
4089 sipe_subscribe_presence_batched_routed(sipe_private
,
4091 sipe_subscribe_presence_batched_routed_free(payload
);
4094 static void process_incoming_notify_rlmi_resub(struct sipe_core_private
*sipe_private
,
4095 const gchar
*data
, unsigned len
)
4098 const sipe_xml
*xn_resource
;
4099 GHashTable
*servers
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
4104 xn_list
= sipe_xml_parse(data
, len
);
4106 for (xn_resource
= sipe_xml_child(xn_list
, "resource");
4108 xn_resource
= sipe_xml_twin(xn_resource
) )
4110 const char *uri
, *state
;
4111 const sipe_xml
*xn_instance
;
4113 xn_instance
= sipe_xml_child(xn_resource
, "instance");
4114 if (!xn_instance
) continue;
4116 uri
= sipe_xml_attribute(xn_resource
, "uri");
4117 state
= sipe_xml_attribute(xn_instance
, "state");
4118 SIPE_DEBUG_INFO("process_incoming_notify_rlmi_resub: uri(%s),state(%s)", uri
, state
);
4120 if (strstr(state
, "resubscribe")) {
4121 const char *poolFqdn
= sipe_xml_attribute(xn_instance
, "poolFqdn");
4123 if (poolFqdn
) { //[MS-PRES] Section 3.4.5.1.3 Processing Details
4124 gchar
*user
= g_strdup(uri
);
4125 host
= g_strdup(poolFqdn
);
4126 server
= g_hash_table_lookup(servers
, host
);
4127 server
= g_slist_append(server
, user
);
4128 g_hash_table_insert(servers
, host
, server
);
4130 sipe_subscribe_presence_single(sipe_private
,
4136 /* Send out any deferred poolFqdn subscriptions */
4137 g_hash_table_foreach(servers
, (GHFunc
) sipe_subscribe_poolfqdn_resource_uri
, sipe_private
);
4138 g_hash_table_destroy(servers
);
4140 sipe_xml_free(xn_list
);
4143 static void process_incoming_notify_pidf(struct sipe_core_private
*sipe_private
,
4144 const gchar
*data
, unsigned len
)
4148 gchar
*activity
= NULL
;
4150 const sipe_xml
*basicstatus
= NULL
, *tuple
, *status
;
4151 gboolean isonline
= FALSE
;
4152 const sipe_xml
*display_name_node
;
4154 pidf
= sipe_xml_parse(data
, len
);
4156 SIPE_DEBUG_INFO("process_incoming_notify_pidf: no parseable pidf:%s", data
);
4160 if ((tuple
= sipe_xml_child(pidf
, "tuple")))
4162 if ((status
= sipe_xml_child(tuple
, "status"))) {
4163 basicstatus
= sipe_xml_child(status
, "basic");
4168 SIPE_DEBUG_INFO_NOFORMAT("process_incoming_notify_pidf: no basic found");
4169 sipe_xml_free(pidf
);
4173 getbasic
= sipe_xml_data(basicstatus
);
4175 SIPE_DEBUG_INFO_NOFORMAT("process_incoming_notify_pidf: no basic data found");
4176 sipe_xml_free(pidf
);
4180 SIPE_DEBUG_INFO("process_incoming_notify_pidf: basic-status(%s)", getbasic
);
4181 if (strstr(getbasic
, "open")) {
4186 uri
= sip_uri(sipe_xml_attribute(pidf
, "entity")); /* with 'sip:' prefix */ /* AOL comes without the prefix */
4188 display_name_node
= sipe_xml_child(pidf
, "display-name");
4189 if (display_name_node
) {
4190 char * display_name
= sipe_xml_data(display_name_node
);
4192 sipe_update_user_info(sipe_private
, uri
, ALIAS_PROP
, display_name
);
4193 g_free(display_name
);
4196 if ((tuple
= sipe_xml_child(pidf
, "tuple"))) {
4197 if ((status
= sipe_xml_child(tuple
, "status"))) {
4198 if ((basicstatus
= sipe_xml_child(status
, "activities"))) {
4199 if ((basicstatus
= sipe_xml_child(basicstatus
, "activity"))) {
4200 activity
= sipe_xml_data(basicstatus
);
4201 SIPE_DEBUG_INFO("process_incoming_notify_pidf: activity(%s)", activity
);
4208 const gchar
* status_id
= NULL
;
4210 if (sipe_strequal(activity
, sipe_activity_map
[SIPE_ACTIVITY_BUSY
].token
)) {
4211 status_id
= SIPE_STATUS_ID_BUSY
;
4212 } else if (sipe_strequal(activity
, sipe_activity_map
[SIPE_ACTIVITY_AWAY
].token
)) {
4213 status_id
= SIPE_STATUS_ID_AWAY
;
4218 status_id
= SIPE_STATUS_ID_AVAILABLE
;
4221 SIPE_DEBUG_INFO("process_incoming_notify_pidf: status_id(%s)", status_id
);
4222 sipe_got_user_status(sipe_private
, uri
, status_id
);
4224 sipe_got_user_status(sipe_private
, uri
, SIPE_STATUS_ID_OFFLINE
);
4229 sipe_xml_free(pidf
);
4234 sipe_user_info_has_updated(struct sipe_core_private
*sipe_private
,
4235 const sipe_xml
*xn_userinfo
)
4237 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
4238 const sipe_xml
*xn_states
;
4240 g_free(sip
->user_states
);
4241 sip
->user_states
= NULL
;
4242 if ((xn_states
= sipe_xml_child(xn_userinfo
, "states")) != NULL
) {
4243 gchar
*orig
= sip
->user_states
= sipe_xml_stringify(xn_states
);
4245 /* this is a hack-around to remove added newline after inner element,
4246 * state in this case, where it shouldn't be.
4247 * After several use of sipe_xml_stringify, amount of added newlines
4248 * grows significantly.
4251 gchar c
, *stripped
= orig
;
4252 while ((c
= *orig
++)) {
4253 if ((c
!= '\n') /* && (c != '\r') */) {
4261 /* Publish initial state if not yet.
4262 * Assuming this happens on initial responce to self subscription
4263 * so we've already updated our UserInfo.
4265 if (!sip
->initial_state_published
) {
4266 send_presence_soap(sipe_private
, FALSE
);
4268 sipe_schedule_seconds(sipe_private
,
4269 "<+update-calendar>",
4271 UPDATE_CALENDAR_DELAY
,
4272 (sipe_schedule_action
) sipe_core_update_calendar
,
4277 static void process_incoming_notify_msrtc(struct sipe_core_private
*sipe_private
,
4278 const gchar
*data
, unsigned len
)
4280 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
4281 char *activity
= NULL
;
4283 const char *status_id
= NULL
;
4286 char *self_uri
= sip_uri_self(sipe_private
);
4289 const char *device_name
= NULL
;
4290 const char *cal_start_time
= NULL
;
4291 const char *cal_granularity
= NULL
;
4292 char *cal_free_busy_base64
= NULL
;
4293 struct sipe_buddy
*sbuddy
;
4294 const sipe_xml
*node
;
4295 sipe_xml
*xn_presentity
;
4296 const sipe_xml
*xn_availability
;
4297 const sipe_xml
*xn_activity
;
4298 const sipe_xml
*xn_display_name
;
4299 const sipe_xml
*xn_email
;
4300 const sipe_xml
*xn_phone_number
;
4301 const sipe_xml
*xn_userinfo
;
4302 const sipe_xml
*xn_note
;
4303 const sipe_xml
*xn_oof
;
4304 const sipe_xml
*xn_state
;
4305 const sipe_xml
*xn_contact
;
4307 char *free_activity
;
4309 const char *user_avail_nil
;
4311 time_t user_avail_since
= 0;
4312 time_t activity_since
= 0;
4314 /* fix for Reuters environment on Linux */
4315 if (data
&& strstr(data
, "encoding=\"utf-16\"")) {
4317 tmp_data
= replace(data
, "encoding=\"utf-16\"", "encoding=\"utf-8\"");
4318 xn_presentity
= sipe_xml_parse(tmp_data
, strlen(tmp_data
));
4321 xn_presentity
= sipe_xml_parse(data
, len
);
4324 xn_availability
= sipe_xml_child(xn_presentity
, "availability");
4325 xn_activity
= sipe_xml_child(xn_presentity
, "activity");
4326 xn_display_name
= sipe_xml_child(xn_presentity
, "displayName");
4327 xn_email
= sipe_xml_child(xn_presentity
, "email");
4328 xn_phone_number
= sipe_xml_child(xn_presentity
, "phoneNumber");
4329 xn_userinfo
= sipe_xml_child(xn_presentity
, "userInfo");
4330 xn_oof
= xn_userinfo
? sipe_xml_child(xn_userinfo
, "oof") : NULL
;
4331 xn_state
= xn_userinfo
? sipe_xml_child(xn_userinfo
, "states/state"): NULL
;
4332 user_avail
= xn_state
? sipe_xml_int_attribute(xn_state
, "avail", 0) : 0;
4333 user_avail_since
= xn_state
? sipe_utils_str_to_time(sipe_xml_attribute(xn_state
, "since")) : 0;
4334 user_avail_nil
= xn_state
? sipe_xml_attribute(xn_state
, "nil") : NULL
;
4335 xn_contact
= xn_userinfo
? sipe_xml_child(xn_userinfo
, "contact") : NULL
;
4336 xn_note
= xn_userinfo
? sipe_xml_child(xn_userinfo
, "note") : NULL
;
4337 note
= xn_note
? sipe_xml_data(xn_note
) : NULL
;
4339 if (sipe_strequal(user_avail_nil
, "true")) { /* null-ed */
4341 user_avail_since
= 0;
4344 free_activity
= NULL
;
4346 name
= sipe_xml_attribute(xn_presentity
, "uri"); /* without 'sip:' prefix */
4347 uri
= sip_uri_from_name(name
);
4348 avl
= sipe_xml_int_attribute(xn_availability
, "aggregate", 0);
4349 epid
= sipe_xml_attribute(xn_availability
, "epid");
4350 act
= sipe_xml_int_attribute(xn_activity
, "aggregate", 0);
4352 status_id
= sipe_get_status_by_act_avail_2005(act
, avl
, &activity
);
4353 res_avail
= sipe_get_availability_by_status(status_id
, NULL
);
4354 if (user_avail
> res_avail
) {
4355 res_avail
= user_avail
;
4356 status_id
= sipe_get_status_by_availability(user_avail
, NULL
);
4359 if (xn_display_name
) {
4360 char *display_name
= g_strdup(sipe_xml_attribute(xn_display_name
, "displayName"));
4361 char *email
= xn_email
? g_strdup(sipe_xml_attribute(xn_email
, "email")) : NULL
;
4362 char *phone_label
= xn_phone_number
? g_strdup(sipe_xml_attribute(xn_phone_number
, "label")) : NULL
;
4363 char *phone_number
= xn_phone_number
? g_strdup(sipe_xml_attribute(xn_phone_number
, "number")) : NULL
;
4364 char *tel_uri
= sip_to_tel_uri(phone_number
);
4366 sipe_update_user_info(sipe_private
, uri
, ALIAS_PROP
, display_name
);
4367 sipe_update_user_info(sipe_private
, uri
, EMAIL_PROP
, email
);
4368 sipe_update_user_info(sipe_private
, uri
, PHONE_PROP
, tel_uri
);
4369 sipe_update_user_info(sipe_private
, uri
, PHONE_DISPLAY_PROP
, !is_empty(phone_label
) ? phone_label
: phone_number
);
4372 g_free(phone_label
);
4373 g_free(phone_number
);
4375 g_free(display_name
);
4380 for (node
= sipe_xml_child(xn_contact
, "tel"); node
; node
= sipe_xml_twin(node
))
4382 /* Ex.: <tel type="work">tel:+3222220000</tel> */
4383 const char *phone_type
= sipe_xml_attribute(node
, "type");
4384 char* phone
= sipe_xml_data(node
);
4386 sipe_update_user_phone(sipe_private
, uri
, phone_type
, phone
, NULL
);
4392 /* devicePresence */
4393 for (node
= sipe_xml_child(xn_presentity
, "devices/devicePresence"); node
; node
= sipe_xml_twin(node
)) {
4394 const sipe_xml
*xn_device_name
;
4395 const sipe_xml
*xn_calendar_info
;
4396 const sipe_xml
*xn_state
;
4400 if (sipe_strequal(sipe_xml_attribute(node
, "epid"), epid
)) {
4401 xn_device_name
= sipe_xml_child(node
, "deviceName");
4402 device_name
= xn_device_name
? sipe_xml_attribute(xn_device_name
, "name") : NULL
;
4406 xn_calendar_info
= sipe_xml_child(node
, "calendarInfo");
4407 if (xn_calendar_info
) {
4408 const char *cal_start_time_tmp
= sipe_xml_attribute(xn_calendar_info
, "startTime");
4410 if (cal_start_time
) {
4411 time_t cal_start_time_t
= sipe_utils_str_to_time(cal_start_time
);
4412 time_t cal_start_time_t_tmp
= sipe_utils_str_to_time(cal_start_time_tmp
);
4414 if (cal_start_time_t_tmp
> cal_start_time_t
) {
4415 cal_start_time
= cal_start_time_tmp
;
4416 cal_granularity
= sipe_xml_attribute(xn_calendar_info
, "granularity");
4417 g_free(cal_free_busy_base64
);
4418 cal_free_busy_base64
= sipe_xml_data(xn_calendar_info
);
4420 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
);
4423 cal_start_time
= cal_start_time_tmp
;
4424 cal_granularity
= sipe_xml_attribute(xn_calendar_info
, "granularity");
4425 g_free(cal_free_busy_base64
);
4426 cal_free_busy_base64
= sipe_xml_data(xn_calendar_info
);
4428 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
);
4433 xn_state
= sipe_xml_child(node
, "states/state");
4435 int dev_avail
= sipe_xml_int_attribute(xn_state
, "avail", 0);
4436 time_t dev_avail_since
= sipe_utils_str_to_time(sipe_xml_attribute(xn_state
, "since"));
4438 state
= sipe_xml_data(xn_state
);
4439 if (dev_avail_since
> user_avail_since
&&
4440 dev_avail
>= res_avail
)
4442 res_avail
= dev_avail
;
4443 if (!is_empty(state
))
4445 if (sipe_strequal(state
, sipe_activity_map
[SIPE_ACTIVITY_ON_PHONE
].token
)) {
4447 activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_ON_PHONE
));
4448 } else if (sipe_strequal(state
, "presenting")) {
4450 activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_IN_CONF
));
4455 activity_since
= dev_avail_since
;
4457 status_id
= sipe_get_status_by_availability(res_avail
, &activity
);
4464 if (xn_oof
&& res_avail
>= 15000) { /* 12000 in 2007 */
4466 activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_OOF
));
4470 sbuddy
= g_hash_table_lookup(sipe_private
->buddies
, uri
);
4473 g_free(sbuddy
->activity
);
4474 sbuddy
->activity
= activity
;
4477 sbuddy
->activity_since
= activity_since
;
4479 sbuddy
->user_avail
= user_avail
;
4480 sbuddy
->user_avail_since
= user_avail_since
;
4482 g_free(sbuddy
->note
);
4483 sbuddy
->note
= NULL
;
4484 if (!is_empty(note
)) { sbuddy
->note
= g_markup_escape_text(note
, -1); }
4486 sbuddy
->is_oof_note
= (xn_oof
!= NULL
);
4488 g_free(sbuddy
->device_name
);
4489 sbuddy
->device_name
= NULL
;
4490 if (!is_empty(device_name
)) { sbuddy
->device_name
= g_strdup(device_name
); }
4492 if (!is_empty(cal_free_busy_base64
)) {
4493 g_free(sbuddy
->cal_start_time
);
4494 sbuddy
->cal_start_time
= g_strdup(cal_start_time
);
4496 sbuddy
->cal_granularity
= sipe_strcase_equal(cal_granularity
, "PT15M") ? 15 : 0;
4498 g_free(sbuddy
->cal_free_busy_base64
);
4499 sbuddy
->cal_free_busy_base64
= cal_free_busy_base64
;
4500 cal_free_busy_base64
= NULL
;
4502 g_free(sbuddy
->cal_free_busy
);
4503 sbuddy
->cal_free_busy
= NULL
;
4506 sbuddy
->last_non_cal_status_id
= status_id
;
4507 g_free(sbuddy
->last_non_cal_activity
);
4508 sbuddy
->last_non_cal_activity
= g_strdup(sbuddy
->activity
);
4510 if (sipe_strcase_equal(sbuddy
->name
, self_uri
)) {
4511 if (!sipe_strequal(sbuddy
->note
, sip
->note
)) /* not same */
4513 sip
->is_oof_note
= sbuddy
->is_oof_note
;
4516 sip
->note
= g_strdup(sbuddy
->note
);
4518 sip
->note_since
= time(NULL
);
4521 g_free(sip
->status
);
4522 sip
->status
= g_strdup(sbuddy
->last_non_cal_status_id
);
4525 g_free(cal_free_busy_base64
);
4528 SIPE_DEBUG_INFO("process_incoming_notify_msrtc: status(%s)", status_id
);
4529 sipe_got_user_status(sipe_private
, uri
, status_id
);
4531 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
) && sipe_strcase_equal(self_uri
, uri
)) {
4532 sipe_user_info_has_updated(sipe_private
, xn_userinfo
);
4536 sipe_xml_free(xn_presentity
);
4541 static void sipe_presence_mime_cb(gpointer user_data
, /* sipe_core_private */
4542 const GSList
*fields
,
4546 const gchar
*type
= sipe_utils_nameval_find(fields
, "Content-Type");
4548 if (strstr(type
,"application/rlmi+xml")) {
4549 process_incoming_notify_rlmi_resub(user_data
, body
, length
);
4550 } else if (strstr(type
, "text/xml+msrtc.pidf")) {
4551 process_incoming_notify_msrtc(user_data
, body
, length
);
4553 process_incoming_notify_rlmi(user_data
, body
, length
);
4557 static void sipe_process_presence(struct sipe_core_private
*sipe_private
,
4560 const char *ctype
= sipmsg_find_header(msg
, "Content-Type");
4562 SIPE_DEBUG_INFO("sipe_process_presence: Content-Type: %s", ctype
? ctype
: "");
4565 (strstr(ctype
, "application/rlmi+xml") ||
4566 strstr(ctype
, "application/msrtc-event-categories+xml")))
4568 if (strstr(ctype
, "multipart"))
4570 sipe_mime_parts_foreach(ctype
, msg
->body
, sipe_presence_mime_cb
, sipe_private
);
4572 else if(strstr(ctype
, "application/msrtc-event-categories+xml") )
4574 process_incoming_notify_rlmi(sipe_private
, msg
->body
, msg
->bodylen
);
4576 else if(strstr(ctype
, "application/rlmi+xml"))
4578 process_incoming_notify_rlmi_resub(sipe_private
, msg
->body
, msg
->bodylen
);
4581 else if(ctype
&& strstr(ctype
, "text/xml+msrtc.pidf"))
4583 process_incoming_notify_msrtc(sipe_private
, msg
->body
, msg
->bodylen
);
4587 process_incoming_notify_pidf(sipe_private
, msg
->body
, msg
->bodylen
);
4591 static void sipe_presence_timeout_mime_cb(gpointer user_data
,
4592 SIPE_UNUSED_PARAMETER
const GSList
*fields
,
4596 GSList
**buddies
= user_data
;
4597 sipe_xml
*xml
= sipe_xml_parse(body
, length
);
4599 if (xml
&& !sipe_strequal(sipe_xml_name(xml
), "list")) {
4600 const gchar
*uri
= sipe_xml_attribute(xml
, "uri");
4601 const sipe_xml
*xn_category
;
4604 * automaton: presence is never expected to change
4606 * see: http://msdn.microsoft.com/en-us/library/ee354295(office.13).aspx
4608 for (xn_category
= sipe_xml_child(xml
, "category");
4610 xn_category
= sipe_xml_twin(xn_category
)) {
4611 if (sipe_strequal(sipe_xml_attribute(xn_category
, "name"),
4613 const sipe_xml
*node
= sipe_xml_child(xn_category
, "contactCard/automaton");
4615 char *boolean
= sipe_xml_data(node
);
4616 if (sipe_strequal(boolean
, "true")) {
4617 SIPE_DEBUG_INFO("sipe_process_presence_timeout: %s is an automaton: - not subscribing to presence updates",
4628 *buddies
= g_slist_append(*buddies
, sip_uri(uri
));
4635 static void sipe_process_presence_timeout(struct sipe_core_private
*sipe_private
,
4636 struct sipmsg
*msg
, gchar
*who
,
4639 const char *ctype
= sipmsg_find_header(msg
, "Content-Type");
4640 gchar
*action_name
= sipe_utils_presence_key(who
);
4642 SIPE_DEBUG_INFO("sipe_process_presence_timeout: Content-Type: %s", ctype
? ctype
: "");
4645 strstr(ctype
, "multipart") &&
4646 (strstr(ctype
, "application/rlmi+xml") ||
4647 strstr(ctype
, "application/msrtc-event-categories+xml"))) {
4648 GSList
*buddies
= NULL
;
4650 sipe_mime_parts_foreach(ctype
, msg
->body
, sipe_presence_timeout_mime_cb
, &buddies
);
4653 struct presence_batched_routed
*payload
= g_malloc(sizeof(struct presence_batched_routed
));
4654 payload
->host
= g_strdup(who
);
4655 payload
->buddies
= buddies
;
4656 sipe_schedule_seconds(sipe_private
,
4660 sipe_subscribe_presence_batched_routed
,
4661 sipe_subscribe_presence_batched_routed_free
);
4662 SIPE_DEBUG_INFO("Resubscription multiple contacts with batched support & route(%s) in %d", who
, timeout
);
4666 sipe_schedule_seconds(sipe_private
,
4670 sipe_subscribe_presence_single
,
4672 SIPE_DEBUG_INFO("Resubscription single contact with batched support(%s) in %d", who
, timeout
);
4674 g_free(action_name
);
4678 * Dispatcher for all incoming subscription information
4679 * whether it comes from NOTIFY, BENOTIFY requests or
4680 * piggy-backed to subscription's OK responce.
4682 * @param request whether initiated from BE/NOTIFY request or OK-response message.
4683 * @param benotify whether initiated from NOTIFY or BENOTIFY request.
4685 void process_incoming_notify(struct sipe_core_private
*sipe_private
,
4687 gboolean request
, gboolean benotify
)
4689 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
4690 const gchar
*content_type
= sipmsg_find_header(msg
, "Content-Type");
4691 const gchar
*event
= sipmsg_find_header(msg
, "Event");
4692 const gchar
*subscription_state
= sipmsg_find_header(msg
, "subscription-state");
4694 SIPE_DEBUG_INFO("process_incoming_notify: subscription_state: %s", subscription_state
? subscription_state
: "");
4696 /* implicit subscriptions */
4697 if (content_type
&& g_str_has_prefix(content_type
, "application/ms-imdn+xml")) {
4698 sipe_process_imdn(sipe_private
, msg
);
4702 /* for one off subscriptions (send with Expire: 0) */
4703 if (sipe_strcase_equal(event
, "vnd-microsoft-provisioning-v2"))
4705 sipe_process_provisioning_v2(sipe_private
, msg
);
4707 else if (sipe_strcase_equal(event
, "vnd-microsoft-provisioning"))
4709 sipe_process_provisioning(sipe_private
, msg
);
4711 else if (sipe_strcase_equal(event
, "presence"))
4713 sipe_process_presence(sipe_private
, msg
);
4715 else if (sipe_strcase_equal(event
, "registration-notify"))
4717 sipe_process_registration_notify(sipe_private
, msg
);
4720 if (!subscription_state
|| strstr(subscription_state
, "active"))
4722 if (sipe_strcase_equal(event
, "vnd-microsoft-roaming-contacts"))
4724 sipe_process_roaming_contacts(sipe_private
, msg
);
4726 else if (sipe_strcase_equal(event
, "vnd-microsoft-roaming-self"))
4728 sipe_process_roaming_self(sipe_private
, msg
);
4730 else if (sipe_strcase_equal(event
, "vnd-microsoft-roaming-ACL"))
4732 sipe_process_roaming_acl(sipe_private
, msg
);
4734 else if (sipe_strcase_equal(event
, "presence.wpending"))
4736 sipe_process_presence_wpending(sipe_private
, msg
);
4738 else if (sipe_strcase_equal(event
, "conference"))
4740 sipe_process_conference(sipe_private
, msg
);
4745 /* The server sends status 'terminated' */
4746 if (subscription_state
&& strstr(subscription_state
, "terminated") ) {
4747 gchar
*who
= parse_from(sipmsg_find_header(msg
, request
? "From" : "To"));
4748 gchar
*key
= sipe_utils_subscription_key(event
, who
);
4750 SIPE_DEBUG_INFO("process_incoming_notify: server says that subscription to %s was terminated.", who
);
4753 sipe_subscriptions_remove(sipe_private
, key
);
4757 if (!request
&& event
) {
4758 const gchar
*expires_header
= sipmsg_find_header(msg
, "Expires");
4759 int timeout
= expires_header
? strtol(expires_header
, NULL
, 10) : 0;
4760 SIPE_DEBUG_INFO("process_incoming_notify: subscription expires:%d", timeout
);
4763 /* 2 min ahead of expiration */
4764 timeout
= (timeout
- 120) > 120 ? (timeout
- 120) : timeout
;
4766 if (sipe_strcase_equal(event
, "presence.wpending") &&
4767 g_slist_find_custom(sip
->allow_events
, "presence.wpending", (GCompareFunc
)g_ascii_strcasecmp
))
4769 gchar
*action_name
= g_strdup_printf("<%s>", "presence.wpending");
4770 sipe_schedule_seconds(sipe_private
,
4774 sipe_subscribe_presence_wpending
,
4776 g_free(action_name
);
4778 else if (sipe_strcase_equal(event
, "presence") &&
4779 g_slist_find_custom(sip
->allow_events
, "presence", (GCompareFunc
)g_ascii_strcasecmp
))
4781 gchar
*who
= parse_from(sipmsg_find_header(msg
, "To"));
4782 gchar
*action_name
= sipe_utils_presence_key(who
);
4784 if (sip
->batched_support
) {
4785 sipe_process_presence_timeout(sipe_private
, msg
, who
, timeout
);
4788 sipe_schedule_seconds(sipe_private
,
4792 sipe_subscribe_presence_single
,
4794 SIPE_DEBUG_INFO("Resubscription single contact (%s) in %d", who
, timeout
);
4796 g_free(action_name
);
4802 /* The client responses on received a NOTIFY message */
4803 if (request
&& !benotify
)
4805 sip_transport_response(sipe_private
, msg
, 200, "OK", NULL
);
4810 * Whether user manually changed status or
4811 * it was changed automatically due to user
4812 * became inactive/active again
4815 sipe_is_user_state(struct sipe_core_private
*sipe_private
)
4817 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
4819 time_t now
= time(NULL
);
4821 SIPE_DEBUG_INFO("sipe_is_user_state: sip->idle_switch : %s", asctime(localtime(&(sip
->idle_switch
))));
4822 SIPE_DEBUG_INFO("sipe_is_user_state: now : %s", asctime(localtime(&now
)));
4824 res
= ((now
- SIPE_IDLE_SET_DELAY
* 2) >= sip
->idle_switch
);
4826 SIPE_DEBUG_INFO("sipe_is_user_state: res = %s", res
? "USER" : "MACHINE");
4831 send_presence_soap0(struct sipe_core_private
*sipe_private
,
4832 gboolean do_publish_calendar
,
4833 gboolean do_reset_status
)
4835 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
4836 struct sipe_calendar
* cal
= sip
->cal
;
4837 int availability
= 0;
4842 gchar
*res_note
= NULL
;
4843 gchar
*res_oof
= NULL
;
4844 const gchar
*note_pub
= NULL
;
4845 gchar
*states
= NULL
;
4846 gchar
*calendar_data
= NULL
;
4847 gchar
*epid
= get_epid(sipe_private
);
4848 time_t now
= time(NULL
);
4849 gchar
*since_time_str
= sipe_utils_time_to_str(now
);
4850 const gchar
*oof_note
= cal
? sipe_ews_get_oof_note(cal
) : NULL
;
4851 const char *user_input
;
4852 gboolean pub_oof
= cal
&& oof_note
&& (!sip
->note
|| cal
->updated
> sip
->note_since
);
4854 if (oof_note
&& sip
->note
) {
4855 SIPE_DEBUG_INFO("cal->oof_start : %s", asctime(localtime(&(cal
->oof_start
))));
4856 SIPE_DEBUG_INFO("sip->note_since : %s", asctime(localtime(&(sip
->note_since
))));
4859 SIPE_DEBUG_INFO("sip->note : %s", sip
->note
? sip
->note
: "");
4861 if (!sip
->initial_state_published
||
4864 g_free(sip
->status
);
4865 sip
->status
= g_strdup(SIPE_STATUS_ID_AVAILABLE
);
4868 sipe_get_act_avail_by_status_2005(sip
->status
, &activity
, &availability
);
4872 note_pub
= oof_note
;
4873 res_oof
= SIPE_SOAP_SET_PRESENCE_OOF_XML
;
4874 cal
->published
= TRUE
;
4875 } else if (sip
->note
) {
4876 if (sip
->is_oof_note
&& !oof_note
) { /* stale OOF note, as it's not present in cal already */
4879 sip
->is_oof_note
= FALSE
;
4880 sip
->note_since
= 0;
4882 note_pub
= sip
->note
;
4883 res_oof
= sip
->is_oof_note
? SIPE_SOAP_SET_PRESENCE_OOF_XML
: "";
4889 /* to protocol internal plain text format */
4890 tmp
= sipe_backend_markup_strip_html(note_pub
);
4891 res_note
= g_markup_printf_escaped(SIPE_SOAP_SET_PRESENCE_NOTE_XML
, tmp
);
4896 if (!do_reset_status
) {
4897 if (sipe_is_user_state(sipe_private
) && !do_publish_calendar
&& sip
->initial_state_published
)
4899 gchar
*activity_token
= NULL
;
4900 int avail_2007
= sipe_get_availability_by_status(sip
->status
, &activity_token
);
4902 states
= g_strdup_printf(SIPE_SOAP_SET_PRESENCE_STATES
,
4907 g_free(activity_token
);
4909 else /* preserve existing publication */
4911 if (sip
->user_states
) {
4912 states
= g_strdup(sip
->user_states
);
4916 /* do nothing - then User state will be erased */
4918 sip
->initial_state_published
= TRUE
;
4921 if (cal
&& (!is_empty(cal
->legacy_dn
) || !is_empty(cal
->email
)) && cal
->fb_start
&& !is_empty(cal
->free_busy
))
4923 char *fb_start_str
= sipe_utils_time_to_str(cal
->fb_start
);
4924 char *free_busy_base64
= sipe_cal_get_freebusy_base64(cal
->free_busy
);
4925 calendar_data
= g_strdup_printf(SIPE_SOAP_SET_PRESENCE_CALENDAR
,
4926 !is_empty(cal
->legacy_dn
) ? cal
->legacy_dn
: cal
->email
,
4929 g_free(fb_start_str
);
4930 g_free(free_busy_base64
);
4933 user_input
= (sipe_is_user_state(sipe_private
) ||
4934 sipe_strequal(sip
->status
, SIPE_STATUS_ID_AVAILABLE
)) ?
4937 /* forming resulting XML */
4938 body
= g_strdup_printf(SIPE_SOAP_SET_PRESENCE
,
4939 sipe_private
->username
,
4942 (tmp
= g_ascii_strup(g_get_host_name(), -1)),
4943 res_note
? res_note
: "",
4944 res_oof
? res_oof
: "",
4945 states
? states
: "",
4946 calendar_data
? calendar_data
: "",
4955 g_free(calendar_data
);
4957 send_soap_request(sipe_private
, body
);
4960 g_free(since_time_str
);
4965 send_presence_soap(struct sipe_core_private
*sipe_private
,
4966 gboolean do_publish_calendar
)
4968 return send_presence_soap0(sipe_private
, do_publish_calendar
, FALSE
);
4973 process_send_presence_category_publish_response(struct sipe_core_private
*sipe_private
,
4975 struct transaction
*trans
)
4977 const gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
4979 if (msg
->response
== 409 && g_str_has_prefix(contenttype
, "application/msrtc-fault+xml")) {
4981 const sipe_xml
*node
;
4985 gboolean has_device_publication
= FALSE
;
4987 xml
= sipe_xml_parse(msg
->body
, msg
->bodylen
);
4989 /* test if version mismatch fault */
4990 fault_code
= sipe_xml_data(sipe_xml_child(xml
, "Faultcode"));
4991 if (!sipe_strequal(fault_code
, "Client.BadCall.WrongDelta")) {
4992 SIPE_DEBUG_INFO("process_send_presence_category_publish_response: unsupported fault code:%s returning.", fault_code
);
4999 /* accumulating information about faulty versions */
5000 faults
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, g_free
);
5001 for (node
= sipe_xml_child(xml
, "details/operation");
5003 node
= sipe_xml_twin(node
))
5005 const gchar
*index
= sipe_xml_attribute(node
, "index");
5006 const gchar
*curVersion
= sipe_xml_attribute(node
, "curVersion");
5008 g_hash_table_insert(faults
, g_strdup(index
), g_strdup(curVersion
));
5009 SIPE_DEBUG_INFO("fault added: index:%s curVersion:%s", index
, curVersion
);
5013 /* here we are parsing own request to figure out what publication
5014 * referensed here only by index went wrong
5016 xml
= sipe_xml_parse(trans
->msg
->body
, trans
->msg
->bodylen
);
5019 for (node
= sipe_xml_child(xml
, "publications/publication"),
5020 index_our
= 1; /* starts with 1 - our first publication */
5022 node
= sipe_xml_twin(node
), index_our
++)
5024 gchar
*idx
= g_strdup_printf("%d", index_our
);
5025 const gchar
*curVersion
= g_hash_table_lookup(faults
, idx
);
5026 const gchar
*categoryName
= sipe_xml_attribute(node
, "categoryName");
5029 if (sipe_strequal("device", categoryName
)) {
5030 has_device_publication
= TRUE
;
5033 if (curVersion
) { /* fault exist on this index */
5034 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
5035 const gchar
*container
= sipe_xml_attribute(node
, "container");
5036 const gchar
*instance
= sipe_xml_attribute(node
, "instance");
5037 /* key is <category><instance><container> */
5038 gchar
*key
= g_strdup_printf("<%s><%s><%s>", categoryName
, instance
, container
);
5039 GHashTable
*category
= g_hash_table_lookup(sip
->our_publications
, categoryName
);
5042 struct sipe_publication
*publication
=
5043 g_hash_table_lookup(category
, key
);
5045 SIPE_DEBUG_INFO("key is %s", key
);
5048 SIPE_DEBUG_INFO("Updating %s with version %s. Was %d before.",
5049 key
, curVersion
, publication
->version
);
5050 /* updating publication's version to the correct one */
5051 publication
->version
= atoi(curVersion
);
5054 /* We somehow lost this category from our publications... */
5055 struct sipe_publication
*publication
= g_new0(struct sipe_publication
, 1);
5056 publication
->category
= g_strdup(categoryName
);
5057 publication
->instance
= atoi(instance
);
5058 publication
->container
= atoi(container
);
5059 publication
->version
= atoi(curVersion
);
5060 category
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
5061 g_free
, (GDestroyNotify
)free_publication
);
5062 g_hash_table_insert(category
, g_strdup(key
), publication
);
5063 g_hash_table_insert(sip
->our_publications
, g_strdup(categoryName
), category
);
5064 SIPE_DEBUG_INFO("added lost category '%s' key '%s'", categoryName
, key
);
5070 g_hash_table_destroy(faults
);
5072 /* rebublishing with right versions */
5073 if (has_device_publication
) {
5074 send_publish_category_initial(sipe_private
);
5076 send_presence_status(sipe_private
, NULL
);
5083 * Returns 'device' XML part for publication.
5084 * Must be g_free'd after use.
5087 sipe_publish_get_category_device(struct sipe_core_private
*sipe_private
)
5089 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
5092 gchar
*epid
= get_epid(sipe_private
);
5093 gchar
*uuid
= generateUUIDfromEPID(epid
);
5094 guint device_instance
= sipe_get_pub_instance(sipe_private
, SIPE_PUB_DEVICE
);
5095 /* key is <category><instance><container> */
5096 gchar
*key
= g_strdup_printf("<%s><%u><%u>", "device", device_instance
, 2);
5097 struct sipe_publication
*publication
=
5098 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "device"), key
);
5103 uri
= sip_uri_self(sipe_private
);
5104 doc
= g_strdup_printf(SIPE_PUB_XML_DEVICE
,
5106 publication
? publication
->version
: 0,
5109 "00:00:00+01:00", /* @TODO make timezone real*/
5120 * A service method - use
5121 * - send_publish_get_category_state_machine and
5122 * - send_publish_get_category_state_user instead.
5123 * Must be g_free'd after use.
5126 sipe_publish_get_category_state(struct sipe_core_private
*sipe_private
,
5127 gboolean is_user_state
)
5129 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
5130 int availability
= sipe_get_availability_by_status(sip
->status
, NULL
);
5131 guint instance
= is_user_state
? sipe_get_pub_instance(sipe_private
, SIPE_PUB_STATE_USER
) :
5132 sipe_get_pub_instance(sipe_private
, SIPE_PUB_STATE_MACHINE
);
5133 /* key is <category><instance><container> */
5134 gchar
*key_2
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 2);
5135 gchar
*key_3
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 3);
5136 struct sipe_publication
*publication_2
=
5137 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "state"), key_2
);
5138 struct sipe_publication
*publication_3
=
5139 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "state"), key_3
);
5144 if (publication_2
&& (publication_2
->availability
== availability
))
5146 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_state: state has NOT changed. Exiting.");
5147 return NULL
; /* nothing to update */
5150 return g_strdup_printf( is_user_state
? SIPE_PUB_XML_STATE_USER
: SIPE_PUB_XML_STATE_MACHINE
,
5152 publication_2
? publication_2
->version
: 0,
5155 publication_3
? publication_3
->version
: 0,
5160 * Only Busy and OOF calendar event are published.
5161 * Different instances are used for that.
5163 * Must be g_free'd after use.
5166 sipe_publish_get_category_state_calendar(struct sipe_core_private
*sipe_private
,
5167 struct sipe_cal_event
*event
,
5171 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
5172 gchar
*start_time_str
;
5173 int availability
= 0;
5176 guint instance
= (cal_satus
== SIPE_CAL_OOF
) ?
5177 sipe_get_pub_instance(sipe_private
, SIPE_PUB_STATE_CALENDAR_OOF
) :
5178 sipe_get_pub_instance(sipe_private
, SIPE_PUB_STATE_CALENDAR
);
5180 /* key is <category><instance><container> */
5181 gchar
*key_2
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 2);
5182 gchar
*key_3
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 3);
5183 struct sipe_publication
*publication_2
=
5184 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "state"), key_2
);
5185 struct sipe_publication
*publication_3
=
5186 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "state"), key_3
);
5191 if (!publication_3
&& !event
) { /* was nothing, have nothing, exiting */
5192 SIPE_DEBUG_INFO("sipe_publish_get_category_state_calendar: "
5193 "Exiting as no publication and no event for cal_satus:%d", cal_satus
);
5199 (publication_3
->availability
== availability
) &&
5200 sipe_strequal(publication_3
->cal_event_hash
, (tmp
= sipe_cal_event_hash(event
))))
5203 SIPE_DEBUG_INFO("sipe_publish_get_category_state_calendar: "
5204 "cal state has NOT changed for cal_satus:%d. Exiting.", cal_satus
);
5205 return NULL
; /* nothing to update */
5210 (event
->cal_status
== SIPE_CAL_BUSY
||
5211 event
->cal_status
== SIPE_CAL_OOF
))
5213 gchar
*availability_xml_str
= NULL
;
5214 gchar
*activity_xml_str
= NULL
;
5216 if (event
->cal_status
== SIPE_CAL_BUSY
) {
5217 availability_xml_str
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_AVAIL
, 6500);
5220 if (event
->cal_status
== SIPE_CAL_BUSY
&& event
->is_meeting
) {
5221 activity_xml_str
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY
,
5222 sipe_activity_map
[SIPE_ACTIVITY_IN_MEETING
].token
,
5223 "minAvailability=\"6500\"",
5224 "maxAvailability=\"8999\"");
5225 } else if (event
->cal_status
== SIPE_CAL_OOF
) {
5226 activity_xml_str
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY
,
5227 sipe_activity_map
[SIPE_ACTIVITY_OOF
].token
,
5228 "minAvailability=\"12000\"",
5231 start_time_str
= sipe_utils_time_to_str(event
->start_time
);
5233 res
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR
,
5235 publication_2
? publication_2
->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
: "",
5244 publication_3
? publication_3
->version
: 0,
5247 availability_xml_str
? availability_xml_str
: "",
5248 activity_xml_str
? activity_xml_str
: "",
5249 event
->subject
? event
->subject
: "",
5250 event
->location
? event
->location
: ""
5252 g_free(start_time_str
);
5253 g_free(availability_xml_str
);
5254 g_free(activity_xml_str
);
5257 else /* including !event, SIPE_CAL_FREE, SIPE_CAL_TENTATIVE */
5259 res
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_CLEAR
,
5261 publication_2
? publication_2
->version
: 0,
5264 publication_3
? publication_3
->version
: 0
5272 * Returns 'machineState' XML part for publication.
5273 * Must be g_free'd after use.
5276 sipe_publish_get_category_state_machine(struct sipe_core_private
*sipe_private
)
5278 return sipe_publish_get_category_state(sipe_private
, FALSE
);
5282 * Returns 'userState' XML part for publication.
5283 * Must be g_free'd after use.
5286 sipe_publish_get_category_state_user(struct sipe_core_private
*sipe_private
)
5288 return sipe_publish_get_category_state(sipe_private
, TRUE
);
5292 * Returns 'note' XML part for publication.
5293 * Must be g_free'd after use.
5295 * Protocol format for Note is plain text.
5297 * @param note a note in Sipe internal HTML format
5298 * @param note_type either personal or OOF
5301 sipe_publish_get_category_note(struct sipe_core_private
*sipe_private
,
5302 const char *note
, /* html */
5303 const char *note_type
,
5307 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
5308 guint instance
= sipe_strequal("OOF", note_type
) ? sipe_get_pub_instance(sipe_private
, SIPE_PUB_NOTE_OOF
) : 0;
5309 /* key is <category><instance><container> */
5310 gchar
*key_note_200
= g_strdup_printf("<%s><%u><%u>", "note", instance
, 200);
5311 gchar
*key_note_300
= g_strdup_printf("<%s><%u><%u>", "note", instance
, 300);
5312 gchar
*key_note_400
= g_strdup_printf("<%s><%u><%u>", "note", instance
, 400);
5314 struct sipe_publication
*publication_note_200
=
5315 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "note"), key_note_200
);
5316 struct sipe_publication
*publication_note_300
=
5317 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "note"), key_note_300
);
5318 struct sipe_publication
*publication_note_400
=
5319 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "note"), key_note_400
);
5321 char *tmp
= note
? sipe_backend_markup_strip_html(note
) : NULL
;
5322 char *n1
= tmp
? g_markup_escape_text(tmp
, -1) : NULL
;
5323 const char *n2
= publication_note_200
? publication_note_200
->note
: NULL
;
5324 char *res
, *tmp1
, *tmp2
, *tmp3
;
5325 char *start_time_attr
;
5326 char *end_time_attr
;
5330 g_free(key_note_200
);
5331 g_free(key_note_300
);
5332 g_free(key_note_400
);
5334 /* we even need to republish empty note */
5335 if (sipe_strequal(n1
, n2
))
5337 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_note: note has NOT changed. Exiting.");
5339 return NULL
; /* nothing to update */
5342 start_time_attr
= note_start
? g_strdup_printf(" startTime=\"%s\"", (tmp
= sipe_utils_time_to_str(note_start
))) : NULL
;
5345 end_time_attr
= note_end
? g_strdup_printf(" endTime=\"%s\"", (tmp
= sipe_utils_time_to_str(note_end
))) : NULL
;
5349 tmp1
= g_strdup_printf(SIPE_PUB_XML_NOTE
,
5352 publication_note_200
? publication_note_200
->version
: 0,
5354 start_time_attr
? start_time_attr
: "",
5355 end_time_attr
? end_time_attr
: "",
5358 tmp2
= g_strdup_printf(SIPE_PUB_XML_NOTE
,
5361 publication_note_300
? publication_note_300
->version
: 0,
5363 start_time_attr
? start_time_attr
: "",
5364 end_time_attr
? end_time_attr
: "",
5367 tmp3
= g_strdup_printf(SIPE_PUB_XML_NOTE
,
5370 publication_note_400
? publication_note_400
->version
: 0,
5372 start_time_attr
? start_time_attr
: "",
5373 end_time_attr
? end_time_attr
: "",
5376 tmp1
= g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR
,
5380 publication_note_200
? publication_note_200
->version
: 0,
5382 tmp2
= g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR
,
5386 publication_note_200
? publication_note_200
->version
: 0,
5388 tmp3
= g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR
,
5392 publication_note_200
? publication_note_200
->version
: 0,
5395 res
= g_strconcat(tmp1
, tmp2
, tmp3
, NULL
);
5397 g_free(start_time_attr
);
5398 g_free(end_time_attr
);
5408 * Returns 'calendarData' XML part with WorkingHours for publication.
5409 * Must be g_free'd after use.
5412 sipe_publish_get_category_cal_working_hours(struct sipe_core_private
*sipe_private
)
5414 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
5415 struct sipe_calendar
* cal
= sip
->cal
;
5417 /* key is <category><instance><container> */
5418 gchar
*key_cal_1
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 1);
5419 gchar
*key_cal_100
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 100);
5420 gchar
*key_cal_200
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 200);
5421 gchar
*key_cal_300
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 300);
5422 gchar
*key_cal_400
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 400);
5423 gchar
*key_cal_32000
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 32000);
5425 struct sipe_publication
*publication_cal_1
=
5426 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_1
);
5427 struct sipe_publication
*publication_cal_100
=
5428 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_100
);
5429 struct sipe_publication
*publication_cal_200
=
5430 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_200
);
5431 struct sipe_publication
*publication_cal_300
=
5432 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_300
);
5433 struct sipe_publication
*publication_cal_400
=
5434 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_400
);
5435 struct sipe_publication
*publication_cal_32000
=
5436 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_32000
);
5438 const char *n1
= cal
? cal
->working_hours_xml_str
: NULL
;
5439 const char *n2
= publication_cal_300
? publication_cal_300
->working_hours_xml_str
: NULL
;
5442 g_free(key_cal_100
);
5443 g_free(key_cal_200
);
5444 g_free(key_cal_300
);
5445 g_free(key_cal_400
);
5446 g_free(key_cal_32000
);
5448 if (!cal
|| is_empty(cal
->email
) || is_empty(cal
->working_hours_xml_str
)) {
5449 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_working_hours: no data to publish, exiting");
5453 if (sipe_strequal(n1
, n2
))
5455 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_working_hours: WorkingHours has NOT changed. Exiting.");
5456 return NULL
; /* nothing to update */
5459 return g_strdup_printf(SIPE_PUB_XML_WORKING_HOURS
,
5461 publication_cal_1
? publication_cal_1
->version
: 0,
5463 cal
->working_hours_xml_str
,
5465 publication_cal_100
? publication_cal_100
->version
: 0,
5467 publication_cal_200
? publication_cal_200
->version
: 0,
5469 cal
->working_hours_xml_str
,
5471 publication_cal_300
? publication_cal_300
->version
: 0,
5473 cal
->working_hours_xml_str
,
5474 /* 400 - Personal */
5475 publication_cal_400
? publication_cal_400
->version
: 0,
5477 cal
->working_hours_xml_str
,
5478 /* 32000 - Blocked */
5479 publication_cal_32000
? publication_cal_32000
->version
: 0
5484 * Returns 'calendarData' XML part with FreeBusy for publication.
5485 * Must be g_free'd after use.
5488 sipe_publish_get_category_cal_free_busy(struct sipe_core_private
*sipe_private
)
5490 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
5491 struct sipe_calendar
* cal
= sip
->cal
;
5492 guint cal_data_instance
= sipe_get_pub_instance(sipe_private
, SIPE_PUB_CALENDAR_DATA
);
5494 char *free_busy_base64
;
5499 /* key is <category><instance><container> */
5500 gchar
*key_cal_1
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 1);
5501 gchar
*key_cal_100
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 100);
5502 gchar
*key_cal_200
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 200);
5503 gchar
*key_cal_300
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 300);
5504 gchar
*key_cal_400
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 400);
5505 gchar
*key_cal_32000
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 32000);
5507 struct sipe_publication
*publication_cal_1
=
5508 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_1
);
5509 struct sipe_publication
*publication_cal_100
=
5510 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_100
);
5511 struct sipe_publication
*publication_cal_200
=
5512 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_200
);
5513 struct sipe_publication
*publication_cal_300
=
5514 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_300
);
5515 struct sipe_publication
*publication_cal_400
=
5516 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_400
);
5517 struct sipe_publication
*publication_cal_32000
=
5518 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_32000
);
5521 g_free(key_cal_100
);
5522 g_free(key_cal_200
);
5523 g_free(key_cal_300
);
5524 g_free(key_cal_400
);
5525 g_free(key_cal_32000
);
5527 if (!cal
|| is_empty(cal
->email
) || !cal
->fb_start
|| is_empty(cal
->free_busy
)) {
5528 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_free_busy: no data to publish, exiting");
5532 fb_start_str
= sipe_utils_time_to_str(cal
->fb_start
);
5533 free_busy_base64
= sipe_cal_get_freebusy_base64(cal
->free_busy
);
5535 st
= publication_cal_300
? publication_cal_300
->fb_start_str
: NULL
;
5536 fb
= publication_cal_300
? publication_cal_300
->free_busy_base64
: NULL
;
5538 /* we will rebuplish the same data to refresh publication time,
5539 * so if data from multiple sources, most recent will be choosen
5541 //if (sipe_strequal(st, fb_start_str) && sipe_strequal(fb, free_busy_base64))
5543 // SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_free_busy: FreeBusy has NOT changed. Exiting.");
5544 // g_free(fb_start_str);
5545 // g_free(free_busy_base64);
5546 // return NULL; /* nothing to update */
5549 res
= g_strdup_printf(SIPE_PUB_XML_FREE_BUSY
,
5552 publication_cal_1
? publication_cal_1
->version
: 0,
5555 publication_cal_100
? publication_cal_100
->version
: 0,
5558 publication_cal_200
? publication_cal_200
->version
: 0,
5564 publication_cal_300
? publication_cal_300
->version
: 0,
5568 /* 400 - Personal */
5570 publication_cal_400
? publication_cal_400
->version
: 0,
5574 /* 32000 - Blocked */
5576 publication_cal_32000
? publication_cal_32000
->version
: 0
5579 g_free(fb_start_str
);
5580 g_free(free_busy_base64
);
5584 static void send_presence_publish(struct sipe_core_private
*sipe_private
,
5585 const char *publications
)
5592 uri
= sip_uri_self(sipe_private
);
5593 doc
= g_strdup_printf(SIPE_SEND_PRESENCE
,
5597 tmp
= get_contact(sipe_private
);
5598 hdr
= g_strdup_printf("Contact: %s\r\n"
5599 "Content-Type: application/msrtc-category-publish+xml\r\n", tmp
);
5601 sip_transport_service(sipe_private
,
5605 process_send_presence_category_publish_response
);
5614 send_publish_category_initial(struct sipe_core_private
*sipe_private
)
5616 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
5617 gchar
*pub_device
= sipe_publish_get_category_device(sipe_private
);
5619 gchar
*publications
;
5621 g_free(sip
->status
);
5622 sip
->status
= g_strdup(SIPE_STATUS_ID_AVAILABLE
); /* our initial state */
5624 pub_machine
= sipe_publish_get_category_state_machine(sipe_private
);
5625 publications
= g_strdup_printf("%s%s",
5627 pub_machine
? pub_machine
: "");
5629 g_free(pub_machine
);
5631 send_presence_publish(sipe_private
, publications
);
5632 g_free(publications
);
5636 send_presence_category_publish(struct sipe_core_private
*sipe_private
)
5638 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
5639 gchar
*pub_state
= sipe_is_user_state(sipe_private
) ?
5640 sipe_publish_get_category_state_user(sipe_private
) :
5641 sipe_publish_get_category_state_machine(sipe_private
);
5642 gchar
*pub_note
= sipe_publish_get_category_note(sipe_private
,
5644 sip
->is_oof_note
? "OOF" : "personal",
5647 gchar
*publications
;
5649 if (!pub_state
&& !pub_note
) {
5650 SIPE_DEBUG_INFO_NOFORMAT("send_presence_category_publish: nothing has changed. Exiting.");
5654 publications
= g_strdup_printf("%s%s",
5655 pub_state
? pub_state
: "",
5656 pub_note
? pub_note
: "");
5661 send_presence_publish(sipe_private
, publications
);
5662 g_free(publications
);
5666 * Publishes self status
5667 * based on own calendar information.
5672 publish_calendar_status_self(struct sipe_core_private
*sipe_private
,
5673 SIPE_UNUSED_PARAMETER
void *unused
)
5675 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
5676 struct sipe_cal_event
* event
= NULL
;
5677 gchar
*pub_cal_working_hours
= NULL
;
5678 gchar
*pub_cal_free_busy
= NULL
;
5679 gchar
*pub_calendar
= NULL
;
5680 gchar
*pub_calendar2
= NULL
;
5681 gchar
*pub_oof_note
= NULL
;
5682 const gchar
*oof_note
;
5683 time_t oof_start
= 0;
5687 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self() no calendar data.");
5691 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self() started.");
5692 if (sip
->cal
->cal_events
) {
5693 event
= sipe_cal_get_event(sip
->cal
->cal_events
, time(NULL
));
5697 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self: current event is NULL");
5699 char *desc
= sipe_cal_event_describe(event
);
5700 SIPE_DEBUG_INFO("publish_calendar_status_self: current event is:\n%s", desc
? desc
: "");
5706 OOF publish, Busy clean
5708 OOF clean, Busy publish
5710 OOF clean, Busy clean
5712 if (event
&& event
->cal_status
== SIPE_CAL_OOF
) {
5713 pub_calendar
= sipe_publish_get_category_state_calendar(sipe_private
, event
, sip
->cal
->email
, SIPE_CAL_OOF
);
5714 pub_calendar2
= sipe_publish_get_category_state_calendar(sipe_private
, NULL
, sip
->cal
->email
, SIPE_CAL_BUSY
);
5715 } else if (event
&& event
->cal_status
== SIPE_CAL_BUSY
) {
5716 pub_calendar
= sipe_publish_get_category_state_calendar(sipe_private
, NULL
, sip
->cal
->email
, SIPE_CAL_OOF
);
5717 pub_calendar2
= sipe_publish_get_category_state_calendar(sipe_private
, event
, sip
->cal
->email
, SIPE_CAL_BUSY
);
5719 pub_calendar
= sipe_publish_get_category_state_calendar(sipe_private
, NULL
, sip
->cal
->email
, SIPE_CAL_OOF
);
5720 pub_calendar2
= sipe_publish_get_category_state_calendar(sipe_private
, NULL
, sip
->cal
->email
, SIPE_CAL_BUSY
);
5723 oof_note
= sipe_ews_get_oof_note(sip
->cal
);
5724 if (sipe_strequal("Scheduled", sip
->cal
->oof_state
)) {
5725 oof_start
= sip
->cal
->oof_start
;
5726 oof_end
= sip
->cal
->oof_end
;
5728 pub_oof_note
= sipe_publish_get_category_note(sipe_private
, oof_note
, "OOF", oof_start
, oof_end
);
5730 pub_cal_working_hours
= sipe_publish_get_category_cal_working_hours(sipe_private
);
5731 pub_cal_free_busy
= sipe_publish_get_category_cal_free_busy(sipe_private
);
5733 if (!pub_cal_working_hours
&& !pub_cal_free_busy
&& !pub_calendar
&& !pub_calendar2
&& !pub_oof_note
) {
5734 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self: nothing has changed.");
5736 gchar
*publications
= g_strdup_printf("%s%s%s%s%s",
5737 pub_cal_working_hours
? pub_cal_working_hours
: "",
5738 pub_cal_free_busy
? pub_cal_free_busy
: "",
5739 pub_calendar
? pub_calendar
: "",
5740 pub_calendar2
? pub_calendar2
: "",
5741 pub_oof_note
? pub_oof_note
: "");
5743 send_presence_publish(sipe_private
, publications
);
5744 g_free(publications
);
5747 g_free(pub_cal_working_hours
);
5748 g_free(pub_cal_free_busy
);
5749 g_free(pub_calendar
);
5750 g_free(pub_calendar2
);
5751 g_free(pub_oof_note
);
5753 /* repeat scheduling */
5754 sipe_sched_calendar_status_self_publish(sipe_private
, time(NULL
));
5757 static void send_presence_status(struct sipe_core_private
*sipe_private
,
5758 SIPE_UNUSED_PARAMETER
void *unused
)
5760 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
5761 PurpleStatus
* status
= purple_account_get_active_status(sip
->account
);
5763 if (!status
) return;
5765 SIPE_DEBUG_INFO("send_presence_status: status: %s (%s)",
5766 purple_status_get_id(status
) ? purple_status_get_id(status
) : "",
5767 sipe_is_user_state(sipe_private
) ? "USER" : "MACHINE");
5769 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
5770 send_presence_category_publish(sipe_private
);
5772 send_presence_soap(sipe_private
, FALSE
);
5776 static guint
sipe_ht_hash_nick(const char *nick
)
5778 char *lc
= g_utf8_strdown(nick
, -1);
5779 guint bucket
= g_str_hash(lc
);
5785 static gboolean
sipe_ht_equals_nick(const char *nick1
, const char *nick2
)
5787 char *nick1_norm
= NULL
;
5788 char *nick2_norm
= NULL
;
5791 if (nick1
== NULL
&& nick2
== NULL
) return TRUE
;
5792 if (nick1
== NULL
|| nick2
== NULL
||
5793 !g_utf8_validate(nick1
, -1, NULL
) ||
5794 !g_utf8_validate(nick2
, -1, NULL
)) return FALSE
;
5796 nick1_norm
= g_utf8_casefold(nick1
, -1);
5797 nick2_norm
= g_utf8_casefold(nick2
, -1);
5798 equal
= g_utf8_collate(nick1_norm
, nick2_norm
) == 0;
5805 /* temporary function */
5806 void sipe_purple_setup(struct sipe_core_public
*sipe_public
,
5807 PurpleConnection
*gc
)
5809 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA
;
5811 sip
->account
= purple_connection_get_account(gc
);
5814 struct sipe_core_public
*sipe_core_allocate(const gchar
*signin_name
,
5815 const gchar
*login_domain
,
5816 const gchar
*login_account
,
5817 const gchar
*password
,
5819 const gchar
*email_url
,
5820 const gchar
**errmsg
)
5822 struct sipe_core_private
*sipe_private
;
5823 struct sipe_account_data
*sip
;
5824 gchar
**user_domain
;
5826 SIPE_DEBUG_INFO("sipe_core_allocate: signin_name '%s'", signin_name
);
5828 /* ensure that sign-in name doesn't contain invalid characters */
5829 if (strpbrk(signin_name
, "\t\v\r\n") != NULL
) {
5830 *errmsg
= _("SIP Exchange user name contains invalid characters");
5834 /* ensure that sign-in name format is name@domain */
5835 if (!strchr(signin_name
, '@') ||
5836 g_str_has_prefix(signin_name
, "@") ||
5837 g_str_has_suffix(signin_name
, "@")) {
5838 *errmsg
= _("User name should be a valid SIP URI\nExample: user@company.com");
5842 /* ensure that email format is name@domain (if provided) */
5843 if (!is_empty(email
) &&
5844 (!strchr(email
, '@') ||
5845 g_str_has_prefix(email
, "@") ||
5846 g_str_has_suffix(email
, "@")))
5848 *errmsg
= _("Email address should be valid if provided\nExample: user@company.com");
5852 /* ensure that user name doesn't contain spaces */
5853 user_domain
= g_strsplit(signin_name
, "@", 2);
5854 SIPE_DEBUG_INFO("sipe_core_allocate: user '%s' domain '%s'", user_domain
[0], user_domain
[1]);
5855 if (strchr(user_domain
[0], ' ') != NULL
) {
5856 g_strfreev(user_domain
);
5857 *errmsg
= _("SIP Exchange user name contains whitespace");
5861 /* ensure that email_url is in proper format if enabled (if provided).
5862 * Example (Exchange): https://server.company.com/EWS/Exchange.asmx
5863 * Example (Domino) : https://[domino_server]/[mail_database_name].nsf
5865 if (!is_empty(email_url
)) {
5866 char *tmp
= g_ascii_strdown(email_url
, -1);
5867 if (!g_str_has_prefix(tmp
, "https://"))
5870 g_strfreev(user_domain
);
5871 *errmsg
= _("Email services URL should be valid if provided\n"
5872 "Example: https://exchange.corp.com/EWS/Exchange.asmx\n"
5873 "Example: https://domino.corp.com/maildatabase.nsf");
5879 sipe_private
= g_new0(struct sipe_core_private
, 1);
5880 sipe_private
->temporary
= sip
= g_new0(struct sipe_account_data
, 1);
5881 sip
->subscribed_buddies
= FALSE
;
5882 sip
->initial_state_published
= FALSE
;
5883 sipe_private
->username
= g_strdup(signin_name
);
5884 sip
->email
= is_empty(email
) ? g_strdup(signin_name
) : g_strdup(email
);
5885 sip
->authdomain
= is_empty(login_domain
) ? NULL
: g_strdup(login_domain
);
5886 sip
->authuser
= is_empty(login_account
) ? NULL
: g_strdup(login_account
);
5887 sip
->password
= g_strdup(password
);
5888 sipe_private
->public.sip_name
= g_strdup(user_domain
[0]);
5889 sipe_private
->public.sip_domain
= g_strdup(user_domain
[1]);
5890 g_strfreev(user_domain
);
5892 sipe_private
->buddies
= g_hash_table_new((GHashFunc
)sipe_ht_hash_nick
, (GEqualFunc
)sipe_ht_equals_nick
);
5893 sip
->our_publications
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
5894 g_free
, (GDestroyNotify
)g_hash_table_destroy
);
5895 sipe_subscriptions_init(sipe_private
);
5896 sip
->status
= g_strdup(SIPE_STATUS_ID_UNKNOWN
);
5898 return((struct sipe_core_public
*)sipe_private
);
5901 void sipe_connection_cleanup(struct sipe_core_private
*sipe_private
)
5903 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
5905 g_free(sipe_private
->epid
);
5906 sipe_private
->epid
= NULL
;
5908 sip_transport_disconnect(sipe_private
);
5910 sipe_schedule_cancel_all(sipe_private
);
5912 if (sip
->allow_events
) {
5913 GSList
*entry
= sip
->allow_events
;
5915 g_free(entry
->data
);
5916 entry
= entry
->next
;
5919 g_slist_free(sip
->allow_events
);
5921 if (sip
->containers
) {
5922 GSList
*entry
= sip
->containers
;
5924 free_container((struct sipe_container
*)entry
->data
);
5925 entry
= entry
->next
;
5928 g_slist_free(sip
->containers
);
5930 if (sipe_private
->contact
)
5931 g_free(sipe_private
->contact
);
5932 sipe_private
->contact
= NULL
;
5934 g_free(sip
->regcallid
);
5935 sip
->regcallid
= NULL
;
5937 if (sipe_private
->focus_factory_uri
)
5938 g_free(sipe_private
->focus_factory_uri
);
5939 sipe_private
->focus_factory_uri
= NULL
;
5942 sipe_cal_calendar_free(sip
->cal
);
5948 * A callback for g_hash_table_foreach_remove
5950 static gboolean
sipe_buddy_remove(SIPE_UNUSED_PARAMETER gpointer key
, gpointer buddy
,
5951 SIPE_UNUSED_PARAMETER gpointer user_data
)
5953 sipe_free_buddy((struct sipe_buddy
*) buddy
);
5955 /* We must return TRUE as the key/value have already been deleted */
5959 void sipe_buddy_free_all(struct sipe_core_private
*sipe_private
)
5961 g_hash_table_foreach_steal(sipe_private
->buddies
, sipe_buddy_remove
, NULL
);
5964 static void sipe_searchresults_im_buddy(PurpleConnection
*gc
, GList
*row
,
5965 SIPE_UNUSED_PARAMETER
void *user_data
)
5967 PurpleAccount
*acct
= purple_connection_get_account(gc
);
5968 char *id
= sip_uri_from_name((gchar
*)g_list_nth_data(row
, 0));
5969 PurpleConversation
*conv
= purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM
, id
, acct
);
5971 conv
= purple_conversation_new(PURPLE_CONV_TYPE_IM
, acct
, id
);
5972 purple_conversation_present(conv
);
5976 static void sipe_searchresults_add_buddy(PurpleConnection
*gc
, GList
*row
,
5977 SIPE_UNUSED_PARAMETER
void *user_data
)
5980 purple_blist_request_add_buddy(purple_connection_get_account(gc
),
5981 g_list_nth_data(row
, 0), _("Other Contacts"), g_list_nth_data(row
, 1));
5984 static gboolean
process_search_contact_response(struct sipe_core_private
*sipe_private
,
5986 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
5988 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
5989 PurpleNotifySearchResults
*results
;
5990 PurpleNotifySearchColumn
*column
;
5991 sipe_xml
*searchResults
;
5992 const sipe_xml
*mrow
;
5993 int match_count
= 0;
5994 gboolean more
= FALSE
;
5997 SIPE_DEBUG_INFO("process_search_contact_response: body:\n%s", msg
->body
? msg
->body
: "");
5999 searchResults
= sipe_xml_parse(msg
->body
, msg
->bodylen
);
6000 if (!searchResults
) {
6001 SIPE_DEBUG_INFO_NOFORMAT("process_search_contact_response: no parseable searchResults");
6005 results
= purple_notify_searchresults_new();
6007 if (results
== NULL
) {
6008 SIPE_DEBUG_ERROR_NOFORMAT("purple_parse_searchreply: Unable to display the search results.");
6009 purple_notify_error(sip
->gc
, NULL
, _("Unable to display the search results"), NULL
);
6011 sipe_xml_free(searchResults
);
6015 column
= purple_notify_searchresults_column_new(_("User name"));
6016 purple_notify_searchresults_column_add(results
, column
);
6018 column
= purple_notify_searchresults_column_new(_("Name"));
6019 purple_notify_searchresults_column_add(results
, column
);
6021 column
= purple_notify_searchresults_column_new(_("Company"));
6022 purple_notify_searchresults_column_add(results
, column
);
6024 column
= purple_notify_searchresults_column_new(_("Country"));
6025 purple_notify_searchresults_column_add(results
, column
);
6027 column
= purple_notify_searchresults_column_new(_("Email"));
6028 purple_notify_searchresults_column_add(results
, column
);
6030 for (mrow
= sipe_xml_child(searchResults
, "Body/Array/row"); mrow
; mrow
= sipe_xml_twin(mrow
)) {
6033 gchar
**uri_parts
= g_strsplit(sipe_xml_attribute(mrow
, "uri"), ":", 2);
6034 row
= g_list_append(row
, g_strdup(uri_parts
[1]));
6035 g_strfreev(uri_parts
);
6037 row
= g_list_append(row
, g_strdup(sipe_xml_attribute(mrow
, "displayName")));
6038 row
= g_list_append(row
, g_strdup(sipe_xml_attribute(mrow
, "company")));
6039 row
= g_list_append(row
, g_strdup(sipe_xml_attribute(mrow
, "country")));
6040 row
= g_list_append(row
, g_strdup(sipe_xml_attribute(mrow
, "email")));
6042 purple_notify_searchresults_row_add(results
, row
);
6046 if ((mrow
= sipe_xml_child(searchResults
, "Body/directorySearch/moreAvailable")) != NULL
) {
6047 char *data
= sipe_xml_data(mrow
);
6048 more
= (g_strcasecmp(data
, "true") == 0);
6052 secondary
= g_strdup_printf(
6053 dngettext(PACKAGE_NAME
,
6054 "Found %d contact%s:",
6055 "Found %d contacts%s:", match_count
),
6056 match_count
, more
? _(" (more matched your query)") : "");
6058 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_IM
, sipe_searchresults_im_buddy
);
6059 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_ADD
, sipe_searchresults_add_buddy
);
6060 purple_notify_searchresults(sip
->gc
, NULL
, NULL
, secondary
, results
, NULL
, NULL
);
6063 sipe_xml_free(searchResults
);
6067 void sipe_search_contact_with_cb(PurpleConnection
*gc
, PurpleRequestFields
*fields
)
6069 GList
*entries
= purple_request_field_group_get_fields(purple_request_fields_get_groups(fields
)->data
);
6070 gchar
**attrs
= g_new(gchar
*, g_list_length(entries
) + 1);
6076 PurpleRequestField
*field
= entries
->data
;
6077 const char *id
= purple_request_field_get_id(field
);
6078 const char *value
= purple_request_field_string_get_value(field
);
6080 SIPE_DEBUG_INFO("sipe_search_contact_with_cb: %s = '%s'", id
, value
? value
: "");
6082 if (value
!= NULL
) attrs
[i
++] = g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW
, id
, value
);
6083 } while ((entries
= g_list_next(entries
)) != NULL
);
6087 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
6088 gchar
*domain_uri
= sip_uri_from_name(sipe_private
->public.sip_domain
);
6089 gchar
*query
= g_strjoinv(NULL
, attrs
);
6090 gchar
*body
= g_strdup_printf(SIPE_SOAP_SEARCH_CONTACT
, 100, query
);
6091 SIPE_DEBUG_INFO("sipe_search_contact_with_cb: body:\n%s", body
? body
: "");
6092 send_soap_request_with_cb(sipe_private
, domain_uri
, body
,
6093 process_search_contact_response
, NULL
);
6102 static void sipe_publish_get_cat_state_user_to_clear(SIPE_UNUSED_PARAMETER
const char *name
,
6106 struct sipe_publication
*publication
= value
;
6108 g_string_append_printf( str
,
6109 SIPE_PUB_XML_PUBLICATION_CLEAR
,
6110 publication
->category
,
6111 publication
->instance
,
6112 publication
->container
,
6113 publication
->version
,
6117 void sipe_core_reset_status(struct sipe_core_public
*sipe_public
)
6119 struct sipe_core_private
*sipe_private
= SIPE_CORE_PRIVATE
;
6120 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA
;
6121 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) /* 2007+ */
6123 GString
* str
= g_string_new(NULL
);
6124 gchar
*publications
;
6126 if (!sip
->user_state_publications
|| g_hash_table_size(sip
->user_state_publications
) == 0) {
6127 SIPE_DEBUG_INFO_NOFORMAT("sipe_reset_status: no userState publications, exiting.");
6131 g_hash_table_foreach(sip
->user_state_publications
, (GHFunc
)sipe_publish_get_cat_state_user_to_clear
, str
);
6132 publications
= g_string_free(str
, FALSE
);
6134 send_presence_publish(sipe_private
, publications
);
6135 g_free(publications
);
6139 send_presence_soap0(sipe_private
, FALSE
, TRUE
);
6143 /** for Access levels menu */
6144 #define INDENT_FMT " %s"
6146 /** Member is directly placed to access level container.
6147 * For example SIP URI of user is in the container.
6149 #define INDENT_MARKED_FMT "* %s"
6151 /** Member is indirectly belong to access level container.
6152 * For example 'sameEnterprise' is in the container and user
6153 * belongs to that same enterprise.
6155 #define INDENT_MARKED_INHERITED_FMT "= %s"
6157 GSList
*sipe_core_buddy_info(struct sipe_core_public
*sipe_public
,
6159 const gchar
*status_name
,
6162 struct sipe_core_private
*sipe_private
= SIPE_CORE_PRIVATE
;
6164 gboolean is_oof_note
= FALSE
;
6165 gchar
*activity
= NULL
;
6166 gchar
*calendar
= NULL
;
6167 gchar
*meeting_subject
= NULL
;
6168 gchar
*meeting_location
= NULL
;
6169 gchar
*access_text
= NULL
;
6170 GSList
*info
= NULL
;
6172 #define SIPE_ADD_BUDDY_INFO(l, t) \
6174 struct sipe_buddy_info *sbi = g_malloc(sizeof(struct sipe_buddy_info)); \
6177 info = g_slist_append(info, sbi); \
6180 if (sipe_public
) { //happens on pidgin exit
6181 struct sipe_buddy
*sbuddy
= g_hash_table_lookup(sipe_private
->buddies
, name
);
6183 note
= sbuddy
->note
;
6184 is_oof_note
= sbuddy
->is_oof_note
;
6185 activity
= sbuddy
->activity
;
6186 calendar
= sipe_cal_get_description(sbuddy
);
6187 meeting_subject
= sbuddy
->meeting_subject
;
6188 meeting_location
= sbuddy
->meeting_location
;
6190 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
6191 gboolean is_group_access
= FALSE
;
6192 const int container_id
= sipe_find_access_level(sipe_private
, "user", sipe_get_no_sip_uri(name
), &is_group_access
);
6193 const char *access_level
= sipe_get_access_level_name(container_id
);
6194 access_text
= is_group_access
?
6195 g_strdup(access_level
) :
6196 g_strdup_printf(INDENT_MARKED_FMT
, access_level
);
6203 gchar
*status_str
= g_strdup(activity
? activity
: status_name
);
6205 SIPE_ADD_BUDDY_INFO(_("Status"), status_str
);
6207 if (is_online
&& !is_empty(calendar
))
6209 SIPE_ADD_BUDDY_INFO(_("Calendar"), calendar
);
6213 if (!is_empty(meeting_location
))
6215 SIPE_ADD_BUDDY_INFO(_("Meeting in"), g_strdup(meeting_location
));
6217 if (!is_empty(meeting_subject
))
6219 SIPE_ADD_BUDDY_INFO(_("Meeting about"), g_strdup(meeting_subject
));
6223 SIPE_DEBUG_INFO("sipe_tooltip_text: %s note: '%s'", name
, note
);
6224 SIPE_ADD_BUDDY_INFO(is_oof_note
? _("Out of office note") : _("Note"),
6225 g_strdup_printf("<i>%s</i>", note
));
6228 SIPE_ADD_BUDDY_INFO(_("Access level"), access_text
);
6234 static PurpleBuddy
*
6235 purple_blist_add_buddy_clone(PurpleGroup
* group
, PurpleBuddy
* buddy
)
6238 const gchar
*server_alias
, *email
;
6239 const PurpleStatus
*status
= purple_presence_get_active_status(purple_buddy_get_presence(buddy
));
6241 clone
= purple_buddy_new(buddy
->account
, buddy
->name
, buddy
->alias
);
6243 purple_blist_add_buddy(clone
, NULL
, group
, NULL
);
6245 server_alias
= purple_buddy_get_server_alias(buddy
);
6247 purple_blist_server_alias_buddy(clone
, server_alias
);
6250 email
= purple_blist_node_get_string(&buddy
->node
, EMAIL_PROP
);
6252 purple_blist_node_set_string(&clone
->node
, EMAIL_PROP
, email
);
6255 purple_presence_set_status_active(purple_buddy_get_presence(clone
), purple_status_get_id(status
), TRUE
);
6257 purple_prpl_got_user_status(clone
->account
, clone
->name
, purple_status_get_id(status
), NULL
);
6262 sipe_buddy_menu_copy_to_cb(PurpleBlistNode
*node
, const char *group_name
)
6264 PurpleBuddy
*buddy
, *b
;
6265 PurpleConnection
*gc
;
6266 PurpleGroup
* group
= purple_find_group(group_name
);
6268 g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node
));
6270 buddy
= (PurpleBuddy
*)node
;
6272 SIPE_DEBUG_INFO("sipe_buddy_menu_copy_to_cb: copying %s to %s", buddy
->name
, group_name
);
6273 gc
= purple_account_get_connection(buddy
->account
);
6275 b
= purple_find_buddy_in_group(buddy
->account
, buddy
->name
, group
);
6277 purple_blist_add_buddy_clone(group
, buddy
);
6280 sipe_group_buddy(gc
, buddy
->name
, NULL
, group_name
);
6284 sipe_buddy_menu_chat_new_cb(PurpleBuddy
*buddy
)
6286 struct sipe_core_private
*sipe_private
= PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE
;
6288 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_new_cb: buddy->name=%s", buddy
->name
);
6290 /* 2007+ conference */
6291 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
))
6293 sipe_conf_add(sipe_private
, buddy
->name
);
6295 else /* 2005- multiparty chat */
6297 gchar
*self
= sip_uri_self(sipe_private
);
6298 struct sip_session
*session
;
6300 session
= sipe_session_add_chat(sipe_private
);
6301 session
->chat_title
= sipe_chat_get_name(session
->callid
);
6302 session
->roster_manager
= g_strdup(self
);
6304 session
->backend_session
= sipe_backend_chat_create(SIPE_CORE_PUBLIC
,
6306 session
->chat_title
,
6309 sipe_backend_chat_add(session
->backend_session
,
6312 sipe_invite(sipe_private
, session
, buddy
->name
, NULL
, NULL
, NULL
, FALSE
);
6319 * For 2007+ conference only.
6322 sipe_buddy_menu_chat_make_leader_cb(PurpleBuddy
*buddy
, const char *chat_title
)
6324 struct sipe_core_private
*sipe_private
= PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE
;
6325 struct sip_session
*session
;
6327 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_make_leader_cb: buddy->name=%s", buddy
->name
);
6328 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_make_leader_cb: chat_title=%s", chat_title
);
6330 session
= sipe_session_find_chat_by_title(sipe_private
, chat_title
);
6332 sipe_conf_modify_user_role(sipe_private
, session
, buddy
->name
);
6336 * For 2007+ conference only.
6339 sipe_buddy_menu_chat_remove_cb(PurpleBuddy
*buddy
, const char *chat_title
)
6341 struct sipe_core_private
*sipe_private
= PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE
;
6342 struct sip_session
*session
;
6344 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_remove_cb: buddy->name=%s", buddy
->name
);
6345 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_remove_cb: chat_title=%s", chat_title
);
6347 session
= sipe_session_find_chat_by_title(sipe_private
, chat_title
);
6349 sipe_conf_delete_user(sipe_private
, session
, buddy
->name
);
6353 sipe_buddy_menu_chat_invite_cb(PurpleBuddy
*buddy
, char *chat_title
)
6355 struct sipe_core_private
*sipe_private
= PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE
;
6356 struct sip_session
*session
;
6358 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_invite_cb: buddy->name=%s", buddy
->name
);
6359 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_invite_cb: chat_title=%s", chat_title
);
6361 session
= sipe_session_find_chat_by_title(sipe_private
, chat_title
);
6363 sipe_invite_to_chat(sipe_private
, session
, buddy
->name
);
6367 sipe_buddy_menu_make_call_cb(PurpleBuddy
*buddy
, const char *phone
)
6369 struct sipe_core_private
*sipe_private
= PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE
;
6371 SIPE_DEBUG_INFO("sipe_buddy_menu_make_call_cb: buddy->name=%s", buddy
->name
);
6373 char *tel_uri
= sip_to_tel_uri(phone
);
6375 SIPE_DEBUG_INFO("sipe_buddy_menu_make_call_cb: going to call number: %s", tel_uri
? tel_uri
: "");
6376 sip_csta_make_call(sipe_private
, tel_uri
);
6383 sipe_buddy_menu_access_level_help_cb(PurpleBuddy
*buddy
)
6385 /** Translators: replace with URL to localized page
6386 * If it doesn't exist copy the original URL */
6387 purple_notify_uri(buddy
->account
->gc
, _("https://sourceforge.net/apps/mediawiki/sipe/index.php?title=Access_Levels"));
6391 sipe_buddy_menu_send_email_cb(PurpleBuddy
*buddy
)
6394 SIPE_DEBUG_INFO("sipe_buddy_menu_send_email_cb: buddy->name=%s", buddy
->name
);
6396 email
= purple_blist_node_get_string(&buddy
->node
, EMAIL_PROP
);
6399 char *command_line
= g_strdup_printf(
6405 " mailto:%s", email
);
6406 SIPE_DEBUG_INFO("sipe_buddy_menu_send_email_cb: going to call email client: %s", command_line
);
6408 g_spawn_command_line_async(command_line
, NULL
);
6409 g_free(command_line
);
6413 SIPE_DEBUG_INFO("sipe_buddy_menu_send_email_cb: no email address stored for buddy=%s", buddy
->name
);
6418 sipe_buddy_menu_access_level_cb(PurpleBuddy
*buddy
,
6419 struct sipe_container
*container
)
6421 struct sipe_core_private
*sipe_private
= PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE
;
6422 struct sipe_container_member
*member
;
6424 if (!container
|| !container
->members
) return;
6426 member
= ((struct sipe_container_member
*)container
->members
->data
);
6428 if (!member
->type
) return;
6430 SIPE_DEBUG_INFO("sipe_buddy_menu_access_level_cb: container->id=%d, member->type=%s, member->value=%s",
6431 container
->id
, member
->type
, member
->value
? member
->value
: "");
6433 sipe_change_access_level(sipe_private
, container
->id
, member
->type
, member
->value
);
6437 sipe_get_access_control_menu(struct sipe_core_private
*sipe_private
,
6441 * A menu which appear when right-clicking on buddy in contact list.
6444 sipe_buddy_menu(PurpleBuddy
*buddy
)
6446 PurpleBlistNode
*g_node
;
6447 PurpleGroup
*group
, *gr_parent
;
6448 PurpleMenuAction
*act
;
6450 GList
*menu_groups
= NULL
;
6451 struct sipe_core_private
*sipe_private
= PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE
;
6452 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
6455 const char *phone_disp_str
;
6456 gchar
*self
= sip_uri_self(sipe_private
);
6458 SIPE_SESSION_FOREACH
{
6459 if (!sipe_strcase_equal(self
, buddy
->name
) && session
->chat_title
&& session
->backend_session
)
6461 if (sipe_backend_chat_find(session
->backend_session
, buddy
->name
))
6463 gboolean conf_op
= sipe_backend_chat_is_operator(session
->backend_session
, self
);
6465 if (session
->focus_uri
6466 && !sipe_backend_chat_is_operator(session
->backend_session
, buddy
->name
) /* Not conf OP */
6467 && conf_op
) /* We are a conf OP */
6469 gchar
*label
= g_strdup_printf(_("Make leader of '%s'"), session
->chat_title
);
6470 act
= purple_menu_action_new(label
,
6471 PURPLE_CALLBACK(sipe_buddy_menu_chat_make_leader_cb
),
6472 session
->chat_title
, NULL
);
6474 menu
= g_list_prepend(menu
, act
);
6477 if (session
->focus_uri
6478 && conf_op
) /* We are a conf OP */
6480 gchar
*label
= g_strdup_printf(_("Remove from '%s'"), session
->chat_title
);
6481 act
= purple_menu_action_new(label
,
6482 PURPLE_CALLBACK(sipe_buddy_menu_chat_remove_cb
),
6483 session
->chat_title
, NULL
);
6485 menu
= g_list_prepend(menu
, act
);
6490 if (!session
->focus_uri
6491 || (session
->focus_uri
&& !session
->locked
))
6493 gchar
*label
= g_strdup_printf(_("Invite to '%s'"), session
->chat_title
);
6494 act
= purple_menu_action_new(label
,
6495 PURPLE_CALLBACK(sipe_buddy_menu_chat_invite_cb
),
6496 session
->chat_title
, NULL
);
6498 menu
= g_list_prepend(menu
, act
);
6502 } SIPE_SESSION_FOREACH_END
;
6504 act
= purple_menu_action_new(_("New chat"),
6505 PURPLE_CALLBACK(sipe_buddy_menu_chat_new_cb
),
6507 menu
= g_list_prepend(menu
, act
);
6509 if (sip
->csta
&& !sip
->csta
->line_status
) {
6512 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_PROP
);
6513 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_DISPLAY_PROP
);
6515 gchar
*label
= g_strdup_printf(_("Work %s"),
6516 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
6517 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
6521 menu
= g_list_prepend(menu
, act
);
6525 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_MOBILE_PROP
);
6526 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_MOBILE_DISPLAY_PROP
);
6528 gchar
*label
= g_strdup_printf(_("Mobile %s"),
6529 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
6530 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
6534 menu
= g_list_prepend(menu
, act
);
6538 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_HOME_PROP
);
6539 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_HOME_DISPLAY_PROP
);
6541 gchar
*label
= g_strdup_printf(_("Home %s"),
6542 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
6543 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
6547 menu
= g_list_prepend(menu
, act
);
6551 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_OTHER_PROP
);
6552 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_OTHER_DISPLAY_PROP
);
6554 gchar
*label
= g_strdup_printf(_("Other %s"),
6555 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
6556 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
6560 menu
= g_list_prepend(menu
, act
);
6564 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_CUSTOM1_PROP
);
6565 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_CUSTOM1_DISPLAY_PROP
);
6567 gchar
*label
= g_strdup_printf(_("Custom1 %s"),
6568 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
6569 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
6573 menu
= g_list_prepend(menu
, act
);
6577 email
= purple_blist_node_get_string(&buddy
->node
, EMAIL_PROP
);
6579 act
= purple_menu_action_new(_("Send email..."),
6580 PURPLE_CALLBACK(sipe_buddy_menu_send_email_cb
),
6582 menu
= g_list_prepend(menu
, act
);
6586 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
6587 GList
*menu_access_levels
= sipe_get_access_control_menu(sipe_private
, buddy
->name
);
6589 act
= purple_menu_action_new(_("Access level"),
6591 NULL
, menu_access_levels
);
6592 menu
= g_list_prepend(menu
, act
);
6596 gr_parent
= purple_buddy_get_group(buddy
);
6597 for (g_node
= purple_blist_get_root(); g_node
; g_node
= g_node
->next
) {
6598 if (g_node
->type
!= PURPLE_BLIST_GROUP_NODE
)
6601 group
= (PurpleGroup
*)g_node
;
6602 if (group
== gr_parent
)
6605 if (purple_find_buddy_in_group(buddy
->account
, buddy
->name
, group
))
6608 act
= purple_menu_action_new(purple_group_get_name(group
),
6609 PURPLE_CALLBACK(sipe_buddy_menu_copy_to_cb
),
6611 menu_groups
= g_list_prepend(menu_groups
, act
);
6613 menu_groups
= g_list_reverse(menu_groups
);
6615 act
= purple_menu_action_new(_("Copy to"),
6618 menu
= g_list_prepend(menu
, act
);
6620 menu
= g_list_reverse(menu
);
6627 sipe_ask_access_domain_cb(PurpleConnection
*gc
, PurpleRequestFields
*fields
)
6629 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
6630 const char *domain
= purple_request_fields_get_string(fields
, "access_domain");
6631 int index
= purple_request_fields_get_choice(fields
, "container_id");
6632 /* move Blocked first */
6633 int i
= (index
== 4) ? 0 : index
+ 1;
6634 int container_id
= containers
[i
];
6636 SIPE_DEBUG_INFO("sipe_ask_access_domain_cb: domain=%s, container_id=(%d)%d", domain
? domain
: "", index
, container_id
);
6638 sipe_change_access_level(sipe_private
, container_id
, "domain", domain
);
6642 sipe_ask_access_domain(struct sipe_core_private
*sipe_private
)
6644 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
6645 PurpleAccount
*account
= sip
->account
;
6646 PurpleConnection
*gc
= sip
->gc
;
6647 PurpleRequestFields
*fields
;
6648 PurpleRequestFieldGroup
*g
;
6649 PurpleRequestField
*f
;
6651 fields
= purple_request_fields_new();
6653 g
= purple_request_field_group_new(NULL
);
6654 f
= purple_request_field_string_new("access_domain", _("Domain"), "partner-company.com", FALSE
);
6655 purple_request_field_set_required(f
, TRUE
);
6656 purple_request_field_group_add_field(g
, f
);
6658 f
= purple_request_field_choice_new("container_id", _("Access level"), 0);
6659 purple_request_field_choice_add(f
, _("Personal")); /* index 0 */
6660 purple_request_field_choice_add(f
, _("Team"));
6661 purple_request_field_choice_add(f
, _("Company"));
6662 purple_request_field_choice_add(f
, _("Public"));
6663 purple_request_field_choice_add(f
, _("Blocked")); /* index 4 */
6664 purple_request_field_choice_set_default_value(f
, 3); /* index */
6665 purple_request_field_set_required(f
, TRUE
);
6666 purple_request_field_group_add_field(g
, f
);
6668 purple_request_fields_add_group(fields
, g
);
6670 purple_request_fields(gc
, _("Add new domain"),
6671 _("Add new domain"), NULL
, fields
,
6672 _("Add"), G_CALLBACK(sipe_ask_access_domain_cb
),
6674 account
, NULL
, NULL
, gc
);
6678 sipe_buddy_menu_access_level_add_domain_cb(PurpleBuddy
*buddy
)
6680 sipe_ask_access_domain(PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE
);
6684 sipe_get_access_levels_menu(struct sipe_core_private
*sipe_private
,
6685 const char* member_type
,
6686 const char* member_value
,
6687 const gboolean extra_menu
)
6689 GList
*menu_access_levels
= NULL
;
6692 PurpleMenuAction
*act
;
6693 struct sipe_container
*container
;
6694 struct sipe_container_member
*member
;
6695 gboolean is_group_access
= FALSE
;
6696 int container_id
= sipe_find_access_level(sipe_private
, member_type
, member_value
, &is_group_access
);
6698 for (i
= 1; i
<= CONTAINERS_LEN
; i
++) {
6699 /* to put Blocked level last in menu list.
6700 * Blocked should remaim in the first place in the containers[] array.
6702 unsigned int j
= (i
== CONTAINERS_LEN
) ? 0 : i
;
6703 const char *acc_level_name
= sipe_get_access_level_name(containers
[j
]);
6705 container
= g_new0(struct sipe_container
, 1);
6706 member
= g_new0(struct sipe_container_member
, 1);
6707 container
->id
= containers
[j
];
6708 container
->members
= g_slist_append(container
->members
, member
);
6709 member
->type
= g_strdup(member_type
);
6710 member
->value
= g_strdup(member_value
);
6712 /* current container/access level */
6713 if (((int)containers
[j
]) == container_id
) {
6714 menu_name
= is_group_access
?
6715 g_strdup_printf(INDENT_MARKED_INHERITED_FMT
, acc_level_name
) :
6716 g_strdup_printf(INDENT_MARKED_FMT
, acc_level_name
);
6718 menu_name
= g_strdup_printf(INDENT_FMT
, acc_level_name
);
6721 act
= purple_menu_action_new(menu_name
,
6722 PURPLE_CALLBACK(sipe_buddy_menu_access_level_cb
),
6725 menu_access_levels
= g_list_prepend(menu_access_levels
, act
);
6728 if (extra_menu
&& (container_id
>= 0)) {
6730 act
= purple_menu_action_new(" --------------", NULL
, NULL
, NULL
);
6731 menu_access_levels
= g_list_prepend(menu_access_levels
, act
);
6733 if (!is_group_access
) {
6734 container
= g_new0(struct sipe_container
, 1);
6735 member
= g_new0(struct sipe_container_member
, 1);
6737 container
->members
= g_slist_append(container
->members
, member
);
6738 member
->type
= g_strdup(member_type
);
6739 member
->value
= g_strdup(member_value
);
6741 /* Translators: remove (clear) previously assigned access level */
6742 menu_name
= g_strdup_printf(INDENT_FMT
, _("Unspecify"));
6743 act
= purple_menu_action_new(menu_name
,
6744 PURPLE_CALLBACK(sipe_buddy_menu_access_level_cb
),
6747 menu_access_levels
= g_list_prepend(menu_access_levels
, act
);
6751 menu_access_levels
= g_list_reverse(menu_access_levels
);
6752 return menu_access_levels
;
6756 sipe_get_access_groups_menu(struct sipe_core_private
*sipe_private
)
6758 GList
*menu_access_groups
= NULL
;
6759 PurpleMenuAction
*act
;
6760 GSList
*access_domains
= NULL
;
6765 act
= purple_menu_action_new(_("People in my company"),
6767 NULL
, sipe_get_access_levels_menu(sipe_private
, "sameEnterprise", NULL
, FALSE
));
6768 menu_access_groups
= g_list_prepend(menu_access_groups
, act
);
6770 /* this is original name, don't edit */
6771 act
= purple_menu_action_new(_("People in domains connected with my company"),
6773 NULL
, sipe_get_access_levels_menu(sipe_private
, "federated", NULL
, FALSE
));
6774 menu_access_groups
= g_list_prepend(menu_access_groups
, act
);
6776 act
= purple_menu_action_new(_("People in public domains"),
6778 NULL
, sipe_get_access_levels_menu(sipe_private
, "publicCloud", NULL
, TRUE
));
6779 menu_access_groups
= g_list_prepend(menu_access_groups
, act
);
6781 access_domains
= sipe_get_access_domains(sipe_private
);
6782 entry
= access_domains
;
6784 domain
= entry
->data
;
6786 menu_name
= g_strdup_printf(_("People at %s"), domain
);
6787 act
= purple_menu_action_new(menu_name
,
6789 NULL
, sipe_get_access_levels_menu(sipe_private
, "domain", g_strdup(domain
), TRUE
));
6790 menu_access_groups
= g_list_prepend(menu_access_groups
, act
);
6793 entry
= entry
->next
;
6797 /* People in domains connected with my company */
6798 act
= purple_menu_action_new("-------------------------------------------", NULL
, NULL
, NULL
);
6799 menu_access_groups
= g_list_prepend(menu_access_groups
, act
);
6801 act
= purple_menu_action_new(_("Add new domain..."),
6802 PURPLE_CALLBACK(sipe_buddy_menu_access_level_add_domain_cb
),
6804 menu_access_groups
= g_list_prepend(menu_access_groups
, act
);
6806 menu_access_groups
= g_list_reverse(menu_access_groups
);
6808 return menu_access_groups
;
6812 sipe_get_access_control_menu(struct sipe_core_private
*sipe_private
,
6815 GList
*menu_access_levels
= NULL
;
6816 GList
*menu_access_groups
= NULL
;
6818 PurpleMenuAction
*act
;
6820 menu_access_levels
= sipe_get_access_levels_menu(sipe_private
, "user", sipe_get_no_sip_uri(uri
), TRUE
);
6822 menu_access_groups
= sipe_get_access_groups_menu(sipe_private
);
6824 menu_name
= g_strdup_printf(INDENT_FMT
, _("Access groups"));
6825 act
= purple_menu_action_new(menu_name
,
6827 NULL
, menu_access_groups
);
6829 menu_access_levels
= g_list_append(menu_access_levels
, act
);
6831 menu_name
= g_strdup_printf(INDENT_FMT
, _("Online help..."));
6832 act
= purple_menu_action_new(menu_name
,
6833 PURPLE_CALLBACK(sipe_buddy_menu_access_level_help_cb
),
6836 menu_access_levels
= g_list_append(menu_access_levels
, act
);
6838 return menu_access_levels
;
6842 sipe_conf_modify_lock(PurpleChat
*chat
, gboolean locked
)
6844 struct sipe_core_private
*sipe_private
= PURPLE_CHAT_TO_SIPE_CORE_PRIVATE
;
6845 struct sip_session
*session
;
6847 session
= sipe_session_find_chat_by_title(sipe_private
,
6848 (gchar
*)g_hash_table_lookup(chat
->components
, "channel"));
6849 sipe_conf_modify_conference_lock(sipe_private
, session
, locked
);
6853 sipe_chat_menu_unlock_cb(PurpleChat
*chat
)
6855 SIPE_DEBUG_INFO_NOFORMAT("sipe_chat_menu_unlock_cb() called");
6856 sipe_conf_modify_lock(chat
, FALSE
);
6860 sipe_chat_menu_lock_cb(PurpleChat
*chat
)
6862 SIPE_DEBUG_INFO_NOFORMAT("sipe_chat_menu_lock_cb() called");
6863 sipe_conf_modify_lock(chat
, TRUE
);
6867 sipe_chat_menu(PurpleChat
*chat
)
6869 PurpleMenuAction
*act
;
6871 struct sipe_core_private
*sipe_private
= PURPLE_CHAT_TO_SIPE_CORE_PRIVATE
;
6872 struct sip_session
*session
;
6875 session
= sipe_session_find_chat_by_title(sipe_private
,
6876 (gchar
*)g_hash_table_lookup(chat
->components
, "channel"));
6877 if (!session
) return NULL
;
6879 self
= sip_uri_self(sipe_private
);
6881 if (session
->focus_uri
&&
6882 sipe_backend_chat_is_operator(session
->backend_session
, self
))
6884 if (session
->locked
) {
6885 act
= purple_menu_action_new(_("Unlock"),
6886 PURPLE_CALLBACK(sipe_chat_menu_unlock_cb
),
6888 menu
= g_list_prepend(menu
, act
);
6890 act
= purple_menu_action_new(_("Lock"),
6891 PURPLE_CALLBACK(sipe_chat_menu_lock_cb
),
6893 menu
= g_list_prepend(menu
, act
);
6897 menu
= g_list_reverse(menu
);
6904 process_get_info_response(struct sipe_core_private
*sipe_private
,
6905 struct sipmsg
*msg
, struct transaction
*trans
)
6907 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
6908 char *uri
= trans
->payload
->data
;
6910 PurpleNotifyUserInfo
*info
;
6911 PurpleBuddy
*pbuddy
= NULL
;
6912 struct sipe_buddy
*sbuddy
;
6913 const char *alias
= NULL
;
6914 char *device_name
= NULL
;
6915 char *server_alias
= NULL
;
6916 char *phone_number
= NULL
;
6919 char *first_name
= NULL
;
6920 char *last_name
= NULL
;
6922 if (!sip
) return FALSE
;
6924 SIPE_DEBUG_INFO("Fetching %s's user info for %s", uri
, sipe_private
->username
);
6926 pbuddy
= purple_find_buddy((PurpleAccount
*)sip
->account
, uri
);
6927 alias
= purple_buddy_get_local_alias(pbuddy
);
6929 //will query buddy UA's capabilities and send answer to log
6930 sipe_options_request(sipe_private
, uri
);
6932 sbuddy
= g_hash_table_lookup(sipe_private
->buddies
, uri
);
6934 device_name
= sbuddy
->device_name
? g_strdup(sbuddy
->device_name
) : NULL
;
6937 info
= purple_notify_user_info_new();
6939 if (msg
->response
!= 200) {
6940 SIPE_DEBUG_INFO("process_get_info_response: SERVICE response is %d", msg
->response
);
6942 sipe_xml
*searchResults
;
6943 const sipe_xml
*mrow
;
6945 SIPE_DEBUG_INFO("process_get_info_response: body:\n%s", msg
->body
? msg
->body
: "");
6946 searchResults
= sipe_xml_parse(msg
->body
, msg
->bodylen
);
6947 if (!searchResults
) {
6948 SIPE_DEBUG_INFO_NOFORMAT("process_get_info_response: no parseable searchResults");
6949 } else if ((mrow
= sipe_xml_child(searchResults
, "Body/Array/row"))) {
6951 server_alias
= g_strdup(sipe_xml_attribute(mrow
, "displayName"));
6952 email
= g_strdup(sipe_xml_attribute(mrow
, "email"));
6953 phone_number
= g_strdup(sipe_xml_attribute(mrow
, "phone"));
6955 /* For 2007 system we will take this from ContactCard -
6956 * it has cleaner tel: URIs at least
6958 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
6959 char *tel_uri
= sip_to_tel_uri(phone_number
);
6960 /* trims its parameters, so call first */
6961 sipe_update_user_info(sipe_private
, uri
, ALIAS_PROP
, server_alias
);
6962 sipe_update_user_info(sipe_private
, uri
, EMAIL_PROP
, email
);
6963 sipe_update_user_info(sipe_private
, uri
, PHONE_PROP
, tel_uri
);
6964 sipe_update_user_info(sipe_private
, uri
, PHONE_DISPLAY_PROP
, phone_number
);
6968 if (server_alias
&& strlen(server_alias
) > 0) {
6969 purple_notify_user_info_add_pair(info
, _("Display name"), server_alias
);
6971 if ((value
= sipe_xml_attribute(mrow
, "title")) && strlen(value
) > 0) {
6972 purple_notify_user_info_add_pair(info
, _("Job title"), value
);
6974 if ((value
= sipe_xml_attribute(mrow
, "office")) && strlen(value
) > 0) {
6975 purple_notify_user_info_add_pair(info
, _("Office"), value
);
6977 if (phone_number
&& strlen(phone_number
) > 0) {
6978 purple_notify_user_info_add_pair(info
, _("Business phone"), phone_number
);
6980 if ((value
= sipe_xml_attribute(mrow
, "company")) && strlen(value
) > 0) {
6981 purple_notify_user_info_add_pair(info
, _("Company"), value
);
6983 if ((value
= sipe_xml_attribute(mrow
, "city")) && strlen(value
) > 0) {
6984 purple_notify_user_info_add_pair(info
, _("City"), value
);
6986 if ((value
= sipe_xml_attribute(mrow
, "state")) && strlen(value
) > 0) {
6987 purple_notify_user_info_add_pair(info
, _("State"), value
);
6989 if ((value
= sipe_xml_attribute(mrow
, "country")) && strlen(value
) > 0) {
6990 purple_notify_user_info_add_pair(info
, _("Country"), value
);
6992 if (email
&& strlen(email
) > 0) {
6993 purple_notify_user_info_add_pair(info
, _("Email address"), email
);
6997 sipe_xml_free(searchResults
);
7000 purple_notify_user_info_add_section_break(info
);
7002 if (is_empty(server_alias
)) {
7003 g_free(server_alias
);
7004 server_alias
= g_strdup(purple_buddy_get_server_alias(pbuddy
));
7006 purple_notify_user_info_add_pair(info
, _("Display name"), server_alias
);
7010 /* present alias if it differs from server alias */
7011 if (alias
&& !sipe_strequal(alias
, server_alias
))
7013 purple_notify_user_info_add_pair(info
, _("Alias"), alias
);
7016 if (is_empty(email
)) {
7018 email
= g_strdup(purple_blist_node_get_string(&pbuddy
->node
, EMAIL_PROP
));
7020 purple_notify_user_info_add_pair(info
, _("Email address"), email
);
7024 site
= purple_blist_node_get_string(&pbuddy
->node
, SITE_PROP
);
7026 purple_notify_user_info_add_pair(info
, _("Site"), site
);
7029 sipe_get_first_last_names(sipe_private
, uri
, &first_name
, &last_name
);
7030 if (first_name
&& last_name
) {
7031 char *link
= g_strconcat("http://www.linkedin.com/pub/dir/", first_name
, "/", last_name
, NULL
);
7033 purple_notify_user_info_add_pair(info
, _("Find on LinkedIn"), link
);
7040 purple_notify_user_info_add_pair(info
, _("Device"), device_name
);
7043 /* show a buddy's user info in a nice dialog box */
7044 purple_notify_userinfo(sip
->gc
, /* connection the buddy info came through */
7045 uri
, /* buddy's URI */
7047 NULL
, /* callback called when dialog closed */
7048 NULL
); /* userdata for callback */
7050 g_free(phone_number
);
7051 g_free(server_alias
);
7053 g_free(device_name
);
7059 * AD search first, LDAP based
7061 void sipe_get_info(PurpleConnection
*gc
, const char *username
)
7063 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
7064 gchar
*domain_uri
= sip_uri_from_name(sipe_private
->public.sip_domain
);
7065 char *row
= g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW
, "msRTCSIP-PrimaryUserAddress", username
);
7066 gchar
*body
= g_strdup_printf(SIPE_SOAP_SEARCH_CONTACT
, 1, row
);
7067 struct transaction_payload
*payload
= g_new0(struct transaction_payload
, 1);
7069 payload
->destroy
= g_free
;
7070 payload
->data
= g_strdup(username
);
7072 SIPE_DEBUG_INFO("sipe_get_contact_data: body:\n%s", body
? body
: "");
7073 send_soap_request_with_cb(sipe_private
, domain_uri
, body
,
7074 process_get_info_response
, payload
);