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-groupchat.h"
80 #include "sipe-mime.h"
82 #include "sipe-schedule.h"
83 #include "sipe-session.h"
84 #include "sipe-subscriptions.h"
86 #include "sipe-media.h"
88 #include "sipe-utils.h"
93 #define SIPE_IDLE_SET_DELAY 1 /* 1 sec */
95 #define UPDATE_CALENDAR_DELAY 1*60 /* 1 min */
96 #define UPDATE_CALENDAR_INTERVAL 30*60 /* 30 min */
98 /* Status identifiers (see also: sipe_status_types()) */
99 #define SIPE_STATUS_ID_UNKNOWN purple_primitive_get_id_from_type(PURPLE_STATUS_UNSET) /* Unset (primitive) */
100 #define SIPE_STATUS_ID_OFFLINE purple_primitive_get_id_from_type(PURPLE_STATUS_OFFLINE) /* Offline (primitive) */
101 #define SIPE_STATUS_ID_AVAILABLE purple_primitive_get_id_from_type(PURPLE_STATUS_AVAILABLE) /* Online */
102 /* PURPLE_STATUS_UNAVAILABLE: */
103 #define SIPE_STATUS_ID_BUSY "busy" /* Busy */
104 #define SIPE_STATUS_ID_BUSYIDLE "busyidle" /* BusyIdle */
105 #define SIPE_STATUS_ID_DND "do-not-disturb" /* Do Not Disturb */
106 #define SIPE_STATUS_ID_IN_MEETING "in-a-meeting" /* In a meeting */
107 #define SIPE_STATUS_ID_IN_CONF "in-a-conference" /* In a conference */
108 #define SIPE_STATUS_ID_ON_PHONE "on-the-phone" /* On the phone */
109 #define SIPE_STATUS_ID_INVISIBLE purple_primitive_get_id_from_type(PURPLE_STATUS_INVISIBLE) /* Appear Offline */
110 /* PURPLE_STATUS_AWAY: */
111 #define SIPE_STATUS_ID_IDLE "idle" /* Idle/Inactive */
112 #define SIPE_STATUS_ID_BRB "be-right-back" /* Be Right Back */
113 #define SIPE_STATUS_ID_AWAY purple_primitive_get_id_from_type(PURPLE_STATUS_AWAY) /* Away (primitive) */
114 /** Reuters status (user settable) */
115 #define SIPE_STATUS_ID_LUNCH "out-to-lunch" /* Out To Lunch */
116 /* ??? PURPLE_STATUS_EXTENDED_AWAY */
117 /* ??? PURPLE_STATUS_MOBILE */
118 /* ??? PURPLE_STATUS_TUNE */
120 /* Status attributes (see also sipe_status_types() */
121 #define SIPE_STATUS_ATTR_ID_MESSAGE "message"
123 static struct sipe_activity_map_struct
128 const char *status_id
;
130 } const sipe_activity_map
[] =
132 /* This has nothing to do with Availability numbers, like 3500 (online).
133 * Just a mapping of Communicator Activities to Purple statuses to be able display them in Pidgin.
135 { SIPE_ACTIVITY_UNSET
, "unset", NULL
, NULL
},
136 { SIPE_ACTIVITY_ONLINE
, "online", NULL
, NULL
},
137 { SIPE_ACTIVITY_INACTIVE
, SIPE_STATUS_ID_IDLE
, N_("Inactive") , NULL
},
138 { SIPE_ACTIVITY_BUSY
, SIPE_STATUS_ID_BUSY
, N_("Busy") , SIPE_STATUS_ID_BUSY
},
139 { SIPE_ACTIVITY_BUSYIDLE
, SIPE_STATUS_ID_BUSYIDLE
, N_("Busy-Idle") , NULL
},
140 { SIPE_ACTIVITY_DND
, SIPE_STATUS_ID_DND
, NULL
, SIPE_STATUS_ID_DND
},
141 { SIPE_ACTIVITY_BRB
, SIPE_STATUS_ID_BRB
, N_("Be right back") , SIPE_STATUS_ID_BRB
},
142 { SIPE_ACTIVITY_AWAY
, "away", NULL
, NULL
},
143 { SIPE_ACTIVITY_LUNCH
, SIPE_STATUS_ID_LUNCH
, N_("Out to lunch") , NULL
},
144 { SIPE_ACTIVITY_OFFLINE
, "offline", NULL
, NULL
},
145 { SIPE_ACTIVITY_ON_PHONE
, SIPE_STATUS_ID_ON_PHONE
, N_("In a call") , NULL
},
146 { SIPE_ACTIVITY_IN_CONF
, SIPE_STATUS_ID_IN_CONF
, N_("In a conference") , NULL
},
147 { SIPE_ACTIVITY_IN_MEETING
, SIPE_STATUS_ID_IN_MEETING
, N_("In a meeting") , NULL
},
148 { SIPE_ACTIVITY_OOF
, "out-of-office", N_("Out of office") , NULL
},
149 { SIPE_ACTIVITY_URGENT_ONLY
, "urgent-interruptions-only", N_("Urgent interruptions only") , NULL
}
151 /** @param x is sipe_activity */
152 #define SIPE_ACTIVITY_I18N(x) gettext(sipe_activity_map[x].desc)
155 sipe_get_activity_by_token(const char *token
)
159 for (i
= 0; i
< SIPE_ACTIVITY_NUM_TYPES
; i
++)
161 if (sipe_strequal(token
, sipe_activity_map
[i
].token
))
162 return sipe_activity_map
[i
].type
;
165 return sipe_activity_map
[0].type
;
169 sipe_get_activity_desc_by_token(const char *token
)
171 if (!token
) return NULL
;
173 return SIPE_ACTIVITY_I18N(sipe_get_activity_by_token(token
));
176 static void send_presence_status(struct sipe_core_private
*sipe_private
,
180 * @param from0 from URI (with 'sip:' prefix). Will be filled with self-URI if NULL passed.
183 send_soap_request_with_cb(struct sipe_core_private
*sipe_private
,
186 TransCallback callback
,
187 struct transaction_payload
*payload
)
189 gchar
*from
= from0
? g_strdup(from0
) : sip_uri_self(sipe_private
);
190 gchar
*contact
= get_contact(sipe_private
);
191 gchar
*hdr
= g_strdup_printf("Contact: %s\r\n"
192 "Content-Type: application/SOAP+xml\r\n",contact
);
194 struct transaction
*trans
= sip_transport_service(sipe_private
,
199 trans
->payload
= payload
;
206 static void send_soap_request(struct sipe_core_private
*sipe_private
,
209 send_soap_request_with_cb(sipe_private
, NULL
, body
, NULL
, NULL
);
213 * Returns pointer to URI without sip: prefix if any
215 * @param sip_uri SIP URI possibly with sip: prefix. Example: sip:first.last@hq.company.com
216 * @return pointer to URL without sip: prefix. Coresponding example: first.last@hq.company.com
218 * Doesn't allocate memory
221 sipe_get_no_sip_uri(const char *sip_uri
)
223 const char *prefix
= "sip:";
224 if (!sip_uri
) return NULL
;
226 if (g_str_has_prefix(sip_uri
, prefix
)) {
227 return (sip_uri
+strlen(prefix
));
234 sipe_contact_set_acl (struct sipe_core_private
*sipe_private
,
238 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
239 gchar
* body
= g_strdup_printf(SIPE_SOAP_ALLOW_DENY
, who
, rights
, sip
->acl_delta
++);
240 send_soap_request(sipe_private
, body
);
245 sipe_change_access_level(struct sipe_core_private
*sipe_private
,
246 const int container_id
,
251 sipe_core_contact_allow_deny (struct sipe_core_public
*sipe_public
,
252 const gchar
* who
, gboolean allow
)
254 struct sipe_core_private
*sipe_private
= SIPE_CORE_PRIVATE
;
257 SIPE_DEBUG_INFO("Authorizing contact %s", who
);
259 SIPE_DEBUG_INFO("Blocking contact %s", who
);
262 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
263 sipe_change_access_level(sipe_private
, (allow
? -1 : 32000), "user", sipe_get_no_sip_uri(who
));
265 sipe_contact_set_acl(sipe_private
, who
, allow
? "AA" : "BD");
270 void sipe_auth_user_cb(void * data
)
272 struct sipe_auth_job
* job
= (struct sipe_auth_job
*) data
;
275 sipe_core_contact_allow_deny((struct sipe_core_public
*)job
->sipe_private
, job
->who
, TRUE
);
280 void sipe_deny_user_cb(void * data
)
282 struct sipe_auth_job
* job
= (struct sipe_auth_job
*) data
;
285 sipe_core_contact_allow_deny((struct sipe_core_public
*)job
->sipe_private
, job
->who
, FALSE
);
289 /** @applicable: 2005-
292 sipe_process_presence_wpending (struct sipe_core_private
*sipe_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 sipe_backend_buddy_request_authorization(SIPE_CORE_PUBLIC
,
326 sipe_xml_free(watchers
);
331 sipe_group_add(struct sipe_core_private
*sipe_private
,
332 struct sipe_group
* group
)
334 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
335 if (sipe_backend_buddy_group_add(SIPE_CORE_PUBLIC
,group
->name
))
337 SIPE_DEBUG_INFO("added group %s (id %d)", group
->name
, group
->id
);
338 sip
->groups
= g_slist_append(sip
->groups
, group
);
342 SIPE_DEBUG_INFO("did not add group %s", group
->name
? group
->name
: "");
346 static struct sipe_group
*sipe_group_find_by_id(struct sipe_core_private
*sipe_private
,
349 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
350 struct sipe_group
*group
;
359 if (group
->id
== id
) {
367 static struct sipe_group
*sipe_group_find_by_name(struct sipe_core_private
*sipe_private
,
370 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
371 struct sipe_group
*group
;
380 if (sipe_strequal(group
->name
, name
)) {
389 sipe_group_rename(struct sipe_core_private
*sipe_private
,
390 struct sipe_group
*group
,
393 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
395 SIPE_DEBUG_INFO("Renaming group %s to %s", group
->name
, name
);
396 body
= g_markup_printf_escaped(SIPE_SOAP_MOD_GROUP
, group
->id
, name
, sip
->contacts_delta
++);
397 send_soap_request(sipe_private
, body
);
400 group
->name
= g_strdup(name
);
404 * Only appends if no such value already stored.
407 GSList
* slist_insert_unique_sorted(GSList
*list
, gpointer data
, GCompareFunc func
) {
409 if (!g_slist_find_custom(list
, data
, func
)) {
410 res
= g_slist_insert_sorted(list
, data
, func
);
416 sipe_group_compare(struct sipe_group
*group1
, struct sipe_group
*group2
) {
417 return group1
->id
- group2
->id
;
421 * Returns string like "2 4 7 8" - group ids buddy belong to.
424 sipe_get_buddy_groups_string (struct sipe_buddy
*buddy
) {
427 //creating array from GList, converting int to gchar*
428 gchar
**ids_arr
= g_new(gchar
*, g_slist_length(buddy
->groups
) + 1);
429 GSList
*entry
= buddy
->groups
;
431 if (!ids_arr
) return NULL
;
434 struct sipe_group
* group
= entry
->data
;
435 ids_arr
[i
] = g_strdup_printf("%d", group
->id
);
440 res
= g_strjoinv(" ", ids_arr
);
446 * Sends buddy update to server
449 sipe_core_group_set_user(struct sipe_core_public
*sipe_public
, const gchar
* who
)
451 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA
;
452 struct sipe_buddy
*buddy
= g_hash_table_lookup(SIPE_CORE_PRIVATE
->buddies
, who
);
453 sipe_backend_buddy backend_buddy
= sipe_backend_buddy_find(sipe_public
, who
, NULL
);
455 if (buddy
&& backend_buddy
) {
456 gchar
*alias
= sipe_backend_buddy_get_alias(sipe_public
, backend_buddy
);
457 gchar
*groups
= sipe_get_buddy_groups_string(buddy
);
460 SIPE_DEBUG_INFO("Saving buddy %s with alias %s and groups %s", who
, alias
, groups
);
462 body
= g_markup_printf_escaped(SIPE_SOAP_SET_CONTACT
,
463 alias
, groups
, "true", buddy
->name
, sip
->contacts_delta
++
465 send_soap_request(SIPE_CORE_PRIVATE
, body
);
473 static gboolean
process_add_group_response(struct sipe_core_private
*sipe_private
,
475 struct transaction
*trans
)
477 if (msg
->response
== 200) {
478 struct sipe_group
*group
;
479 struct group_user_context
*ctx
= trans
->payload
->data
;
481 const sipe_xml
*node
;
483 struct sipe_buddy
*buddy
;
485 xml
= sipe_xml_parse(msg
->body
, msg
->bodylen
);
490 node
= sipe_xml_child(xml
, "Body/addGroup/groupID");
496 group_id
= sipe_xml_data(node
);
502 group
= g_new0(struct sipe_group
, 1);
503 group
->id
= (int)g_ascii_strtod(group_id
, NULL
);
505 group
->name
= g_strdup(ctx
->group_name
);
507 sipe_group_add(sipe_private
, group
);
509 if (ctx
->user_name
) {
510 buddy
= g_hash_table_lookup(sipe_private
->buddies
, ctx
->user_name
);
512 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, group
, (GCompareFunc
)sipe_group_compare
);
515 sipe_core_group_set_user(SIPE_CORE_PUBLIC
, ctx
->user_name
);
524 static void sipe_group_context_destroy(gpointer data
)
526 struct group_user_context
*ctx
= data
;
527 g_free(ctx
->group_name
);
528 g_free(ctx
->user_name
);
532 static void sipe_group_create (struct sipe_core_private
*sipe_private
,
533 const gchar
*name
, const gchar
* who
)
535 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
536 struct transaction_payload
*payload
= g_new0(struct transaction_payload
, 1);
537 struct group_user_context
*ctx
= g_new0(struct group_user_context
, 1);
538 const gchar
*soap_name
= sipe_strequal(name
, _("Other Contacts")) ? "~" : name
;
540 ctx
->group_name
= g_strdup(name
);
541 ctx
->user_name
= g_strdup(who
);
542 payload
->destroy
= sipe_group_context_destroy
;
545 body
= g_markup_printf_escaped(SIPE_SOAP_ADD_GROUP
, soap_name
, sip
->contacts_delta
++);
546 send_soap_request_with_cb(sipe_private
, NULL
, body
, process_add_group_response
, payload
);
551 sipe_sched_calendar_status_update(struct sipe_core_private
*sipe_private
,
552 time_t calculate_from
);
555 sipe_get_availability_by_status(const char* sipe_status_id
, char** activity_token
);
558 sipe_get_status_by_availability(int avail
,
562 sipe_set_purple_account_status_and_note(const PurpleAccount
*account
,
563 const char *status_id
,
565 time_t do_not_publish
[]);
568 sipe_apply_calendar_status(struct sipe_core_private
*sipe_private
,
569 struct sipe_buddy
*sbuddy
,
570 const char *status_id
)
572 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
573 time_t cal_avail_since
;
574 int cal_status
= sipe_cal_get_status(sbuddy
, time(NULL
), &cal_avail_since
);
580 if (cal_status
< SIPE_CAL_NO_DATA
) {
581 SIPE_DEBUG_INFO("sipe_apply_calendar_status: cal_status : %d for %s", cal_status
, sbuddy
->name
);
582 SIPE_DEBUG_INFO("sipe_apply_calendar_status: cal_avail_since : %s", asctime(localtime(&cal_avail_since
)));
585 /* scheduled Cal update call */
587 status_id
= sbuddy
->last_non_cal_status_id
;
588 g_free(sbuddy
->activity
);
589 sbuddy
->activity
= g_strdup(sbuddy
->last_non_cal_activity
);
593 SIPE_DEBUG_INFO("sipe_apply_calendar_status: status_id is NULL for %s, exiting.",
594 sbuddy
->name
? sbuddy
->name
: "" );
598 /* adjust to calendar status */
599 if (cal_status
!= SIPE_CAL_NO_DATA
) {
600 SIPE_DEBUG_INFO("sipe_apply_calendar_status: user_avail_since: %s", asctime(localtime(&sbuddy
->user_avail_since
)));
602 if (cal_status
== SIPE_CAL_BUSY
603 && cal_avail_since
> sbuddy
->user_avail_since
604 && 6500 >= sipe_get_availability_by_status(status_id
, NULL
))
606 status_id
= SIPE_STATUS_ID_BUSY
;
607 g_free(sbuddy
->activity
);
608 sbuddy
->activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_IN_MEETING
));
610 avail
= sipe_get_availability_by_status(status_id
, NULL
);
612 SIPE_DEBUG_INFO("sipe_apply_calendar_status: activity_since : %s", asctime(localtime(&sbuddy
->activity_since
)));
613 if (cal_avail_since
> sbuddy
->activity_since
) {
614 if (cal_status
== SIPE_CAL_OOF
615 && avail
>= 15000) /* 12000 in 2007 */
617 g_free(sbuddy
->activity
);
618 sbuddy
->activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_OOF
));
623 /* then set status_id actually */
624 SIPE_DEBUG_INFO("sipe_apply_calendar_status: to %s for %s", status_id
, sbuddy
->name
? sbuddy
->name
: "" );
625 sipe_backend_buddy_set_status(SIPE_CORE_PUBLIC
, sbuddy
->name
, status_id
);
627 /* set our account state to the one in roaming (including calendar info) */
628 self_uri
= sip_uri_self(sipe_private
);
629 if (sip
->initial_state_published
&& sipe_strcase_equal(sbuddy
->name
, self_uri
)) {
630 if (sipe_strequal(status_id
, SIPE_STATUS_ID_OFFLINE
)) {
631 status_id
= g_strdup(SIPE_STATUS_ID_INVISIBLE
); /* not not let offline status switch us off */
634 SIPE_DEBUG_INFO("sipe_apply_calendar_status: switch to '%s' for the account", sip
->status
);
635 sipe_set_purple_account_status_and_note(sip
->account
, status_id
, sip
->note
, sip
->do_not_publish
);
641 sipe_core_buddy_got_status(struct sipe_core_public
*sipe_public
,
643 const gchar
*status_id
)
645 struct sipe_core_private
*sipe_private
= SIPE_CORE_PRIVATE
;
646 struct sipe_buddy
*sbuddy
= g_hash_table_lookup(sipe_private
->buddies
, uri
);
650 /* Check if on 2005 system contact's calendar,
651 * then set/preserve it.
653 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
654 sipe_apply_calendar_status(sipe_private
, sbuddy
, status_id
);
656 sipe_backend_buddy_set_status(sipe_public
, uri
, status_id
);
661 update_calendar_status_cb(SIPE_UNUSED_PARAMETER
char *name
,
662 struct sipe_buddy
*sbuddy
,
663 struct sipe_core_private
*sipe_private
)
665 sipe_apply_calendar_status(sipe_private
, sbuddy
, NULL
);
669 * Updates contact's status
670 * based on their calendar information.
672 * Applicability: 2005 systems
675 update_calendar_status(struct sipe_core_private
*sipe_private
,
676 SIPE_UNUSED_PARAMETER
void *unused
)
678 SIPE_DEBUG_INFO_NOFORMAT("update_calendar_status() started.");
679 g_hash_table_foreach(sipe_private
->buddies
, (GHFunc
)update_calendar_status_cb
, sipe_private
);
681 /* repeat scheduling */
682 sipe_sched_calendar_status_update(sipe_private
, time(NULL
) + 3*60 /* 3 min */);
686 * Schedules process of contacts' status update
687 * based on their calendar information.
688 * Should be scheduled to the beginning of every
689 * 15 min interval, like:
690 * 13:00, 13:15, 13:30, 13:45, etc.
692 * Applicability: 2005 systems
695 sipe_sched_calendar_status_update(struct sipe_core_private
*sipe_private
,
696 time_t calculate_from
)
698 int interval
= 15*60;
699 /** start of the beginning of closest 15 min interval. */
700 time_t next_start
= ((time_t)((int)((int)calculate_from
)/interval
+ 1)*interval
);
702 SIPE_DEBUG_INFO("sipe_sched_calendar_status_update: calculate_from time: %s",
703 asctime(localtime(&calculate_from
)));
704 SIPE_DEBUG_INFO("sipe_sched_calendar_status_update: next start time : %s",
705 asctime(localtime(&next_start
)));
707 sipe_schedule_seconds(sipe_private
,
708 "<+2005-cal-status>",
710 next_start
- time(NULL
),
711 update_calendar_status
,
716 * Schedules process of self status publish
717 * based on own calendar information.
718 * Should be scheduled to the beginning of every
719 * 15 min interval, like:
720 * 13:00, 13:15, 13:30, 13:45, etc.
722 * Applicability: 2007+ systems
725 sipe_sched_calendar_status_self_publish(struct sipe_core_private
*sipe_private
,
726 time_t calculate_from
)
729 /** start of the beginning of closest 5 min interval. */
730 time_t next_start
= ((time_t)((int)((int)calculate_from
)/interval
+ 1)*interval
);
732 SIPE_DEBUG_INFO("sipe_sched_calendar_status_self_publish: calculate_from time: %s",
733 asctime(localtime(&calculate_from
)));
734 SIPE_DEBUG_INFO("sipe_sched_calendar_status_self_publish: next start time : %s",
735 asctime(localtime(&next_start
)));
737 sipe_schedule_seconds(sipe_private
,
738 "<+2007-cal-status>",
740 next_start
- time(NULL
),
741 publish_calendar_status_self
,
745 static void sipe_subscribe_resource_uri(const char *name
,
746 SIPE_UNUSED_PARAMETER gpointer value
,
747 gchar
**resources_uri
)
749 gchar
*tmp
= *resources_uri
;
750 *resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"/>\n", tmp
, name
);
754 static void sipe_subscribe_resource_uri_with_context(const char *name
, gpointer value
, gchar
**resources_uri
)
756 struct sipe_buddy
*sbuddy
= (struct sipe_buddy
*)value
;
757 gchar
*context
= sbuddy
&& sbuddy
->just_added
? "><context/></resource>" : "/>";
758 gchar
*tmp
= *resources_uri
;
760 if (sbuddy
) sbuddy
->just_added
= FALSE
; /* should be enought to include context one time */
762 *resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"%s\n", tmp
, name
, context
);
767 * Support for Batch Category SUBSCRIBE [MS-PRES] - msrtc-event-categories+xml OCS 2007
768 * Support for Batch Category SUBSCRIBE [MS-SIP] - adrl+xml LCS 2005
769 * The user sends an initial batched category SUBSCRIBE request against all contacts on his roaming list in only a request
770 * A batch category SUBSCRIBE request MUST have the same To-URI and From-URI.
771 * This header will be send only if adhoclist there is a "Supported: adhoclist" in REGISTER answer else will be send a Single Category SUBSCRIBE
774 static void sipe_subscribe_presence_batched_to(struct sipe_core_private
*sipe_private
,
775 gchar
*resources_uri
,
778 gchar
*contact
= get_contact(sipe_private
);
783 gchar
*autoextend
= "";
786 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
787 require
= ", categoryList";
788 accept
= ", application/msrtc-event-categories+xml, application/xpidf+xml, application/pidf+xml";
789 content_type
= "application/msrtc-adrl-categorylist+xml";
790 content
= g_strdup_printf(
791 "<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
792 "<action name=\"subscribe\" id=\"63792024\">\n"
793 "<adhocList>\n%s</adhocList>\n"
794 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
795 "<category name=\"calendarData\"/>\n"
796 "<category name=\"contactCard\"/>\n"
797 "<category name=\"note\"/>\n"
798 "<category name=\"state\"/>\n"
801 "</batchSub>", sipe_private
->username
, resources_uri
);
803 autoextend
= "Supported: com.microsoft.autoextend\r\n";
804 content_type
= "application/adrl+xml";
805 content
= g_strdup_printf(
806 "<adhoclist xmlns=\"urn:ietf:params:xml:ns:adrl\" uri=\"sip:%s\" name=\"sip:%s\">\n"
807 "<create xmlns=\"\">\n%s</create>\n"
808 "</adhoclist>\n", sipe_private
->username
, sipe_private
->username
, resources_uri
);
810 g_free(resources_uri
);
812 request
= g_strdup_printf(
813 "Require: adhoclist%s\r\n"
814 "Supported: eventlist\r\n"
815 "Accept: application/rlmi+xml, multipart/related, text/xml+msrtc.pidf%s\r\n"
816 "Supported: ms-piggyback-first-notify\r\n"
817 "%sSupported: ms-benotify\r\n"
818 "Proxy-Require: ms-benotify\r\n"
819 "Event: presence\r\n"
820 "Content-Type: %s\r\n"
821 "Contact: %s\r\n", require
, accept
, autoextend
, content_type
, contact
);
824 sipe_subscribe_presence_buddy(sipe_private
, to
, request
, content
);
831 static void sipe_subscribe_presence_batched(struct sipe_core_private
*sipe_private
,
832 SIPE_UNUSED_PARAMETER
void *unused
)
834 gchar
*to
= sip_uri_self(sipe_private
);
835 gchar
*resources_uri
= g_strdup("");
836 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
837 g_hash_table_foreach(sipe_private
->buddies
, (GHFunc
) sipe_subscribe_resource_uri_with_context
, &resources_uri
);
839 g_hash_table_foreach(sipe_private
->buddies
, (GHFunc
) sipe_subscribe_resource_uri
, &resources_uri
);
842 sipe_subscribe_presence_batched_to(sipe_private
, resources_uri
, to
);
845 struct presence_batched_routed
{
850 static void sipe_subscribe_presence_batched_routed_free(void *payload
)
852 struct presence_batched_routed
*data
= payload
;
853 GSList
*buddies
= data
->buddies
;
855 g_free(buddies
->data
);
856 buddies
= buddies
->next
;
858 g_slist_free(data
->buddies
);
863 static void sipe_subscribe_presence_batched_routed(struct sipe_core_private
*sipe_private
,
866 struct presence_batched_routed
*data
= payload
;
867 GSList
*buddies
= data
->buddies
;
868 gchar
*resources_uri
= g_strdup("");
870 gchar
*tmp
= resources_uri
;
871 resources_uri
= g_strdup_printf("%s<resource uri=\"%s\"/>\n", tmp
, (char *) buddies
->data
);
873 buddies
= buddies
->next
;
875 sipe_subscribe_presence_batched_to(sipe_private
, resources_uri
,
876 g_strdup(data
->host
));
880 * Single Category SUBSCRIBE [MS-PRES] ; To send when the server returns a 200 OK message with state="resubscribe" in response.
881 * The user sends a single SUBSCRIBE request to the subscribed contact.
882 * The To-URI and the URI listed in the resource list MUST be the same for a single category SUBSCRIBE request.
886 static void sipe_subscribe_presence_single(struct sipe_core_private
*sipe_private
,
889 gchar
*to
= sip_uri((char *)buddy_name
);
890 gchar
*tmp
= get_contact(sipe_private
);
892 gchar
*content
= NULL
;
893 gchar
*autoextend
= "";
894 gchar
*content_type
= "";
895 struct sipe_buddy
*sbuddy
= g_hash_table_lookup(sipe_private
->buddies
, to
);
896 gchar
*context
= sbuddy
&& sbuddy
->just_added
? "><context/></resource>" : "/>";
898 if (sbuddy
) sbuddy
->just_added
= FALSE
;
900 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
901 content_type
= "Content-Type: application/msrtc-adrl-categorylist+xml\r\n";
903 autoextend
= "Supported: com.microsoft.autoextend\r\n";
906 request
= g_strdup_printf(
907 "Accept: application/msrtc-event-categories+xml, text/xml+msrtc.pidf, application/xpidf+xml, application/pidf+xml, application/rlmi+xml, multipart/related\r\n"
908 "Supported: ms-piggyback-first-notify\r\n"
909 "%s%sSupported: ms-benotify\r\n"
910 "Proxy-Require: ms-benotify\r\n"
911 "Event: presence\r\n"
912 "Contact: %s\r\n", autoextend
, content_type
, tmp
);
914 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
915 content
= g_strdup_printf(
916 "<batchSub xmlns=\"http://schemas.microsoft.com/2006/01/sip/batch-subscribe\" uri=\"sip:%s\" name=\"\">\n"
917 "<action name=\"subscribe\" id=\"63792024\"><adhocList>\n"
918 "<resource uri=\"%s\"%s\n"
920 "<categoryList xmlns=\"http://schemas.microsoft.com/2006/09/sip/categorylist\">\n"
921 "<category name=\"calendarData\"/>\n"
922 "<category name=\"contactCard\"/>\n"
923 "<category name=\"note\"/>\n"
924 "<category name=\"state\"/>\n"
927 "</batchSub>", sipe_private
->username
, to
, context
);
932 sipe_subscribe_presence_buddy(sipe_private
, to
, request
, content
);
939 void sipe_set_status(PurpleAccount
*account
, PurpleStatus
*status
)
941 SIPE_DEBUG_INFO("sipe_set_status: status=%s", purple_status_get_id(status
));
943 if (!purple_status_is_active(status
))
947 struct sipe_core_private
*sipe_private
= PURPLE_ACCOUNT_TO_SIPE_CORE_PRIVATE
;
948 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
953 time_t now
= time(NULL
);
954 const char *status_id
= purple_status_get_id(status
);
955 const char *note
= purple_status_get_attr_string(status
, SIPE_STATUS_ATTR_ID_MESSAGE
);
956 sipe_activity activity
= sipe_get_activity_by_token(status_id
);
957 gboolean do_not_publish
= ((now
- sip
->do_not_publish
[activity
]) <= 2);
959 /* when other point of presence clears note, but we are keeping
962 if (do_not_publish
&& !note
&& sip
->cal
&& sip
->cal
->oof_note
) {
963 SIPE_DEBUG_INFO_NOFORMAT("sipe_set_status: enabling publication as OOF note keepers.");
964 do_not_publish
= FALSE
;
967 SIPE_DEBUG_INFO("sipe_set_status: was: sip->do_not_publish[%s]=%d [?] now(time)=%d",
968 status_id
, (int)sip
->do_not_publish
[activity
], (int)now
);
970 sip
->do_not_publish
[activity
] = 0;
971 SIPE_DEBUG_INFO("sipe_set_status: set: sip->do_not_publish[%s]=%d [0]",
972 status_id
, (int)sip
->do_not_publish
[activity
]);
976 SIPE_DEBUG_INFO_NOFORMAT("sipe_set_status: publication was switched off, exiting.");
981 sip
->status
= g_strdup(status_id
);
983 /* hack to escape apostrof before comparison */
984 tmp
= note
? sipe_utils_str_replace(note
, "'", "'") : NULL
;
986 /* this will preserve OOF flag as well */
987 if (!sipe_strequal(tmp
, sip
->note
)) {
988 sip
->is_oof_note
= FALSE
;
990 sip
->note
= g_strdup(note
);
991 sip
->note_since
= time(NULL
);
995 /* schedule 2 sec to capture idle flag */
996 action_name
= g_strdup_printf("<%s>", "+set-status");
997 sipe_schedule_seconds(sipe_private
,
1000 SIPE_IDLE_SET_DELAY
,
1001 send_presence_status
,
1003 g_free(action_name
);
1009 sipe_set_idle(PurpleConnection
* gc
,
1012 SIPE_DEBUG_INFO("sipe_set_idle: interval=%d", interval
);
1015 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
1016 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
1019 sip
->idle_switch
= time(NULL
);
1020 SIPE_DEBUG_INFO("sipe_set_idle: sip->idle_switch : %s", asctime(localtime(&(sip
->idle_switch
))));
1026 sipe_group_buddy(PurpleConnection
*gc
,
1028 const char *old_group_name
,
1029 const char *new_group_name
)
1031 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
1032 struct sipe_buddy
* buddy
= g_hash_table_lookup(sipe_private
->buddies
, who
);
1033 struct sipe_group
* old_group
= NULL
;
1034 struct sipe_group
* new_group
;
1036 SIPE_DEBUG_INFO("sipe_group_buddy[CB]: who:%s old_group_name:%s new_group_name:%s",
1037 who
? who
: "", old_group_name
? old_group_name
: "", new_group_name
? new_group_name
: "");
1039 if(!buddy
) { // buddy not in roaming list
1043 if (old_group_name
) {
1044 old_group
= sipe_group_find_by_name(sipe_private
, old_group_name
);
1046 new_group
= sipe_group_find_by_name(sipe_private
, new_group_name
);
1049 buddy
->groups
= g_slist_remove(buddy
->groups
, old_group
);
1050 SIPE_DEBUG_INFO("buddy %s removed from old group %s", who
, old_group_name
);
1054 sipe_group_create(sipe_private
, new_group_name
, who
);
1056 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, new_group
, (GCompareFunc
)sipe_group_compare
);
1057 sipe_core_group_set_user(SIPE_CORE_PUBLIC
, who
);
1061 void sipe_add_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
1063 SIPE_DEBUG_INFO("sipe_add_buddy[CB]: buddy:%s group:%s", buddy
? buddy
->name
: "", group
? group
->name
: "");
1065 /* libpurple can call us with undefined buddy or group */
1066 if (buddy
&& group
) {
1067 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
1069 /* Buddy name must be lower case as we use purple_normalize_nocase() to compare */
1070 gchar
*buddy_name
= g_ascii_strdown(buddy
->name
, -1);
1071 purple_blist_rename_buddy(buddy
, buddy_name
);
1074 /* Prepend sip: if needed */
1075 if (!g_str_has_prefix(buddy
->name
, "sip:")) {
1076 gchar
*buf
= sip_uri_from_name(buddy
->name
);
1077 purple_blist_rename_buddy(buddy
, buf
);
1081 if (!g_hash_table_lookup(sipe_private
->buddies
, buddy
->name
)) {
1082 struct sipe_buddy
*b
= g_new0(struct sipe_buddy
, 1);
1083 SIPE_DEBUG_INFO("sipe_add_buddy: adding %s", buddy
->name
);
1084 b
->name
= g_strdup(buddy
->name
);
1085 b
->just_added
= TRUE
;
1086 g_hash_table_insert(sipe_private
->buddies
, b
->name
, b
);
1087 /* @TODO should go to callback */
1088 sipe_subscribe_presence_single(sipe_private
,
1091 SIPE_DEBUG_INFO("sipe_add_buddy: buddy %s already in internal list", buddy
->name
);
1094 sipe_group_buddy(gc
, buddy
->name
, NULL
, group
->name
);
1098 static void sipe_free_buddy(struct sipe_buddy
*buddy
)
1102 * We are calling g_hash_table_foreach_steal(). That means that no
1103 * key/value deallocation functions are called. Therefore the glib
1104 * hash code does not touch the key (buddy->name) or value (buddy)
1105 * of the to-be-deleted hash node at all. It follows that we
1107 * - MUST free the memory for the key ourselves and
1108 * - ARE allowed to do it in this function
1110 * Conclusion: glib must be broken on the Windows platform if sipe
1111 * crashes with SIGTRAP when closing. You'll have to live
1112 * with the memory leak until this is fixed.
1114 g_free(buddy
->name
);
1116 g_free(buddy
->activity
);
1117 g_free(buddy
->meeting_subject
);
1118 g_free(buddy
->meeting_location
);
1119 g_free(buddy
->note
);
1121 g_free(buddy
->cal_start_time
);
1122 g_free(buddy
->cal_free_busy_base64
);
1123 g_free(buddy
->cal_free_busy
);
1124 g_free(buddy
->last_non_cal_activity
);
1126 sipe_cal_free_working_hours(buddy
->cal_working_hours
);
1128 g_free(buddy
->device_name
);
1129 g_slist_free(buddy
->groups
);
1134 * Unassociates buddy from group first.
1135 * Then see if no groups left, removes buddy completely.
1136 * Otherwise updates buddy groups on server.
1138 void sipe_remove_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
1140 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
1141 struct sipe_buddy
*b
;
1142 struct sipe_group
*g
= NULL
;
1144 SIPE_DEBUG_INFO("sipe_remove_buddy[CB]: buddy:%s group:%s", buddy
? buddy
->name
: "", group
? group
->name
: "");
1147 b
= g_hash_table_lookup(sipe_private
->buddies
, buddy
->name
);
1151 g
= sipe_group_find_by_name(sipe_private
, group
->name
);
1155 b
->groups
= g_slist_remove(b
->groups
, g
);
1156 SIPE_DEBUG_INFO("buddy %s removed from group %s", buddy
->name
, g
->name
);
1159 if (g_slist_length(b
->groups
) < 1) {
1160 gchar
*action_name
= sipe_utils_presence_key(buddy
->name
);
1161 sipe_schedule_cancel(sipe_private
, action_name
);
1162 g_free(action_name
);
1164 g_hash_table_remove(sipe_private
->buddies
, buddy
->name
);
1167 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
1168 gchar
* body
= g_strdup_printf(SIPE_SOAP_DEL_CONTACT
, b
->name
, sip
->contacts_delta
++);
1169 send_soap_request(sipe_private
, body
);
1175 //updates groups on server
1176 sipe_core_group_set_user(SIPE_CORE_PUBLIC
, b
->name
);
1182 sipe_rename_group(PurpleConnection
*gc
,
1183 const char *old_name
,
1185 SIPE_UNUSED_PARAMETER GList
*moved_buddies
)
1187 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
1188 struct sipe_group
* s_group
= sipe_group_find_by_name(sipe_private
, old_name
);
1190 sipe_group_rename(sipe_private
, s_group
, group
->name
);
1192 SIPE_DEBUG_INFO("Cannot find group %s to rename", old_name
);
1197 sipe_remove_group(PurpleConnection
*gc
, PurpleGroup
*group
)
1199 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
1200 struct sipe_group
* s_group
= sipe_group_find_by_name(sipe_private
, group
->name
);
1202 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
1204 SIPE_DEBUG_INFO("Deleting group %s", group
->name
);
1205 body
= g_strdup_printf(SIPE_SOAP_DEL_GROUP
, s_group
->id
, sip
->contacts_delta
++);
1206 send_soap_request(sipe_private
, body
);
1209 sip
->groups
= g_slist_remove(sip
->groups
, s_group
);
1210 g_free(s_group
->name
);
1213 SIPE_DEBUG_INFO("Cannot find group %s to delete", group
->name
);
1218 * A callback for g_hash_table_foreach
1221 sipe_buddy_subscribe_cb(char *buddy_name
,
1222 SIPE_UNUSED_PARAMETER
struct sipe_buddy
*buddy
,
1223 struct sipe_core_private
*sipe_private
)
1225 gchar
*action_name
= sipe_utils_presence_key(buddy_name
);
1226 /* g_hash_table_size() can never return 0, otherwise this function wouldn't be called :-) */
1227 guint time_range
= (g_hash_table_size(sipe_private
->buddies
) * 1000) / 25; /* time interval for 25 requests per sec. In msec. */
1228 guint timeout
= ((guint
) rand()) / (RAND_MAX
/ time_range
) + 1; /* random period within the range but never 0! */
1230 sipe_schedule_mseconds(sipe_private
,
1232 g_strdup(buddy_name
),
1234 sipe_subscribe_presence_single
,
1236 g_free(action_name
);
1240 * Removes entries from local buddy list
1241 * that does not correspond ones in the roaming contact list.
1243 static void sipe_cleanup_local_blist(struct sipe_core_private
*sipe_private
) {
1244 GSList
*buddies
= sipe_backend_buddy_find_all(SIPE_CORE_PUBLIC
, NULL
, NULL
);
1245 GSList
*entry
= buddies
;
1246 struct sipe_buddy
*buddy
;
1247 sipe_backend_buddy b
;
1251 SIPE_DEBUG_INFO("sipe_cleanup_local_blist: overall %d backend buddies (including clones)", g_slist_length(buddies
));
1252 SIPE_DEBUG_INFO("sipe_cleanup_local_blist: %d sipe buddies (unique)", g_hash_table_size(sipe_private
->buddies
));
1255 gname
= sipe_backend_buddy_get_group_name(SIPE_CORE_PUBLIC
, b
);
1256 bname
= sipe_backend_buddy_get_name(SIPE_CORE_PUBLIC
, b
);
1257 buddy
= g_hash_table_lookup(sipe_private
->buddies
, bname
);
1260 gboolean in_sipe_groups
= FALSE
;
1261 GSList
*entry2
= buddy
->groups
;
1263 struct sipe_group
*group
= entry2
->data
;
1264 if (sipe_strequal(group
->name
, gname
)) {
1265 in_sipe_groups
= TRUE
;
1268 entry2
= entry2
->next
;
1270 if(!in_sipe_groups
) {
1271 SIPE_DEBUG_INFO("*** REMOVING %s from blist group: %s as not having this group in roaming list", bname
, gname
);
1272 sipe_backend_buddy_remove(SIPE_CORE_PUBLIC
, b
);
1275 SIPE_DEBUG_INFO("*** REMOVING %s from blist group: %s as this buddy not in roaming list", bname
, gname
);
1276 sipe_backend_buddy_remove(SIPE_CORE_PUBLIC
, b
);
1279 entry
= entry
->next
;
1281 g_slist_free(buddies
);
1285 sipe_find_access_level(struct sipe_core_private
*sipe_private
,
1288 gboolean
*is_group_access
);
1291 sipe_refresh_blocked_status_cb(char *buddy_name
,
1292 SIPE_UNUSED_PARAMETER
struct sipe_buddy
*buddy
,
1293 struct sipe_core_private
*sipe_private
)
1295 int container_id
= sipe_find_access_level(sipe_private
, "user", buddy_name
, NULL
);
1296 gboolean blocked
= (container_id
== 32000);
1297 gboolean blocked_in_blist
= sipe_backend_buddy_is_blocked(SIPE_CORE_PUBLIC
, buddy_name
);
1299 /* SIPE_DEBUG_INFO("sipe_refresh_blocked_status_cb: buddy_name=%s, blocked=%s, blocked_in_blist=%s",
1300 buddy_name, blocked ? "T" : "F", blocked_in_blist ? "T" : "F"); */
1302 if (blocked
!= blocked_in_blist
) {
1303 sipe_backend_buddy_set_blocked_status(SIPE_CORE_PUBLIC
, buddy_name
, blocked
);
1308 sipe_refresh_blocked_status(struct sipe_core_private
*sipe_private
)
1310 g_hash_table_foreach(sipe_private
->buddies
,
1311 (GHFunc
) sipe_refresh_blocked_status_cb
,
1315 static gboolean
sipe_process_roaming_contacts(struct sipe_core_private
*sipe_private
,
1318 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
1319 int len
= msg
->bodylen
;
1321 const gchar
*tmp
= sipmsg_find_header(msg
, "Event");
1322 const sipe_xml
*item
;
1324 const gchar
*contacts_delta
;
1325 const sipe_xml
*group_node
;
1326 if (!g_str_has_prefix(tmp
, "vnd-microsoft-roaming-contacts")) {
1330 /* Convert the contact from XML to backend Buddies */
1331 isc
= sipe_xml_parse(msg
->body
, len
);
1336 contacts_delta
= sipe_xml_attribute(isc
, "deltaNum");
1337 if (contacts_delta
) {
1338 sip
->contacts_delta
= (int)g_ascii_strtod(contacts_delta
, NULL
);
1341 if (sipe_strequal(sipe_xml_name(isc
), "contactList")) {
1344 for (group_node
= sipe_xml_child(isc
, "group"); group_node
; group_node
= sipe_xml_twin(group_node
)) {
1345 struct sipe_group
* group
= g_new0(struct sipe_group
, 1);
1346 const char *name
= sipe_xml_attribute(group_node
, "name");
1348 if (g_str_has_prefix(name
, "~")) {
1349 name
= _("Other Contacts");
1351 group
->name
= g_strdup(name
);
1352 group
->id
= (int)g_ascii_strtod(sipe_xml_attribute(group_node
, "id"), NULL
);
1354 sipe_group_add(sipe_private
, group
);
1357 // Make sure we have at least one group
1358 if (g_slist_length(sip
->groups
) == 0) {
1359 sipe_group_create(sipe_private
, _("Other Contacts"), NULL
);
1362 /* Parse contacts */
1363 for (item
= sipe_xml_child(isc
, "contact"); item
; item
= sipe_xml_twin(item
)) {
1364 const gchar
*uri
= sipe_xml_attribute(item
, "uri");
1365 const gchar
*name
= sipe_xml_attribute(item
, "name");
1367 struct sipe_buddy
*buddy
= NULL
;
1369 gchar
**item_groups
;
1372 /* Buddy name must be lower case as we use purple_normalize_nocase() to compare */
1373 tmp
= sip_uri_from_name(uri
);
1374 buddy_name
= g_ascii_strdown(tmp
, -1);
1377 /* assign to group Other Contacts if nothing else received */
1378 tmp
= g_strdup(sipe_xml_attribute(item
, "groups"));
1380 struct sipe_group
*group
= sipe_group_find_by_name(sipe_private
, _("Other Contacts"));
1382 tmp
= group
? g_strdup_printf("%d", group
->id
) : g_strdup("1");
1384 item_groups
= g_strsplit(tmp
, " ", 0);
1387 while (item_groups
[i
]) {
1388 struct sipe_group
*group
= sipe_group_find_by_id(sipe_private
, g_ascii_strtod(item_groups
[i
], NULL
));
1390 // If couldn't find the right group for this contact, just put them in the first group we have
1391 if (group
== NULL
&& g_slist_length(sip
->groups
) > 0) {
1392 group
= sip
->groups
->data
;
1395 if (group
!= NULL
) {
1397 sipe_backend_buddy b
= sipe_backend_buddy_find(SIPE_CORE_PUBLIC
, buddy_name
, group
->name
);
1399 b
= sipe_backend_buddy_add(SIPE_CORE_PUBLIC
, buddy_name
, uri
, group
->name
);
1400 SIPE_DEBUG_INFO("Created new buddy %s with alias %s", buddy_name
, uri
);
1403 b_alias
= sipe_backend_buddy_get_alias(SIPE_CORE_PUBLIC
, b
);
1404 if (sipe_strcase_equal(uri
, b_alias
)) {
1405 if (name
!= NULL
&& strlen(name
) != 0) {
1406 sipe_backend_buddy_set_alias(SIPE_CORE_PUBLIC
, b
, name
);
1408 SIPE_DEBUG_INFO("Replaced buddy %s alias with %s", buddy_name
, name
);
1414 buddy
= g_new0(struct sipe_buddy
, 1);
1415 buddy
->name
= sipe_backend_buddy_get_name(SIPE_CORE_PUBLIC
, b
);
1416 g_hash_table_insert(sipe_private
->buddies
, buddy
->name
, buddy
);
1418 SIPE_DEBUG_INFO("Added SIPE buddy %s", buddy
->name
);
1421 buddy
->groups
= slist_insert_unique_sorted(buddy
->groups
, group
, (GCompareFunc
)sipe_group_compare
);
1423 SIPE_DEBUG_INFO("Added buddy %s to group %s", buddy
->name
, group
->name
);
1425 SIPE_DEBUG_INFO("No group found for contact %s! Unable to add to buddy list",
1430 } // while, contact groups
1431 g_strfreev(item_groups
);
1436 sipe_cleanup_local_blist(sipe_private
);
1438 /* Add self-contact if not there yet. 2005 systems. */
1439 /* This will resemble subscription to roaming_self in 2007 systems */
1440 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
1441 gchar
*self_uri
= sip_uri_self(sipe_private
);
1442 struct sipe_buddy
*buddy
= g_hash_table_lookup(sipe_private
->buddies
, self_uri
);
1445 buddy
= g_new0(struct sipe_buddy
, 1);
1446 buddy
->name
= g_strdup(self_uri
);
1447 g_hash_table_insert(sipe_private
->buddies
, buddy
->name
, buddy
);
1454 /* subscribe to buddies */
1455 if (!sip
->subscribed_buddies
) { //do it once, then count Expire field to schedule resubscribe.
1456 if (sip
->batched_support
) {
1457 sipe_subscribe_presence_batched(sipe_private
, NULL
);
1459 g_hash_table_foreach(sipe_private
->buddies
,
1460 (GHFunc
)sipe_buddy_subscribe_cb
,
1463 sip
->subscribed_buddies
= TRUE
;
1465 /* for 2005 systems schedule contacts' status update
1466 * based on their calendar information
1468 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
1469 sipe_sched_calendar_status_update(sipe_private
, time(NULL
));
1476 * Fires on deregistration event initiated by server.
1477 * [MS-SIPREGE] SIP extension.
1482 // Content-Type: text/registration-event
1483 // subscription-state: terminated;expires=0
1484 // ms-diagnostics-public: 4141;reason="User disabled"
1486 // deregistered;event=rejected
1488 static void sipe_process_registration_notify(struct sipe_core_private
*sipe_private
,
1491 const gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
1492 gchar
*event
= NULL
;
1493 gchar
*reason
= NULL
;
1494 const gchar
*diagnostics
= sipmsg_find_header(msg
, "ms-diagnostics");
1497 diagnostics
= diagnostics
? diagnostics
: sipmsg_find_header(msg
, "ms-diagnostics-public");
1498 SIPE_DEBUG_INFO_NOFORMAT("sipe_process_registration_notify: deregistration received.");
1500 if (!g_ascii_strncasecmp(contenttype
, "text/registration-event", 23)) {
1501 event
= sipmsg_find_part_of_header(msg
->body
, "event=", NULL
, NULL
);
1502 //@TODO have proper parameter extraction _by_name_ func, case insesitive.
1503 event
= event
? event
: sipmsg_find_part_of_header(msg
->body
, "event=", ";", NULL
);
1505 SIPE_DEBUG_INFO_NOFORMAT("sipe_process_registration_notify: unknown content type, exiting.");
1509 if (diagnostics
!= NULL
) {
1510 reason
= sipmsg_find_part_of_header(diagnostics
, "reason=\"", "\"", NULL
);
1511 } else { // for LCS2005
1513 if (event
&& sipe_strcase_equal(event
, "unregistered")) {
1514 error_id
= 4140; // [MS-SIPREGE]
1515 //reason = g_strdup(_("User logged out")); // [MS-OCER]
1516 reason
= g_strdup(_("you are already signed in at another location"));
1517 } else if (event
&& sipe_strcase_equal(event
, "rejected")) {
1519 reason
= g_strdup(_("user disabled")); // [MS-OCER]
1520 } else if (event
&& sipe_strcase_equal(event
, "deactivated")) {
1522 reason
= g_strdup(_("user moved")); // [MS-OCER]
1526 warning
= g_strdup_printf(_("You have been rejected by the server: %s"), reason
? reason
: _("no reason given"));
1529 sipe_backend_connection_error(SIPE_CORE_PUBLIC
,
1530 SIPE_CONNECTION_ERROR_INVALID_USERNAME
,
1536 static void sipe_process_provisioning_v2(struct sipe_core_private
*sipe_private
,
1539 sipe_xml
*xn_provision_group_list
;
1540 const sipe_xml
*node
;
1542 xn_provision_group_list
= sipe_xml_parse(msg
->body
, msg
->bodylen
);
1544 /* provisionGroup */
1545 for (node
= sipe_xml_child(xn_provision_group_list
, "provisionGroup"); node
; node
= sipe_xml_twin(node
)) {
1546 if (sipe_strequal("ServerConfiguration", sipe_xml_attribute(node
, "name"))) {
1547 g_free(sipe_private
->focus_factory_uri
);
1548 sipe_private
->focus_factory_uri
= sipe_xml_data(sipe_xml_child(node
, "focusFactoryUri"));
1549 SIPE_DEBUG_INFO("sipe_process_provisioning_v2: sipe_private->focus_factory_uri=%s",
1550 sipe_private
->focus_factory_uri
? sipe_private
->focus_factory_uri
: "");
1553 g_free(sipe_private
->mras_uri
);
1554 sipe_private
->mras_uri
= g_strstrip(sipe_xml_data(sipe_xml_child(node
, "mrasUri")));
1555 SIPE_DEBUG_INFO("sipe_process_provisioning_v2: sipe_private->mras_uri=%s",
1556 sipe_private
->mras_uri
? sipe_private
->mras_uri
: "");
1558 if (sipe_private
->mras_uri
)
1559 sipe_media_get_av_edge_credentials(sipe_private
);
1564 sipe_xml_free(xn_provision_group_list
);
1567 /** for 2005 system */
1569 sipe_process_provisioning(struct sipe_core_private
*sipe_private
,
1572 sipe_xml
*xn_provision
;
1573 const sipe_xml
*node
;
1575 xn_provision
= sipe_xml_parse(msg
->body
, msg
->bodylen
);
1576 if ((node
= sipe_xml_child(xn_provision
, "user"))) {
1577 SIPE_DEBUG_INFO("sipe_process_provisioning: uri=%s", sipe_xml_attribute(node
, "uri"));
1578 if ((node
= sipe_xml_child(node
, "line"))) {
1579 const gchar
*line_uri
= sipe_xml_attribute(node
, "uri");
1580 const gchar
*server
= sipe_xml_attribute(node
, "server");
1581 SIPE_DEBUG_INFO("sipe_process_provisioning: line_uri=%s server=%s", line_uri
, server
);
1582 sip_csta_open(sipe_private
, line_uri
, server
);
1585 sipe_xml_free(xn_provision
);
1588 static void sipe_process_roaming_acl(struct sipe_core_private
*sipe_private
,
1591 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
1592 const gchar
*contacts_delta
;
1595 xml
= sipe_xml_parse(msg
->body
, msg
->bodylen
);
1601 contacts_delta
= sipe_xml_attribute(xml
, "deltaNum");
1604 sip
->acl_delta
= (int)g_ascii_strtod(contacts_delta
, NULL
);
1610 /** MS-PRES container */
1611 struct sipe_container
{
1616 /** MS-PRES container member */
1617 struct sipe_container_member
{
1618 /** user, domain, sameEnterprise, federated, publicCloud; everyone */
1624 free_container_member(struct sipe_container_member
*member
)
1626 if (!member
) return;
1628 g_free(member
->type
);
1629 g_free(member
->value
);
1634 free_container(struct sipe_container
*container
)
1638 if (!container
) return;
1640 entry
= container
->members
;
1642 void *data
= entry
->data
;
1643 entry
= g_slist_remove(entry
, data
);
1644 free_container_member((struct sipe_container_member
*)data
);
1650 sipe_send_container_members_prepare(const guint container_id
,
1651 const guint container_version
,
1652 const gchar
*action
,
1655 char **container_xmls
)
1657 gchar
*value_str
= value
? g_strdup_printf(" value=\"%s\"", value
) : g_strdup("");
1660 if (!container_xmls
) return;
1662 body
= g_strdup_printf(
1663 "<container id=\"%d\" version=\"%d\"><member action=\"%s\" type=\"%s\"%s/></container>",
1671 if ((*container_xmls
) == NULL
) {
1672 *container_xmls
= body
;
1674 char *tmp
= *container_xmls
;
1676 *container_xmls
= g_strconcat(*container_xmls
, body
, NULL
);
1683 sipe_send_set_container_members(struct sipe_core_private
*sipe_private
,
1684 char *container_xmls
)
1691 if (!container_xmls
) return;
1693 self
= sip_uri_self(sipe_private
);
1694 body
= g_strdup_printf(
1695 "<setContainerMembers xmlns=\"http://schemas.microsoft.com/2006/09/sip/container-management\">"
1697 "</setContainerMembers>",
1700 contact
= get_contact(sipe_private
);
1701 hdr
= g_strdup_printf("Contact: %s\r\n"
1702 "Content-Type: application/msrtc-setcontainermembers+xml\r\n", contact
);
1705 sip_transport_service(sipe_private
,
1717 * Finds locally stored MS-PRES container member
1719 static struct sipe_container_member
*
1720 sipe_find_container_member(struct sipe_container
*container
,
1724 struct sipe_container_member
*member
;
1727 if (container
== NULL
|| type
== NULL
) {
1731 entry
= container
->members
;
1733 member
= entry
->data
;
1734 if (sipe_strcase_equal(member
->type
, type
) &&
1735 sipe_strcase_equal(member
->value
, value
))
1739 entry
= entry
->next
;
1745 * Finds locally stored MS-PRES container by id
1747 static struct sipe_container
*
1748 sipe_find_container(struct sipe_core_private
*sipe_private
,
1751 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
1752 struct sipe_container
*container
;
1759 entry
= sip
->containers
;
1761 container
= entry
->data
;
1762 if (id
== container
->id
) {
1765 entry
= entry
->next
;
1771 sipe_get_access_domains(struct sipe_core_private
*sipe_private
)
1773 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
1774 struct sipe_container
*container
;
1775 struct sipe_container_member
*member
;
1780 if (!sip
) return NULL
;
1782 entry
= sip
->containers
;
1784 container
= entry
->data
;
1786 entry2
= container
->members
;
1788 member
= entry2
->data
;
1789 if (sipe_strcase_equal(member
->type
, "domain"))
1791 res
= slist_insert_unique_sorted(res
, g_strdup(member
->value
), (GCompareFunc
)g_ascii_strcasecmp
);
1793 entry2
= entry2
->next
;
1795 entry
= entry
->next
;
1801 * Returns pointer to domain part in provided Email URL
1803 * @param email an email URL. Example: first.last@hq.company.com
1804 * @return pointer to domain part of email URL. Coresponding example: hq.company.com
1806 * Doesn't allocate memory
1809 sipe_get_domain(const char *email
)
1813 if (!email
) return NULL
;
1815 tmp
= strstr(email
, "@");
1817 if (tmp
&& ((tmp
+1) < (email
+ strlen(email
)))) {
1825 /* @TODO: replace with binary search for faster access? */
1826 /** source: http://support.microsoft.com/kb/897567 */
1827 static const char * const public_domains
[] = {
1828 "aol.com", "icq.com", "love.com", "mac.com", "br.live.com",
1829 "hotmail.co.il", "hotmail.co.jp", "hotmail.co.th", "hotmail.co.uk",
1830 "hotmail.com", "hotmail.com.ar", "hotmail.com.tr", "hotmail.es",
1831 "hotmail.de", "hotmail.fr", "hotmail.it", "live.at", "live.be",
1832 "live.ca", "live.cl", "live.cn", "live.co.in", "live.co.kr",
1833 "live.co.uk", "live.co.za", "live.com", "live.com.ar", "live.com.au",
1834 "live.com.co", "live.com.mx", "live.com.my", "live.com.pe",
1835 "live.com.ph", "live.com.pk", "live.com.pt", "live.com.sg",
1836 "live.com.ve", "live.de", "live.dk", "live.fr", "live.hk", "live.ie",
1837 "live.in", "live.it", "live.jp", "live.nl", "live.no", "live.ph",
1838 "live.ru", "live.se", "livemail.com.br", "livemail.tw",
1839 "messengeruser.com", "msn.com", "passport.com", "sympatico.ca",
1840 "tw.live.com", "webtv.net", "windowslive.com", "windowslive.es",
1845 sipe_is_public_domain(const char *domain
)
1848 while (public_domains
[i
]) {
1849 if (sipe_strcase_equal(public_domains
[i
], domain
)) {
1866 sipe_get_access_level_name(int container_id
)
1868 switch(container_id
) {
1869 case 32000: return _("Blocked");
1870 case 400: return _("Personal");
1871 case 300: return _("Team");
1872 case 200: return _("Company");
1873 case 100: return _("Public");
1875 return _("Unknown");
1878 static const guint containers
[] = {32000, 400, 300, 200, 100};
1879 #define CONTAINERS_LEN (sizeof(containers) / sizeof(guint))
1883 sipe_find_member_access_level(struct sipe_core_private
*sipe_private
,
1888 const gchar
*value_mod
= value
;
1890 if (!type
) return -1;
1892 if (sipe_strequal("user", type
)) {
1893 value_mod
= sipe_get_no_sip_uri(value
);
1896 for (i
= 0; i
< CONTAINERS_LEN
; i
++) {
1897 struct sipe_container_member
*member
;
1898 struct sipe_container
*container
= sipe_find_container(sipe_private
, containers
[i
]);
1899 if (!container
) continue;
1901 member
= sipe_find_container_member(container
, type
, value_mod
);
1902 if (member
) return containers
[i
];
1908 /** Member type: user, domain, sameEnterprise, federated, publicCloud; everyone */
1910 sipe_find_access_level(struct sipe_core_private
*sipe_private
,
1913 gboolean
*is_group_access
)
1915 int container_id
= -1;
1917 if (sipe_strequal("user", type
)) {
1919 const char *no_sip_uri
= sipe_get_no_sip_uri(value
);
1921 container_id
= sipe_find_member_access_level(sipe_private
, "user", no_sip_uri
);
1922 if (container_id
>= 0) {
1923 if (is_group_access
) *is_group_access
= FALSE
;
1924 return container_id
;
1927 domain
= sipe_get_domain(no_sip_uri
);
1928 container_id
= sipe_find_member_access_level(sipe_private
, "domain", domain
);
1929 if (container_id
>= 0) {
1930 if (is_group_access
) *is_group_access
= TRUE
;
1931 return container_id
;
1934 container_id
= sipe_find_member_access_level(sipe_private
, "sameEnterprise", NULL
);
1935 if ((container_id
>= 0) && sipe_strcase_equal(sipe_private
->public.sip_domain
, domain
)) {
1936 if (is_group_access
) *is_group_access
= TRUE
;
1937 return container_id
;
1940 container_id
= sipe_find_member_access_level(sipe_private
, "publicCloud", NULL
);
1941 if ((container_id
>= 0) && sipe_is_public_domain(domain
)) {
1942 if (is_group_access
) *is_group_access
= TRUE
;
1943 return container_id
;
1946 container_id
= sipe_find_member_access_level(sipe_private
, "everyone", NULL
);
1947 if ((container_id
>= 0)) {
1948 if (is_group_access
) *is_group_access
= TRUE
;
1949 return container_id
;
1952 container_id
= sipe_find_member_access_level(sipe_private
, type
, value
);
1953 if (is_group_access
) *is_group_access
= FALSE
;
1956 return container_id
;
1960 * @param container_id a new access level. If -1 then current access level
1961 * is just removed (I.e. the member is removed from all containers).
1962 * @param type a type of member. E.g. "user", "sameEnterprise", etc.
1963 * @param value a value for member. E.g. SIP URI for "user" member type.
1966 sipe_change_access_level(struct sipe_core_private
*sipe_private
,
1967 const int container_id
,
1972 int current_container_id
= -1;
1973 char *container_xmls
= NULL
;
1975 /* for each container: find/delete */
1976 for (i
= 0; i
< CONTAINERS_LEN
; i
++) {
1977 struct sipe_container_member
*member
;
1978 struct sipe_container
*container
= sipe_find_container(sipe_private
, containers
[i
]);
1980 if (!container
) continue;
1982 member
= sipe_find_container_member(container
, type
, value
);
1984 current_container_id
= containers
[i
];
1985 /* delete/publish current access level */
1986 if (container_id
< 0 || container_id
!= current_container_id
) {
1987 sipe_send_container_members_prepare(current_container_id
, container
->version
, "remove", type
, value
, &container_xmls
);
1988 /* remove member from our cache, to be able to recalculate AL below */
1989 container
->members
= g_slist_remove(container
->members
, member
);
1990 current_container_id
= -1;
1995 /* recalculate AL below */
1996 current_container_id
= sipe_find_access_level(sipe_private
, type
, value
, NULL
);
1998 /* assign/publish new access level */
1999 if (container_id
!= current_container_id
&& container_id
>= 0) {
2000 struct sipe_container
*container
= sipe_find_container(sipe_private
, container_id
);
2001 guint version
= container
? container
->version
: 0;
2003 sipe_send_container_members_prepare(container_id
, version
, "add", type
, value
, &container_xmls
);
2006 if (container_xmls
) {
2007 sipe_send_set_container_members(sipe_private
, container_xmls
);
2009 g_free(container_xmls
);
2013 free_publication(struct sipe_publication
*publication
)
2015 g_free(publication
->category
);
2016 g_free(publication
->cal_event_hash
);
2017 g_free(publication
->note
);
2019 g_free(publication
->working_hours_xml_str
);
2020 g_free(publication
->fb_start_str
);
2021 g_free(publication
->free_busy_base64
);
2023 g_free(publication
);
2026 /* key is <category><instance><container> */
2028 sipe_is_our_publication(struct sipe_core_private
*sipe_private
,
2031 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
2034 /* filling keys for our publications if not yet cached */
2035 if (!sip
->our_publication_keys
) {
2036 guint device_instance
= sipe_get_pub_instance(sipe_private
, SIPE_PUB_DEVICE
);
2037 guint machine_instance
= sipe_get_pub_instance(sipe_private
, SIPE_PUB_STATE_MACHINE
);
2038 guint user_instance
= sipe_get_pub_instance(sipe_private
, SIPE_PUB_STATE_USER
);
2039 guint calendar_instance
= sipe_get_pub_instance(sipe_private
, SIPE_PUB_STATE_CALENDAR
);
2040 guint cal_oof_instance
= sipe_get_pub_instance(sipe_private
, SIPE_PUB_STATE_CALENDAR_OOF
);
2041 guint cal_data_instance
= sipe_get_pub_instance(sipe_private
, SIPE_PUB_CALENDAR_DATA
);
2042 guint note_oof_instance
= sipe_get_pub_instance(sipe_private
, SIPE_PUB_NOTE_OOF
);
2044 SIPE_DEBUG_INFO_NOFORMAT("* Our Publication Instances *");
2045 SIPE_DEBUG_INFO("\tDevice : %u\t0x%08X", device_instance
, device_instance
);
2046 SIPE_DEBUG_INFO("\tMachine State : %u\t0x%08X", machine_instance
, machine_instance
);
2047 SIPE_DEBUG_INFO("\tUser Stare : %u\t0x%08X", user_instance
, user_instance
);
2048 SIPE_DEBUG_INFO("\tCalendar State : %u\t0x%08X", calendar_instance
, calendar_instance
);
2049 SIPE_DEBUG_INFO("\tCalendar OOF State : %u\t0x%08X", cal_oof_instance
, cal_oof_instance
);
2050 SIPE_DEBUG_INFO("\tCalendar FreeBusy : %u\t0x%08X", cal_data_instance
, cal_data_instance
);
2051 SIPE_DEBUG_INFO("\tOOF Note : %u\t0x%08X", note_oof_instance
, note_oof_instance
);
2052 SIPE_DEBUG_INFO("\tNote : %u", 0);
2053 SIPE_DEBUG_INFO("\tCalendar WorkingHours: %u", 0);
2056 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2057 g_strdup_printf("<%s><%u><%u>", "device", device_instance
, 2));
2059 /* state:machineState */
2060 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2061 g_strdup_printf("<%s><%u><%u>", "state", machine_instance
, 2));
2062 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2063 g_strdup_printf("<%s><%u><%u>", "state", machine_instance
, 3));
2065 /* state:userState */
2066 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2067 g_strdup_printf("<%s><%u><%u>", "state", user_instance
, 2));
2068 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2069 g_strdup_printf("<%s><%u><%u>", "state", user_instance
, 3));
2071 /* state:calendarState */
2072 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2073 g_strdup_printf("<%s><%u><%u>", "state", calendar_instance
, 2));
2074 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2075 g_strdup_printf("<%s><%u><%u>", "state", calendar_instance
, 3));
2077 /* state:calendarState OOF */
2078 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2079 g_strdup_printf("<%s><%u><%u>", "state", cal_oof_instance
, 2));
2080 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2081 g_strdup_printf("<%s><%u><%u>", "state", cal_oof_instance
, 3));
2084 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2085 g_strdup_printf("<%s><%u><%u>", "note", 0, 200));
2086 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2087 g_strdup_printf("<%s><%u><%u>", "note", 0, 300));
2088 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2089 g_strdup_printf("<%s><%u><%u>", "note", 0, 400));
2092 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2093 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance
, 200));
2094 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2095 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance
, 300));
2096 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2097 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance
, 400));
2099 /* calendarData:WorkingHours */
2100 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2101 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 1));
2102 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2103 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 100));
2104 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2105 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 200));
2106 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2107 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 300));
2108 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2109 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 400));
2110 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2111 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 32000));
2113 /* calendarData:FreeBusy */
2114 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2115 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 1));
2116 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2117 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 100));
2118 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2119 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 200));
2120 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2121 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 300));
2122 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2123 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 400));
2124 sip
->our_publication_keys
= g_slist_append(sip
->our_publication_keys
,
2125 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 32000));
2127 //SIPE_DEBUG_INFO("sipe_is_our_publication: sip->our_publication_keys length=%d",
2128 // sip->our_publication_keys ? (int) g_slist_length(sip->our_publication_keys) : -1);
2131 //SIPE_DEBUG_INFO("sipe_is_our_publication: key=%s", key);
2133 entry
= sip
->our_publication_keys
;
2135 //SIPE_DEBUG_INFO(" sipe_is_our_publication: entry->data=%s", entry->data);
2136 if (sipe_strequal(entry
->data
, key
)) {
2139 entry
= entry
->next
;
2144 /** Property names to store in blist.xml */
2145 #define ALIAS_PROP "alias"
2146 #define EMAIL_PROP "email"
2147 #define PHONE_PROP "phone"
2148 #define PHONE_DISPLAY_PROP "phone-display"
2149 #define PHONE_MOBILE_PROP "phone-mobile"
2150 #define PHONE_MOBILE_DISPLAY_PROP "phone-mobile-display"
2151 #define PHONE_HOME_PROP "phone-home"
2152 #define PHONE_HOME_DISPLAY_PROP "phone-home-display"
2153 #define PHONE_OTHER_PROP "phone-other"
2154 #define PHONE_OTHER_DISPLAY_PROP "phone-other-display"
2155 #define PHONE_CUSTOM1_PROP "phone-custom1"
2156 #define PHONE_CUSTOM1_DISPLAY_PROP "phone-custom1-display"
2157 #define SITE_PROP "site"
2158 #define COMPANY_PROP "company"
2159 #define DEPARTMENT_PROP "department"
2160 #define TITLE_PROP "title"
2161 #define OFFICE_PROP "office"
2162 /** implies work address */
2163 #define ADDRESS_STREET_PROP "address-street"
2164 #define ADDRESS_CITY_PROP "address-city"
2165 #define ADDRESS_STATE_PROP "address-state"
2166 #define ADDRESS_ZIPCODE_PROP "address-zipcode"
2167 #define ADDRESS_COUNTRYCODE_PROP "address-country-code"
2170 * Tries to figure out user first and last name
2171 * based on Display Name and email properties.
2173 * Allocates memory - must be g_free()'d
2175 * Examples to parse:
2177 * First Last - Company Name
2180 * Last, First (C)(STP) (Company)
2181 * first.last@company.com (preprocessed as "first last")
2182 * first.last.company.com@reuters.net (preprocessed as "first last company com")
2184 * Unusable examples:
2185 * user@company.com (preprocessed as "user")
2186 * first.m.last@company.com (preprocessed as "first m last")
2187 * user.company.com@reuters.net (preprocessed as "user company com")
2190 sipe_get_first_last_names(struct sipe_core_private
*sipe_private
,
2195 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
2196 sipe_backend_buddy p_buddy
;
2199 const char *first
, *last
;
2202 gboolean has_comma
= FALSE
;
2204 if (!sip
|| !uri
) return;
2206 p_buddy
= sipe_backend_buddy_find(SIPE_CORE_PUBLIC
, uri
, NULL
);
2208 if (!p_buddy
) return;
2210 display_name
= sipe_backend_buddy_get_alias(SIPE_CORE_PUBLIC
, p_buddy
);
2211 email
= sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC
, p_buddy
, SIPE_BUDDY_INFO_EMAIL
);
2213 if (!display_name
&& !email
) return;
2215 /* if no display name, make "first last anything_else" out of email */
2216 if (email
&& !display_name
) {
2217 display_name
= g_strndup(email
, strstr(email
, "@") - email
);
2218 display_name
= sipe_utils_str_replace((tmp
= display_name
), ".", " ");
2223 has_comma
= (strstr(display_name
, ",") != NULL
);
2224 display_name
= sipe_utils_str_replace((tmp
= display_name
), ", ", " ");
2226 display_name
= sipe_utils_str_replace((tmp
= display_name
), ",", " ");
2230 parts
= g_strsplit(display_name
, " ", 0);
2232 if (!parts
[0] || !parts
[1]) {
2234 g_free(display_name
);
2248 *first_name
= g_strstrip(g_strdup(first
));
2252 *last_name
= g_strstrip(g_strdup(last
));
2256 g_free(display_name
);
2261 * Update user information
2263 * @param uri buddy SIP URI with 'sip:' prefix whose info we want to change.
2264 * @param property_name
2265 * @param property_value may be modified to strip white space
2268 sipe_update_user_info(struct sipe_core_private
*sipe_private
,
2270 sipe_buddy_info_fields propkey
,
2271 char *property_value
)
2273 GSList
*buddies
, *entry
;
2276 property_value
= g_strstrip(property_value
);
2278 entry
= buddies
= sipe_backend_buddy_find_all(SIPE_CORE_PUBLIC
, uri
, NULL
); /* all buddies in different groups */
2281 gchar
*server_alias
;
2283 sipe_backend_buddy p_buddy
= entry
->data
;
2285 /* for Display Name */
2286 if (propkey
== SIPE_BUDDY_INFO_DISPLAY_NAME
) {
2287 alias
= sipe_backend_buddy_get_alias(SIPE_CORE_PUBLIC
, p_buddy
);
2288 if (property_value
&& sipe_is_bad_alias(uri
, alias
)) {
2289 SIPE_DEBUG_INFO("Replacing alias for %s with %s", uri
, property_value
);
2290 sipe_backend_buddy_set_alias(SIPE_CORE_PUBLIC
, p_buddy
, property_value
);
2294 server_alias
= sipe_backend_buddy_get_server_alias(SIPE_CORE_PUBLIC
, p_buddy
);
2295 if (!is_empty(property_value
) &&
2296 (!sipe_strequal(property_value
, server_alias
) || is_empty(server_alias
)) )
2298 SIPE_DEBUG_INFO("Replacing service alias for %s with %s", uri
, property_value
);
2299 sipe_backend_buddy_set_server_alias(SIPE_CORE_PUBLIC
, p_buddy
, property_value
);
2301 g_free(server_alias
);
2303 /* for other properties */
2305 if (!is_empty(property_value
)) {
2306 prop_str
= sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC
, p_buddy
, propkey
);
2307 if (!prop_str
|| !sipe_strcase_equal(prop_str
, property_value
)) {
2308 sipe_backend_buddy_set_string(SIPE_CORE_PUBLIC
, p_buddy
, propkey
, property_value
);
2314 entry
= entry
->next
;
2316 g_slist_free(buddies
);
2321 * Suitable for both 2005 and 2007 systems.
2323 * @param uri buddy SIP URI with 'sip:' prefix whose info we want to change.
2325 * @param phone may be modified to strip white space
2326 * @param phone_display_string may be modified to strip white space
2329 sipe_update_user_phone(struct sipe_core_private
*sipe_private
,
2331 const gchar
*phone_type
,
2333 gchar
*phone_display_string
)
2335 sipe_buddy_info_fields phone_node
= SIPE_BUDDY_INFO_WORK_PHONE
; /* work phone by default */
2336 sipe_buddy_info_fields phone_display_node
= SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY
; /* work phone by default */
2338 if(!phone
|| strlen(phone
) == 0) return;
2340 if ((sipe_strequal(phone_type
, "mobile") || sipe_strequal(phone_type
, "cell"))) {
2341 phone_node
= SIPE_BUDDY_INFO_MOBILE_PHONE
;
2342 phone_display_node
= SIPE_BUDDY_INFO_MOBILE_PHONE_DISPLAY
;
2343 } else if (sipe_strequal(phone_type
, "home")) {
2344 phone_node
= SIPE_BUDDY_INFO_HOME_PHONE
;
2345 phone_display_node
= SIPE_BUDDY_INFO_HOME_PHONE_DISPLAY
;
2346 } else if (sipe_strequal(phone_type
, "other")) {
2347 phone_node
= SIPE_BUDDY_INFO_OTHER_PHONE
;
2348 phone_display_node
= SIPE_BUDDY_INFO_OTHER_PHONE_DISPLAY
;
2349 } else if (sipe_strequal(phone_type
, "custom1")) {
2350 phone_node
= SIPE_BUDDY_INFO_CUSTOM1_PHONE
;
2351 phone_display_node
= SIPE_BUDDY_INFO_CUSTOM1_PHONE_DISPLAY
;
2354 sipe_update_user_info(sipe_private
, uri
, phone_node
, phone
);
2355 if (phone_display_string
) {
2356 sipe_update_user_info(sipe_private
, uri
, phone_display_node
, phone_display_string
);
2361 sipe_core_update_calendar(struct sipe_core_public
*sipe_public
)
2363 SIPE_DEBUG_INFO_NOFORMAT("sipe_core_update_calendar: started.");
2366 * If failed, the branch will be disabled for subsequent calls.
2367 * Can't rely that user turned the functionality on in account settings.
2369 sipe_ews_update_calendar(SIPE_CORE_PRIVATE
);
2370 sipe_domino_update_calendar(SIPE_CORE_PRIVATE
);
2372 /* schedule repeat */
2373 sipe_schedule_seconds(SIPE_CORE_PRIVATE
,
2374 "<+update-calendar>",
2376 UPDATE_CALENDAR_INTERVAL
,
2377 (sipe_schedule_action
)sipe_core_update_calendar
,
2380 SIPE_DEBUG_INFO_NOFORMAT("sipe_core_update_calendar: finished.");
2384 * This method motivates Purple's Host (e.g. Pidgin) to update its UI
2385 * by using standard Purple's means of signals and saved statuses.
2387 * Thus all UI elements get updated: Status Button with Note, docklet.
2388 * This is ablolutely important as both our status and note can come
2389 * inbound (roaming) or be updated programmatically (e.g. based on our
2393 sipe_set_purple_account_status_and_note(const PurpleAccount
*account
,
2394 const char *status_id
,
2395 const char *message
,
2396 time_t do_not_publish
[])
2398 PurpleStatus
*status
= purple_account_get_active_status(account
);
2399 gboolean changed
= TRUE
;
2401 if (g_str_equal(status_id
, purple_status_get_id(status
)) &&
2402 sipe_strequal(message
, purple_status_get_attr_string(status
, SIPE_STATUS_ATTR_ID_MESSAGE
)))
2407 if (purple_savedstatus_is_idleaway()) {
2412 PurpleSavedStatus
*saved_status
;
2413 const PurpleStatusType
*acct_status_type
=
2414 purple_status_type_find_with_id(account
->status_types
, status_id
);
2415 PurpleStatusPrimitive primitive
= purple_status_type_get_primitive(acct_status_type
);
2416 sipe_activity activity
= sipe_get_activity_by_token(status_id
);
2418 saved_status
= purple_savedstatus_find_transient_by_type_and_message(primitive
, message
);
2420 purple_savedstatus_set_substatus(saved_status
, account
, acct_status_type
, message
);
2423 /* If this type+message is unique then create a new transient saved status
2424 * Ref: gtkstatusbox.c
2426 if (!saved_status
) {
2428 GList
*active_accts
= purple_accounts_get_all_active();
2430 saved_status
= purple_savedstatus_new(NULL
, primitive
);
2431 purple_savedstatus_set_message(saved_status
, message
);
2433 for (tmp
= active_accts
; tmp
!= NULL
; tmp
= tmp
->next
) {
2434 purple_savedstatus_set_substatus(saved_status
,
2435 (PurpleAccount
*)tmp
->data
, acct_status_type
, message
);
2437 g_list_free(active_accts
);
2440 do_not_publish
[activity
] = time(NULL
);
2441 SIPE_DEBUG_INFO("sipe_set_purple_account_status_and_note: do_not_publish[%s]=%d [now]",
2442 status_id
, (int)do_not_publish
[activity
]);
2444 /* Set the status for each account */
2445 purple_savedstatus_activate(saved_status
);
2449 struct hash_table_delete_payload
{
2450 GHashTable
*hash_table
;
2455 sipe_remove_category_container_publications_cb(const char *name
,
2456 struct sipe_publication
*publication
,
2457 struct hash_table_delete_payload
*payload
)
2459 if (publication
->container
== payload
->container
) {
2460 g_hash_table_remove(payload
->hash_table
, name
);
2464 sipe_remove_category_container_publications(GHashTable
*our_publications
,
2465 const char *category
,
2468 struct hash_table_delete_payload payload
;
2469 payload
.hash_table
= g_hash_table_lookup(our_publications
, category
);
2471 if (!payload
.hash_table
) return;
2473 payload
.container
= container
;
2474 g_hash_table_foreach(payload
.hash_table
, (GHFunc
)sipe_remove_category_container_publications_cb
, &payload
);
2478 send_publish_category_initial(struct sipe_core_private
*sipe_private
);
2481 * When we receive some self (BE) NOTIFY with a new subscriber
2482 * we sends a setSubscribers request to him [SIP-PRES] 4.8
2485 static void sipe_process_roaming_self(struct sipe_core_private
*sipe_private
,
2488 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
2492 const sipe_xml
*node
;
2493 const sipe_xml
*node2
;
2494 char *display_name
= NULL
;
2496 GSList
*category_names
= NULL
;
2497 int aggreg_avail
= 0;
2498 gboolean do_update_status
= FALSE
;
2499 gboolean has_note_cleaned
= FALSE
;
2501 SIPE_DEBUG_INFO_NOFORMAT("sipe_process_roaming_self");
2503 xml
= sipe_xml_parse(msg
->body
, msg
->bodylen
);
2506 contact
= get_contact(sipe_private
);
2507 to
= sip_uri_self(sipe_private
);
2511 /* set list of categories participating in this XML */
2512 for (node
= sipe_xml_child(xml
, "categories/category"); node
; node
= sipe_xml_twin(node
)) {
2513 const gchar
*name
= sipe_xml_attribute(node
, "name");
2514 category_names
= slist_insert_unique_sorted(category_names
, (gchar
*)name
, (GCompareFunc
)strcmp
);
2516 SIPE_DEBUG_INFO("sipe_process_roaming_self: category_names length=%d",
2517 category_names
? (int) g_slist_length(category_names
) : -1);
2518 /* drop category information */
2519 if (category_names
) {
2520 GSList
*entry
= category_names
;
2522 GHashTable
*cat_publications
;
2523 const gchar
*category
= entry
->data
;
2524 entry
= entry
->next
;
2525 SIPE_DEBUG_INFO("sipe_process_roaming_self: dropping category: %s", category
);
2526 cat_publications
= g_hash_table_lookup(sip
->our_publications
, category
);
2527 if (cat_publications
) {
2528 g_hash_table_remove(sip
->our_publications
, category
);
2529 SIPE_DEBUG_INFO("sipe_process_roaming_self: dropped category: %s", category
);
2533 g_slist_free(category_names
);
2534 /* filling our categories reflected in roaming data */
2535 for (node
= sipe_xml_child(xml
, "categories/category"); node
; node
= sipe_xml_twin(node
)) {
2537 const gchar
*name
= sipe_xml_attribute(node
, "name");
2538 guint container
= sipe_xml_int_attribute(node
, "container", -1);
2539 guint instance
= sipe_xml_int_attribute(node
, "instance", -1);
2540 guint version
= sipe_xml_int_attribute(node
, "version", 0);
2541 time_t publish_time
= (tmp
= sipe_xml_attribute(node
, "publishTime")) ?
2542 sipe_utils_str_to_time(tmp
) : 0;
2544 GHashTable
*cat_publications
= g_hash_table_lookup(sip
->our_publications
, name
);
2546 /* Ex. clear note: <category name="note"/> */
2547 if (container
== (guint
)-1) {
2550 do_update_status
= TRUE
;
2554 /* Ex. clear note: <category name="note" container="200"/> */
2555 if (instance
== (guint
)-1) {
2556 if (container
== 200) {
2559 do_update_status
= TRUE
;
2561 SIPE_DEBUG_INFO("sipe_process_roaming_self: removing publications for: %s/%u", name
, container
);
2562 sipe_remove_category_container_publications(
2563 sip
->our_publications
, name
, container
);
2567 /* key is <category><instance><container> */
2568 key
= g_strdup_printf("<%s><%u><%u>", name
, instance
, container
);
2569 SIPE_DEBUG_INFO("sipe_process_roaming_self: key=%s version=%d", key
, version
);
2571 /* capture all userState publication for later clean up if required */
2572 if (sipe_strequal(name
, "state") && (container
== 2 || container
== 3)) {
2573 const sipe_xml
*xn_state
= sipe_xml_child(node
, "state");
2575 if (xn_state
&& sipe_strequal(sipe_xml_attribute(xn_state
, "type"), "userState")) {
2576 struct sipe_publication
*publication
= g_new0(struct sipe_publication
, 1);
2577 publication
->category
= g_strdup(name
);
2578 publication
->instance
= instance
;
2579 publication
->container
= container
;
2580 publication
->version
= version
;
2582 if (!sip
->user_state_publications
) {
2583 sip
->user_state_publications
= g_hash_table_new_full(
2584 g_str_hash
, g_str_equal
,
2585 g_free
, (GDestroyNotify
)free_publication
);
2587 g_hash_table_insert(sip
->user_state_publications
, g_strdup(key
), publication
);
2588 SIPE_DEBUG_INFO("sipe_process_roaming_self: added to user_state_publications key=%s version=%d",
2593 if (sipe_is_our_publication(sipe_private
, key
)) {
2594 struct sipe_publication
*publication
= g_new0(struct sipe_publication
, 1);
2596 publication
->category
= g_strdup(name
);
2597 publication
->instance
= instance
;
2598 publication
->container
= container
;
2599 publication
->version
= version
;
2601 /* filling publication->availability */
2602 if (sipe_strequal(name
, "state")) {
2603 const sipe_xml
*xn_state
= sipe_xml_child(node
, "state");
2604 const sipe_xml
*xn_avail
= sipe_xml_child(xn_state
, "availability");
2607 gchar
*avail_str
= sipe_xml_data(xn_avail
);
2609 publication
->availability
= atoi(avail_str
);
2613 /* for calendarState */
2614 if (xn_state
&& sipe_strequal(sipe_xml_attribute(xn_state
, "type"), "calendarState")) {
2615 const sipe_xml
*xn_activity
= sipe_xml_child(xn_state
, "activity");
2616 struct sipe_cal_event
*event
= g_new0(struct sipe_cal_event
, 1);
2618 event
->start_time
= sipe_utils_str_to_time(sipe_xml_attribute(xn_state
, "startTime"));
2620 if (sipe_strequal(sipe_xml_attribute(xn_activity
, "token"),
2621 sipe_activity_map
[SIPE_ACTIVITY_IN_MEETING
].token
))
2623 event
->is_meeting
= TRUE
;
2626 event
->subject
= sipe_xml_data(sipe_xml_child(xn_state
, "meetingSubject"));
2627 event
->location
= sipe_xml_data(sipe_xml_child(xn_state
, "meetingLocation"));
2629 publication
->cal_event_hash
= sipe_cal_event_hash(event
);
2630 SIPE_DEBUG_INFO("sipe_process_roaming_self: hash=%s",
2631 publication
->cal_event_hash
);
2632 sipe_cal_event_free(event
);
2635 /* filling publication->note */
2636 if (sipe_strequal(name
, "note")) {
2637 const sipe_xml
*xn_body
= sipe_xml_child(node
, "note/body");
2639 if (!has_note_cleaned
) {
2640 has_note_cleaned
= TRUE
;
2644 sip
->note_since
= publish_time
;
2646 do_update_status
= TRUE
;
2649 g_free(publication
->note
);
2650 publication
->note
= NULL
;
2654 publication
->note
= g_markup_escape_text((tmp
= sipe_xml_data(xn_body
)), -1);
2656 if (publish_time
>= sip
->note_since
) {
2658 sip
->note
= g_strdup(publication
->note
);
2659 sip
->note_since
= publish_time
;
2660 sip
->is_oof_note
= sipe_strequal(sipe_xml_attribute(xn_body
, "type"), "OOF");
2662 do_update_status
= TRUE
;
2667 /* filling publication->fb_start_str, free_busy_base64, working_hours_xml_str */
2668 if (sipe_strequal(name
, "calendarData") && (publication
->container
== 300)) {
2669 const sipe_xml
*xn_free_busy
= sipe_xml_child(node
, "calendarData/freeBusy");
2670 const sipe_xml
*xn_working_hours
= sipe_xml_child(node
, "calendarData/WorkingHours");
2672 publication
->fb_start_str
= g_strdup(sipe_xml_attribute(xn_free_busy
, "startTime"));
2673 publication
->free_busy_base64
= sipe_xml_data(xn_free_busy
);
2675 if (xn_working_hours
) {
2676 publication
->working_hours_xml_str
= sipe_xml_stringify(xn_working_hours
);
2680 if (!cat_publications
) {
2681 cat_publications
= g_hash_table_new_full(
2682 g_str_hash
, g_str_equal
,
2683 g_free
, (GDestroyNotify
)free_publication
);
2684 g_hash_table_insert(sip
->our_publications
, g_strdup(name
), cat_publications
);
2685 SIPE_DEBUG_INFO("sipe_process_roaming_self: added GHashTable cat=%s", name
);
2687 g_hash_table_insert(cat_publications
, g_strdup(key
), publication
);
2688 SIPE_DEBUG_INFO("sipe_process_roaming_self: added key=%s version=%d", key
, version
);
2692 /* aggregateState (not an our publication) from 2-nd container */
2693 if (sipe_strequal(name
, "state") && container
== 2) {
2694 const sipe_xml
*xn_state
= sipe_xml_child(node
, "state");
2696 if (xn_state
&& sipe_strequal(sipe_xml_attribute(xn_state
, "type"), "aggregateState")) {
2697 const sipe_xml
*xn_avail
= sipe_xml_child(xn_state
, "availability");
2700 gchar
*avail_str
= sipe_xml_data(xn_avail
);
2702 aggreg_avail
= atoi(avail_str
);
2707 do_update_status
= TRUE
;
2711 /* userProperties published by server from AD */
2712 if (!sip
->csta
&& sipe_strequal(name
, "userProperties")) {
2713 const sipe_xml
*line
;
2714 /* line, for Remote Call Control (RCC) */
2715 for (line
= sipe_xml_child(node
, "userProperties/lines/line"); line
; line
= sipe_xml_twin(line
)) {
2716 const gchar
*line_server
= sipe_xml_attribute(line
, "lineServer");
2717 const gchar
*line_type
= sipe_xml_attribute(line
, "lineType");
2720 if (!line_server
|| !(sipe_strequal(line_type
, "Rcc") || sipe_strequal(line_type
, "Dual"))) continue;
2722 line_uri
= sipe_xml_data(line
);
2724 SIPE_DEBUG_INFO("sipe_process_roaming_self: line_uri=%s server=%s", line_uri
, line_server
);
2725 sip_csta_open(sipe_private
, line_uri
, line_server
);
2733 SIPE_DEBUG_INFO("sipe_process_roaming_self: sip->our_publications size=%d",
2734 sip
->our_publications
? (int) g_hash_table_size(sip
->our_publications
) : -1);
2737 for (node
= sipe_xml_child(xml
, "containers/container"); node
; node
= sipe_xml_twin(node
)) {
2738 guint id
= sipe_xml_int_attribute(node
, "id", 0);
2739 struct sipe_container
*container
= sipe_find_container(sipe_private
, id
);
2742 sip
->containers
= g_slist_remove(sip
->containers
, container
);
2743 SIPE_DEBUG_INFO("sipe_process_roaming_self: removed existing container id=%d v%d", container
->id
, container
->version
);
2744 free_container(container
);
2746 container
= g_new0(struct sipe_container
, 1);
2748 container
->version
= sipe_xml_int_attribute(node
, "version", 0);
2749 sip
->containers
= g_slist_append(sip
->containers
, container
);
2750 SIPE_DEBUG_INFO("sipe_process_roaming_self: added container id=%d v%d", container
->id
, container
->version
);
2752 for (node2
= sipe_xml_child(node
, "member"); node2
; node2
= sipe_xml_twin(node2
)) {
2753 struct sipe_container_member
*member
= g_new0(struct sipe_container_member
, 1);
2754 member
->type
= g_strdup(sipe_xml_attribute(node2
, "type"));
2755 member
->value
= g_strdup(sipe_xml_attribute(node2
, "value"));
2756 container
->members
= g_slist_append(container
->members
, member
);
2757 SIPE_DEBUG_INFO("sipe_process_roaming_self: added container member type=%s value=%s",
2758 member
->type
, member
->value
? member
->value
: "");
2762 SIPE_DEBUG_INFO("sipe_process_roaming_self: sip->access_level_set=%s", sip
->access_level_set
? "TRUE" : "FALSE");
2763 if (!sip
->access_level_set
&& sipe_xml_child(xml
, "containers")) {
2764 char *container_xmls
= NULL
;
2765 int sameEnterpriseAL
= sipe_find_access_level(sipe_private
, "sameEnterprise", NULL
, NULL
);
2766 int federatedAL
= sipe_find_access_level(sipe_private
, "federated", NULL
, NULL
);
2768 SIPE_DEBUG_INFO("sipe_process_roaming_self: sameEnterpriseAL=%d", sameEnterpriseAL
);
2769 SIPE_DEBUG_INFO("sipe_process_roaming_self: federatedAL=%d", federatedAL
);
2770 /* initial set-up to let counterparties see your status */
2771 if (sameEnterpriseAL
< 0) {
2772 struct sipe_container
*container
= sipe_find_container(sipe_private
, 200);
2773 guint version
= container
? container
->version
: 0;
2774 sipe_send_container_members_prepare(200, version
, "add", "sameEnterprise", NULL
, &container_xmls
);
2776 if (federatedAL
< 0) {
2777 struct sipe_container
*container
= sipe_find_container(sipe_private
, 100);
2778 guint version
= container
? container
->version
: 0;
2779 sipe_send_container_members_prepare(100, version
, "add", "federated", NULL
, &container_xmls
);
2781 sip
->access_level_set
= TRUE
;
2783 if (container_xmls
) {
2784 sipe_send_set_container_members(sipe_private
, container_xmls
);
2786 g_free(container_xmls
);
2789 /* Refresh contacts' blocked status */
2790 sipe_refresh_blocked_status(sipe_private
);
2793 for (node
= sipe_xml_child(xml
, "subscribers/subscriber"); node
; node
= sipe_xml_twin(node
)) {
2795 const char *acknowledged
;
2799 user
= sipe_xml_attribute(node
, "user"); /* without 'sip:' prefix */
2800 if (!user
) continue;
2801 SIPE_DEBUG_INFO("sipe_process_roaming_self: user %s", user
);
2802 display_name
= g_strdup(sipe_xml_attribute(node
, "displayName"));
2803 uri
= sip_uri_from_name(user
);
2805 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_DISPLAY_NAME
, display_name
);
2807 acknowledged
= sipe_xml_attribute(node
, "acknowledged");
2808 if(sipe_strcase_equal(acknowledged
,"false")){
2809 SIPE_DEBUG_INFO("sipe_process_roaming_self: user added you %s", user
);
2810 if (!sipe_backend_buddy_find(SIPE_CORE_PUBLIC
, uri
, NULL
)) {
2811 sipe_backend_buddy_request_add(SIPE_CORE_PUBLIC
, uri
, display_name
);
2814 hdr
= g_strdup_printf(
2816 "Content-Type: application/msrtc-presence-setsubscriber+xml\r\n", contact
);
2818 body
= g_strdup_printf(
2819 "<setSubscribers xmlns=\"http://schemas.microsoft.com/2006/09/sip/presence-subscribers\">"
2820 "<subscriber user=\"%s\" acknowledged=\"true\"/>"
2821 "</setSubscribers>", user
);
2823 sip_transport_service(sipe_private
,
2831 g_free(display_name
);
2838 /* Publish initial state if not yet.
2839 * Assuming this happens on initial responce to subscription to roaming-self
2840 * so we've already updated our roaming data in full.
2843 if (!sip
->initial_state_published
) {
2844 send_publish_category_initial(sipe_private
);
2845 sipe_groupchat_init(sipe_private
);
2846 sip
->initial_state_published
= TRUE
;
2848 sipe_schedule_seconds(sipe_private
,
2849 "<+update-calendar>",
2851 UPDATE_CALENDAR_DELAY
,
2852 (sipe_schedule_action
)sipe_core_update_calendar
,
2854 do_update_status
= FALSE
;
2855 } else if (aggreg_avail
) {
2857 g_free(sip
->status
);
2858 if (aggreg_avail
&& aggreg_avail
< 18000) { /* not offline */
2859 sip
->status
= g_strdup(sipe_get_status_by_availability(aggreg_avail
, NULL
));
2861 sip
->status
= g_strdup(SIPE_STATUS_ID_INVISIBLE
); /* not not let offline status switch us off */
2865 if (do_update_status
) {
2866 SIPE_DEBUG_INFO("sipe_process_roaming_self: switch to '%s' for the account", sip
->status
);
2867 sipe_set_purple_account_status_and_note(sip
->account
, sip
->status
, sip
->note
, sip
->do_not_publish
);
2873 /* IM Session (INVITE and MESSAGE methods) */
2875 /* EndPoints: "alice alisson" <sip:alice@atlanta.local>, <sip:bob@atlanta.local>;epid=ebca82d94d, <sip:carol@atlanta.local> */
2877 get_end_points (struct sipe_core_private
*sipe_private
,
2878 struct sip_session
*session
)
2882 if (session
== NULL
) {
2886 res
= g_strdup_printf("<sip:%s>", sipe_private
->username
);
2888 SIPE_DIALOG_FOREACH
{
2890 res
= g_strdup_printf("%s, <%s>", res
, dialog
->with
);
2893 if (dialog
->theirepid
) {
2895 res
= g_strdup_printf("%s;epid=%s", res
, dialog
->theirepid
);
2898 } SIPE_DIALOG_FOREACH_END
;
2904 process_options_response(SIPE_UNUSED_PARAMETER
struct sipe_core_private
*sipe_private
,
2906 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
2908 gboolean ret
= TRUE
;
2910 if (msg
->response
!= 200) {
2911 SIPE_DEBUG_INFO("process_options_response: OPTIONS response is %d", msg
->response
);
2915 SIPE_DEBUG_INFO("process_options_response: body:\n%s", msg
->body
? msg
->body
: "");
2921 * Asks UA/proxy about its capabilities.
2923 static void sipe_options_request(struct sipe_core_private
*sipe_private
,
2926 gchar
*to
= sip_uri(who
);
2927 gchar
*contact
= get_contact(sipe_private
);
2928 gchar
*request
= g_strdup_printf(
2929 "Accept: application/sdp\r\n"
2930 "Contact: %s\r\n", contact
);
2933 sip_transport_request(sipe_private
,
2940 process_options_response
);
2947 sipe_present_info(struct sipe_core_private
*sipe_private
,
2948 struct sip_session
*session
,
2949 const gchar
*message
)
2951 sipe_backend_notify_message_info(SIPE_CORE_PUBLIC
,
2952 session
->chat_session
? session
->chat_session
->backend
: NULL
,
2958 sipe_present_err(struct sipe_core_private
*sipe_private
,
2959 struct sip_session
*session
,
2960 const gchar
*message
)
2962 sipe_backend_notify_message_error(SIPE_CORE_PUBLIC
,
2963 session
->chat_session
? session
->chat_session
->backend
: NULL
,
2969 sipe_present_message_undelivered_err(struct sipe_core_private
*sipe_private
,
2970 struct sip_session
*session
,
2974 const gchar
*message
)
2976 char *msg
, *msg_tmp
, *msg_tmp2
;
2979 msg_tmp
= message
? sipe_backend_markup_strip_html(message
) : NULL
;
2980 msg
= msg_tmp
? g_strdup_printf("<font color=\"#888888\"></b>%s<b></font>", msg_tmp
) : NULL
;
2982 /* Service unavailable; Server Internal Error; Server Time-out */
2983 if (sip_error
== 606 && sip_warning
== 309) { /* Not acceptable all. */ /* Message contents not allowed by policy */
2984 label
= _("Your message or invitation was not delivered, possibly because it contains a hyperlink or other content that the system administrator has blocked.");
2987 } else if (sip_error
== 500 || sip_error
== 503 || sip_error
== 504 || sip_error
== 603) {
2988 label
= _("This message was not delivered to %s because the service is not available");
2989 } else if (sip_error
== 486) { /* Busy Here */
2990 label
= _("This message was not delivered to %s because one or more recipients do not want to be disturbed");
2991 } else if (sip_error
== 415) { /* Unsupported media type */
2992 label
= _("This message was not delivered to %s because one or more recipients don't support this type of message");
2994 label
= _("This message was not delivered to %s because one or more recipients are offline");
2997 msg_tmp
= g_strdup_printf( "%s%s\n%s" ,
2998 msg_tmp2
= g_strdup_printf(label
, who
? who
: ""),
3001 sipe_present_err(sipe_private
, session
, msg_tmp
);
3009 process_message_response(struct sipe_core_private
*sipe_private
,
3011 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
3013 gboolean ret
= TRUE
;
3014 gchar
*with
= parse_from(sipmsg_find_header(msg
, "To"));
3015 struct sip_session
*session
= sipe_session_find_im(sipe_private
, with
);
3016 struct sip_dialog
*dialog
;
3019 struct queued_message
*message
;
3022 SIPE_DEBUG_INFO_NOFORMAT("process_message_response: unable to find IM session");
3027 dialog
= sipe_dialog_find(session
, with
);
3029 SIPE_DEBUG_INFO_NOFORMAT("process_message_response: session outgoing dialog is NULL");
3034 cseq
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "CSeq"), NULL
, " ", NULL
);
3035 key
= g_strdup_printf("<%s><%d><MESSAGE><%s>", sipmsg_find_header(msg
, "Call-ID"), atoi(cseq
), with
);
3037 message
= g_hash_table_lookup(session
->unconfirmed_messages
, key
);
3039 if (msg
->response
>= 400) {
3040 sipe_backend_buddy pbuddy
;
3041 gchar
*alias
= g_strdup(with
);
3042 const char *warn_hdr
= sipmsg_find_header(msg
, "Warning");
3045 SIPE_DEBUG_INFO_NOFORMAT("process_message_response: MESSAGE response >= 400");
3048 gchar
**parts
= g_strsplit(warn_hdr
, " ", 2);
3050 warning
= atoi(parts
[0]);
3055 /* cancel file transfer as rejected by server */
3056 if (msg
->response
== 606 && /* Not acceptable all. */
3057 warning
== 309 && /* Message contents not allowed by policy */
3058 message
&& g_str_has_prefix(message
->content_type
, "text/x-msmsgsinvite"))
3060 GSList
*parsed_body
= sipe_ft_parse_msg_body(msg
->body
);
3061 sipe_ft_incoming_cancel(dialog
, parsed_body
);
3062 sipe_utils_nameval_free(parsed_body
);
3065 if ((pbuddy
= sipe_backend_buddy_find(SIPE_CORE_PUBLIC
, with
, NULL
))) {
3067 alias
= sipe_backend_buddy_get_alias(SIPE_CORE_PUBLIC
,pbuddy
);
3070 sipe_present_message_undelivered_err(sipe_private
, session
, msg
->response
, warning
, alias
, (message
? message
->body
: NULL
));
3072 /* drop dangling IM sessions: assume that BYE from remote never reached us */
3073 if (msg
->response
== 408 || /* Request timeout */
3074 msg
->response
== 480 || /* Temporarily Unavailable */
3075 msg
->response
== 481) { /* Call/Transaction Does Not Exist */
3076 SIPE_DEBUG_INFO_NOFORMAT("process_message_response: assuming dangling IM session, dropping it.");
3077 sip_transport_bye(sipe_private
, dialog
);
3079 /* We might not get a valid reply to our BYE,
3080 so make sure the dialog is removed for sure. */
3081 sipe_dialog_remove(session
, with
);
3088 const gchar
*message_id
= sipmsg_find_header(msg
, "Message-Id");
3090 g_hash_table_insert(session
->conf_unconfirmed_messages
, g_strdup(message_id
), g_strdup(message
->body
));
3091 SIPE_DEBUG_INFO("process_message_response: added message with id %s to conf_unconfirmed_messages(count=%d)",
3092 message_id
, g_hash_table_size(session
->conf_unconfirmed_messages
));
3095 g_hash_table_remove(session
->unconfirmed_messages
, key
);
3096 SIPE_DEBUG_INFO("process_message_response: removed message %s from unconfirmed_messages(count=%d)",
3097 key
, g_hash_table_size(session
->unconfirmed_messages
));
3103 if (ret
) sipe_im_process_queue(sipe_private
, session
);
3108 process_message_timeout(struct sipe_core_private
*sipe_private
,
3110 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
3112 gchar
*with
= parse_from(sipmsg_find_header(msg
, "To"));
3113 struct sip_session
*session
= sipe_session_find_im(sipe_private
, with
);
3116 sipe_backend_buddy pbuddy
;
3117 gchar
*alias
= NULL
;
3120 SIPE_DEBUG_INFO_NOFORMAT("process_message_timeout: unable to find IM session");
3125 /* Remove timed-out message from unconfirmed list */
3126 cseq
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "CSeq"), NULL
, " ", NULL
);
3127 key
= g_strdup_printf("<%s><%d><MESSAGE><%s>", sipmsg_find_header(msg
, "Call-ID"), atoi(cseq
), with
);
3129 g_hash_table_remove(session
->unconfirmed_messages
, key
);
3130 SIPE_DEBUG_INFO("process_message_timeout: removed message %s from unconfirmed_messages(count=%d)",
3131 key
, g_hash_table_size(session
->unconfirmed_messages
));
3134 if ((pbuddy
= sipe_backend_buddy_find(SIPE_CORE_PUBLIC
, with
, NULL
))) {
3135 alias
= sipe_backend_buddy_get_alias(SIPE_CORE_PUBLIC
,pbuddy
);
3138 sipe_present_message_undelivered_err(sipe_private
, session
, -1, -1,
3139 alias
? alias
: with
,
3147 static void sipe_send_message(struct sipe_core_private
*sipe_private
,
3148 struct sip_dialog
*dialog
,
3149 const char *msg
, const char *content_type
)
3153 char *msgtext
= NULL
;
3154 const gchar
*msgr
= "";
3157 if (content_type
== NULL
)
3158 content_type
= "text/plain";
3160 if (!g_str_has_prefix(content_type
, "text/x-msmsgsinvite")) {
3164 sipe_parse_html(msg
, &msgformat
, &msgtext
);
3165 SIPE_DEBUG_INFO("sipe_send_message: msgformat=%s", msgformat
);
3167 msgr_value
= sipmsg_get_msgr_string(msgformat
);
3170 msgr
= tmp2
= g_strdup_printf(";msgr=%s", msgr_value
);
3174 msgtext
= g_strdup(msg
);
3177 tmp
= get_contact(sipe_private
);
3178 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8\r\n");
3179 //hdr = g_strdup("Content-Type: text/rtf\r\n");
3180 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8;msgr=WAAtAE0ATQBTAC....AoADQA\r\nSupported: timer\r\n");
3182 hdr
= g_strdup_printf("Contact: %s\r\nContent-Type: %s; charset=UTF-8%s\r\n", tmp
, content_type
, msgr
);
3186 sip_transport_request_timeout(sipe_private
,
3193 process_message_response
,
3195 process_message_timeout
);
3202 sipe_im_process_queue (struct sipe_core_private
*sipe_private
,
3203 struct sip_session
* session
)
3205 GSList
*entry2
= session
->outgoing_message_queue
;
3207 struct queued_message
*msg
= entry2
->data
;
3209 /* for multiparty chat or conference */
3210 if (session
->chat_session
) {
3211 gchar
*who
= sip_uri_self(sipe_private
);
3212 sipe_backend_chat_message(SIPE_CORE_PUBLIC
,
3213 session
->chat_session
->backend
,
3219 SIPE_DIALOG_FOREACH
{
3221 struct queued_message
*message
;
3223 if (dialog
->outgoing_invite
) continue; /* do not send messages as INVITE is not responded. */
3225 message
= g_new0(struct queued_message
,1);
3226 message
->body
= g_strdup(msg
->body
);
3227 if (msg
->content_type
!= NULL
)
3228 message
->content_type
= g_strdup(msg
->content_type
);
3230 key
= g_strdup_printf("<%s><%d><MESSAGE><%s>", dialog
->callid
, (dialog
->cseq
) + 1, dialog
->with
);
3231 g_hash_table_insert(session
->unconfirmed_messages
, g_strdup(key
), message
);
3232 SIPE_DEBUG_INFO("sipe_im_process_queue: added message %s to unconfirmed_messages(count=%d)",
3233 key
, g_hash_table_size(session
->unconfirmed_messages
));
3236 sipe_send_message(sipe_private
, dialog
, msg
->body
, msg
->content_type
);
3237 } SIPE_DIALOG_FOREACH_END
;
3239 entry2
= sipe_session_dequeue_message(session
);
3244 sipe_refer_notify(struct sipe_core_private
*sipe_private
,
3245 struct sip_session
*session
,
3252 struct sip_dialog
*dialog
= sipe_dialog_find(session
, who
);
3254 hdr
= g_strdup_printf(
3256 "Subscription-State: %s\r\n"
3257 "Content-Type: message/sipfrag\r\n",
3258 status
>= 200 ? "terminated" : "active");
3260 body
= g_strdup_printf(
3261 "SIP/2.0 %d %s\r\n",
3264 sip_transport_request(sipe_private
,
3278 process_invite_response(struct sipe_core_private
*sipe_private
,
3279 struct sipmsg
*msg
, struct transaction
*trans
)
3281 gchar
*with
= parse_from(sipmsg_find_header(msg
, "To"));
3282 struct sip_session
*session
;
3283 struct sip_dialog
*dialog
;
3286 struct queued_message
*message
;
3287 struct sipmsg
*request_msg
= trans
->msg
;
3289 const gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
3292 session
= sipe_session_find_chat_or_im(sipe_private
, callid
, with
);
3294 SIPE_DEBUG_INFO_NOFORMAT("process_invite_response: unable to find IM session");
3299 dialog
= sipe_dialog_find(session
, with
);
3301 SIPE_DEBUG_INFO_NOFORMAT("process_invite_response: session outgoing dialog is NULL");
3306 sipe_dialog_parse(dialog
, msg
, TRUE
);
3308 cseq
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "CSeq"), NULL
, " ", NULL
);
3309 key
= g_strdup_printf("<%s><%d><INVITE>", dialog
->callid
, atoi(cseq
));
3311 message
= g_hash_table_lookup(session
->unconfirmed_messages
, key
);
3313 if (msg
->response
!= 200) {
3314 sipe_backend_buddy pbuddy
;
3315 gchar
*alias
= g_strdup(with
);
3316 const char *warn_hdr
= sipmsg_find_header(msg
, "Warning");
3319 SIPE_DEBUG_INFO_NOFORMAT("process_invite_response: INVITE response not 200");
3322 gchar
**parts
= g_strsplit(warn_hdr
, " ", 2);
3324 warning
= atoi(parts
[0]);
3329 /* cancel file transfer as rejected by server */
3330 if (msg
->response
== 606 && /* Not acceptable all. */
3331 warning
== 309 && /* Message contents not allowed by policy */
3332 message
&& g_str_has_prefix(message
->content_type
, "text/x-msmsgsinvite"))
3334 GSList
*parsed_body
= sipe_ft_parse_msg_body(message
->body
);
3335 sipe_ft_incoming_cancel(dialog
, parsed_body
);
3336 sipe_utils_nameval_free(parsed_body
);
3339 if ((pbuddy
= sipe_backend_buddy_find(SIPE_CORE_PUBLIC
, with
, NULL
))) {
3341 alias
= sipe_backend_buddy_get_alias(SIPE_CORE_PUBLIC
, pbuddy
);
3345 sipe_present_message_undelivered_err(sipe_private
, session
, msg
->response
, warning
, alias
, message
->body
);
3347 gchar
*tmp_msg
= g_strdup_printf(_("Failed to invite %s"), alias
);
3348 sipe_present_err(sipe_private
, session
, tmp_msg
);
3352 sipe_dialog_remove(session
, with
);
3354 if (session
->is_groupchat
) {
3355 sipe_groupchat_invite_failed(sipe_private
, session
);
3365 sip_transport_ack(sipe_private
, dialog
);
3366 dialog
->outgoing_invite
= NULL
;
3367 dialog
->is_established
= TRUE
;
3369 referred_by
= parse_from(sipmsg_find_header(request_msg
, "Referred-By"));
3371 sipe_refer_notify(sipe_private
, session
, referred_by
, 200, "OK");
3372 g_free(referred_by
);
3375 /* add user to chat if it is a multiparty session */
3376 if (session
->chat_session
&&
3377 (session
->chat_session
->type
== SIPE_CHAT_TYPE_MULTIPARTY
)) {
3378 sipe_backend_chat_add(session
->chat_session
->backend
,
3383 if (session
->is_groupchat
) {
3384 sipe_groupchat_invite_response(sipe_private
, dialog
);
3387 if(g_slist_find_custom(dialog
->supported
, "ms-text-format", (GCompareFunc
)g_ascii_strcasecmp
)) {
3388 SIPE_DEBUG_INFO_NOFORMAT("process_invite_response: remote system accepted message in INVITE");
3389 sipe_session_dequeue_message(session
);
3392 sipe_im_process_queue(sipe_private
, session
);
3394 g_hash_table_remove(session
->unconfirmed_messages
, key
);
3395 SIPE_DEBUG_INFO("process_invite_response: removed message %s from unconfirmed_messages(count=%d)",
3396 key
, g_hash_table_size(session
->unconfirmed_messages
));
3405 sipe_invite(struct sipe_core_private
*sipe_private
,
3406 struct sip_session
*session
,
3408 const gchar
*msg_body
,
3409 const gchar
*msg_content_type
,
3410 const gchar
*referred_by
,
3411 const gboolean is_triggered
)
3418 char *ms_text_format
= NULL
;
3419 char *ms_conversation_id
= NULL
;
3420 gchar
*roster_manager
;
3422 gchar
*referred_by_str
;
3423 gboolean is_multiparty
=
3424 session
->chat_session
&&
3425 (session
->chat_session
->type
== SIPE_CHAT_TYPE_MULTIPARTY
);
3426 struct sip_dialog
*dialog
= sipe_dialog_find(session
, who
);
3428 if (dialog
&& dialog
->is_established
) {
3429 SIPE_DEBUG_INFO("session with %s already has a dialog open", who
);
3434 dialog
= sipe_dialog_add(session
);
3435 dialog
->callid
= session
->callid
? g_strdup(session
->callid
) : gencallid();
3436 dialog
->with
= g_strdup(who
);
3439 if (!(dialog
->ourtag
)) {
3440 dialog
->ourtag
= gentag();
3446 char *msgtext
= NULL
;
3448 const gchar
*msgr
= "";
3450 struct queued_message
*message
;
3453 if (!g_str_has_prefix(msg_content_type
, "text/x-msmsgsinvite")) {
3457 sipe_parse_html(msg_body
, &msgformat
, &msgtext
);
3458 SIPE_DEBUG_INFO("sipe_invite: msgformat=%s", msgformat
);
3460 msgr_value
= sipmsg_get_msgr_string(msgformat
);
3463 msgr
= tmp
= g_strdup_printf(";msgr=%s", msgr_value
);
3467 /* When Sipe reconnects after a crash, we are not able
3468 * to send messages to contacts with which we had open
3469 * conversations when the crash occured. Server sends
3470 * error response with reason="This client has an IM
3471 * session with the same conversation ID"
3473 * Setting random Ms-Conversation-ID prevents this problem
3474 * so we can continue the conversation. */
3475 ms_conversation_id
= g_strdup_printf("Ms-Conversation-ID: %u\r\n",
3476 rand() % 1000000000);
3478 msgtext
= g_strdup(msg_body
);
3481 base64_msg
= g_base64_encode((guchar
*) msgtext
, strlen(msgtext
));
3482 ms_text_format
= g_strdup_printf(SIPE_INVITE_TEXT
,
3483 msg_content_type
? msg_content_type
: "text/plain",
3490 message
= g_new0(struct queued_message
,1);
3491 message
->body
= g_strdup(msg_body
);
3492 if (msg_content_type
!= NULL
)
3493 message
->content_type
= g_strdup(msg_content_type
);
3495 key
= g_strdup_printf("<%s><%d><INVITE>", dialog
->callid
, (dialog
->cseq
) + 1);
3496 g_hash_table_insert(session
->unconfirmed_messages
, g_strdup(key
), message
);
3497 SIPE_DEBUG_INFO("sipe_invite: added message %s to unconfirmed_messages(count=%d)",
3498 key
, g_hash_table_size(session
->unconfirmed_messages
));
3502 contact
= get_contact(sipe_private
);
3503 end_points
= get_end_points(sipe_private
, session
);
3504 self
= sip_uri_self(sipe_private
);
3505 roster_manager
= g_strdup_printf(
3506 "Roster-Manager: %s\r\n"
3507 "EndPoints: %s\r\n",
3510 referred_by_str
= referred_by
?
3512 "Referred-By: %s\r\n",
3515 hdr
= g_strdup_printf(
3516 "Supported: ms-sender\r\n"
3523 "Content-Type: application/sdp\r\n",
3524 is_multiparty
&& sipe_strcase_equal(session
->chat_session
->id
, self
) ? roster_manager
: "",
3526 is_triggered
? "TriggeredInvite: TRUE\r\n" : "",
3527 is_triggered
|| is_multiparty
? "Require: com.microsoft.rtc-multiparty\r\n" : "",
3529 ms_text_format
? ms_text_format
: "",
3530 ms_conversation_id
? ms_conversation_id
: "");
3531 g_free(ms_text_format
);
3532 g_free(ms_conversation_id
);
3535 body
= g_strdup_printf(
3537 "o=- 0 0 IN IP4 %s\r\n"
3541 "m=%s %d sip null\r\n"
3542 "a=accept-types:" SDP_ACCEPT_TYPES
"\r\n",
3543 sipe_backend_network_ip_address(),
3544 sipe_backend_network_ip_address(),
3545 SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
) ? "message" : "x-ms-message",
3546 sip_transport_port(sipe_private
));
3548 dialog
->outgoing_invite
= sip_transport_request(sipe_private
,
3555 process_invite_response
);
3558 g_free(roster_manager
);
3560 g_free(referred_by_str
);
3567 sipe_convo_closed(PurpleConnection
* gc
, const char *who
)
3569 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
3571 SIPE_DEBUG_INFO("conversation with %s closed", who
);
3572 sipe_session_close(sipe_private
,
3573 sipe_session_find_im(sipe_private
, who
));
3576 int sipe_im_send(PurpleConnection
*gc
, const char *who
, const char *what
,
3577 SIPE_UNUSED_PARAMETER PurpleMessageFlags flags
)
3579 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
3580 struct sip_session
*session
;
3581 struct sip_dialog
*dialog
;
3582 gchar
*uri
= sip_uri(who
);
3584 SIPE_DEBUG_INFO("sipe_im_send what='%s'", what
);
3586 session
= sipe_session_find_or_add_im(sipe_private
, uri
);
3587 dialog
= sipe_dialog_find(session
, uri
);
3589 // Queue the message
3590 sipe_session_enqueue_message(session
, what
, NULL
);
3592 if (dialog
&& !dialog
->outgoing_invite
) {
3593 sipe_im_process_queue(sipe_private
, session
);
3594 } else if (!dialog
|| !dialog
->outgoing_invite
) {
3595 // Need to send the INVITE to get the outgoing dialog setup
3596 sipe_invite(sipe_private
, session
, uri
, what
, NULL
, NULL
, FALSE
);
3604 * Returns 2005-style activity and Availability.
3606 * @param status Sipe statis id.
3609 sipe_get_act_avail_by_status_2005(const char *status
,
3613 int avail
= 300; /* online */
3614 int act
= 400; /* Available */
3616 if (sipe_strequal(status
, SIPE_STATUS_ID_AWAY
)) {
3618 //} else if (sipe_strequal(status, SIPE_STATUS_ID_LUNCH)) {
3620 } else if (sipe_strequal(status
, SIPE_STATUS_ID_BRB
)) {
3622 } else if (sipe_strequal(status
, SIPE_STATUS_ID_AVAILABLE
)) {
3624 //} else if (sipe_strequal(status, SIPE_STATUS_ID_ON_PHONE)) {
3626 } else if (sipe_strequal(status
, SIPE_STATUS_ID_BUSY
) ||
3627 sipe_strequal(status
, SIPE_STATUS_ID_DND
)) {
3629 } else if (sipe_strequal(status
, SIPE_STATUS_ID_INVISIBLE
) ||
3630 sipe_strequal(status
, SIPE_STATUS_ID_OFFLINE
)) {
3631 avail
= 0; /* offline */
3634 act
= 400; /* Available */
3637 if (activity
) *activity
= act
;
3638 if (availability
) *availability
= avail
;
3644 * @param activity 2005 aggregated activity. Ex.: 600
3645 * @param availablity 2005 aggregated availablity. Ex.: 300
3648 sipe_get_status_by_act_avail_2005(const int activity
,
3649 const int availablity
,
3650 char **activity_desc
)
3652 const char *status_id
= NULL
;
3653 const char *act
= NULL
;
3655 if (activity
< 150) {
3656 status_id
= SIPE_STATUS_ID_AWAY
;
3657 } else if (activity
< 200) {
3658 //status_id = SIPE_STATUS_ID_LUNCH;
3659 status_id
= SIPE_STATUS_ID_AWAY
;
3660 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_LUNCH
);
3661 } else if (activity
< 300) {
3662 //status_id = SIPE_STATUS_ID_IDLE;
3663 status_id
= SIPE_STATUS_ID_AWAY
;
3664 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_INACTIVE
);
3665 } else if (activity
< 400) {
3666 status_id
= SIPE_STATUS_ID_BRB
;
3667 } else if (activity
< 500) {
3668 status_id
= SIPE_STATUS_ID_AVAILABLE
;
3669 } else if (activity
< 600) {
3670 //status_id = SIPE_STATUS_ID_ON_PHONE;
3671 status_id
= SIPE_STATUS_ID_BUSY
;
3672 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_ON_PHONE
);
3673 } else if (activity
< 700) {
3674 status_id
= SIPE_STATUS_ID_BUSY
;
3675 } else if (activity
< 800) {
3676 status_id
= SIPE_STATUS_ID_AWAY
;
3678 status_id
= SIPE_STATUS_ID_AVAILABLE
;
3681 if (availablity
< 100)
3682 status_id
= SIPE_STATUS_ID_OFFLINE
;
3684 if (activity_desc
&& act
) {
3685 g_free(*activity_desc
);
3686 *activity_desc
= g_strdup(act
);
3693 * [MS-PRES] Table 3: Conversion of legacyInterop elements and attributes to MSRTC elements and attributes.
3696 sipe_get_status_by_availability(int avail
,
3697 char** activity_desc
)
3700 const char *act
= NULL
;
3703 status
= SIPE_STATUS_ID_OFFLINE
;
3704 } else if (avail
< 4500) {
3705 status
= SIPE_STATUS_ID_AVAILABLE
;
3706 } else if (avail
< 6000) {
3707 //status = SIPE_STATUS_ID_IDLE;
3708 status
= SIPE_STATUS_ID_AVAILABLE
;
3709 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_INACTIVE
);
3710 } else if (avail
< 7500) {
3711 status
= SIPE_STATUS_ID_BUSY
;
3712 } else if (avail
< 9000) {
3713 //status = SIPE_STATUS_ID_BUSYIDLE;
3714 status
= SIPE_STATUS_ID_BUSY
;
3715 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_BUSYIDLE
);
3716 } else if (avail
< 12000) {
3717 status
= SIPE_STATUS_ID_DND
;
3718 } else if (avail
< 15000) {
3719 status
= SIPE_STATUS_ID_BRB
;
3720 } else if (avail
< 18000) {
3721 status
= SIPE_STATUS_ID_AWAY
;
3723 status
= SIPE_STATUS_ID_OFFLINE
;
3726 if (activity_desc
&& act
) {
3727 g_free(*activity_desc
);
3728 *activity_desc
= g_strdup(act
);
3735 * Returns 2007-style availability value
3737 * @param sipe_status_id (in)
3738 * @param activity_token (out) Must be g_free()'d after use if consumed.
3741 sipe_get_availability_by_status(const char* sipe_status_id
, char** activity_token
)
3744 sipe_activity activity
= SIPE_ACTIVITY_UNSET
;
3746 if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_AWAY
)) {
3747 availability
= 15500;
3748 if (!activity_token
|| !(*activity_token
)) {
3749 activity
= SIPE_ACTIVITY_AWAY
;
3751 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_BRB
)) {
3752 availability
= 12500;
3753 activity
= SIPE_ACTIVITY_BRB
;
3754 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_DND
)) {
3755 availability
= 9500;
3756 activity
= SIPE_ACTIVITY_DND
;
3757 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_BUSY
)) {
3758 availability
= 6500;
3759 if (!activity_token
|| !(*activity_token
)) {
3760 activity
= SIPE_ACTIVITY_BUSY
;
3762 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_AVAILABLE
)) {
3763 availability
= 3500;
3764 activity
= SIPE_ACTIVITY_ONLINE
;
3765 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_UNKNOWN
)) {
3768 // Offline or invisible
3769 availability
= 18500;
3770 activity
= SIPE_ACTIVITY_OFFLINE
;
3773 if (activity_token
) {
3774 *activity_token
= g_strdup(sipe_activity_map
[activity
].token
);
3776 return availability
;
3779 static void process_incoming_notify_rlmi(struct sipe_core_private
*sipe_private
,
3780 const gchar
*data
, unsigned len
)
3782 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
3784 sipe_xml
*xn_categories
;
3785 const sipe_xml
*xn_category
;
3786 const char *status
= NULL
;
3787 gboolean do_update_status
= FALSE
;
3788 gboolean has_note_cleaned
= FALSE
;
3789 gboolean has_free_busy_cleaned
= FALSE
;
3791 xn_categories
= sipe_xml_parse(data
, len
);
3792 uri
= sipe_xml_attribute(xn_categories
, "uri"); /* with 'sip:' prefix */
3794 for (xn_category
= sipe_xml_child(xn_categories
, "category");
3796 xn_category
= sipe_xml_twin(xn_category
) )
3798 const sipe_xml
*xn_node
;
3800 const char *attrVar
= sipe_xml_attribute(xn_category
, "name");
3801 time_t publish_time
= (tmp
= sipe_xml_attribute(xn_category
, "publishTime")) ?
3802 sipe_utils_str_to_time(tmp
) : 0;
3805 if (sipe_strequal(attrVar
, "contactCard"))
3807 const sipe_xml
*card
= sipe_xml_child(xn_category
, "contactCard");
3810 const sipe_xml
*node
;
3811 /* identity - Display Name and email */
3812 node
= sipe_xml_child(card
, "identity");
3814 char* display_name
= sipe_xml_data(
3815 sipe_xml_child(node
, "name/displayName"));
3816 char* email
= sipe_xml_data(
3817 sipe_xml_child(node
, "email"));
3819 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_DISPLAY_NAME
, display_name
);
3820 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_EMAIL
, email
);
3822 g_free(display_name
);
3826 node
= sipe_xml_child(card
, "company");
3828 char* company
= sipe_xml_data(node
);
3829 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_COMPANY
, company
);
3833 node
= sipe_xml_child(card
, "department");
3835 char* department
= sipe_xml_data(node
);
3836 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_DEPARTMENT
, department
);
3840 node
= sipe_xml_child(card
, "title");
3842 char* title
= sipe_xml_data(node
);
3843 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_JOB_TITLE
, title
);
3847 node
= sipe_xml_child(card
, "office");
3849 char* office
= sipe_xml_data(node
);
3850 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_OFFICE
, office
);
3854 node
= sipe_xml_child(card
, "url");
3856 char* site
= sipe_xml_data(node
);
3857 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_SITE
, site
);
3861 for (node
= sipe_xml_child(card
, "phone");
3863 node
= sipe_xml_twin(node
))
3865 const char *phone_type
= sipe_xml_attribute(node
, "type");
3866 char* phone
= sipe_xml_data(sipe_xml_child(node
, "uri"));
3867 char* phone_display_string
= sipe_xml_data(sipe_xml_child(node
, "displayString"));
3869 sipe_update_user_phone(sipe_private
, uri
, phone_type
, phone
, phone_display_string
);
3872 g_free(phone_display_string
);
3875 for (node
= sipe_xml_child(card
, "address");
3877 node
= sipe_xml_twin(node
))
3879 if (sipe_strequal(sipe_xml_attribute(node
, "type"), "work")) {
3880 char* street
= sipe_xml_data(sipe_xml_child(node
, "street"));
3881 char* city
= sipe_xml_data(sipe_xml_child(node
, "city"));
3882 char* state
= sipe_xml_data(sipe_xml_child(node
, "state"));
3883 char* zipcode
= sipe_xml_data(sipe_xml_child(node
, "zipcode"));
3884 char* country_code
= sipe_xml_data(sipe_xml_child(node
, "countryCode"));
3886 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_STREET
, street
);
3887 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_CITY
, city
);
3888 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_STATE
, state
);
3889 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_ZIPCODE
, zipcode
);
3890 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_COUNTRY
, country_code
);
3896 g_free(country_code
);
3904 else if (sipe_strequal(attrVar
, "note"))
3907 struct sipe_buddy
*sbuddy
= g_hash_table_lookup(sipe_private
->buddies
, uri
);
3909 if (!has_note_cleaned
) {
3910 has_note_cleaned
= TRUE
;
3912 g_free(sbuddy
->note
);
3913 sbuddy
->note
= NULL
;
3914 sbuddy
->is_oof_note
= FALSE
;
3915 sbuddy
->note_since
= publish_time
;
3917 do_update_status
= TRUE
;
3919 if (sbuddy
&& (publish_time
>= sbuddy
->note_since
)) {
3920 /* clean up in case no 'note' element is supplied
3921 * which indicate note removal in client
3923 g_free(sbuddy
->note
);
3924 sbuddy
->note
= NULL
;
3925 sbuddy
->is_oof_note
= FALSE
;
3926 sbuddy
->note_since
= publish_time
;
3928 xn_node
= sipe_xml_child(xn_category
, "note/body");
3931 sbuddy
->note
= g_markup_escape_text((tmp
= sipe_xml_data(xn_node
)), -1);
3933 sbuddy
->is_oof_note
= sipe_strequal(sipe_xml_attribute(xn_node
, "type"), "OOF");
3934 sbuddy
->note_since
= publish_time
;
3936 SIPE_DEBUG_INFO("process_incoming_notify_rlmi: uri(%s), note(%s)",
3937 uri
, sbuddy
->note
? sbuddy
->note
: "");
3939 /* to trigger UI refresh in case no status info is supplied in this update */
3940 do_update_status
= TRUE
;
3945 else if(sipe_strequal(attrVar
, "state"))
3949 const sipe_xml
*xn_availability
;
3950 const sipe_xml
*xn_activity
;
3951 const sipe_xml
*xn_meeting_subject
;
3952 const sipe_xml
*xn_meeting_location
;
3953 struct sipe_buddy
*sbuddy
= uri
? g_hash_table_lookup(sipe_private
->buddies
, uri
) : NULL
;
3955 xn_node
= sipe_xml_child(xn_category
, "state");
3956 if (!xn_node
) continue;
3957 xn_availability
= sipe_xml_child(xn_node
, "availability");
3958 if (!xn_availability
) continue;
3959 xn_activity
= sipe_xml_child(xn_node
, "activity");
3960 xn_meeting_subject
= sipe_xml_child(xn_node
, "meetingSubject");
3961 xn_meeting_location
= sipe_xml_child(xn_node
, "meetingLocation");
3963 tmp
= sipe_xml_data(xn_availability
);
3964 availability
= atoi(tmp
);
3967 /* activity, meeting_subject, meeting_location */
3972 g_free(sbuddy
->activity
);
3973 sbuddy
->activity
= NULL
;
3975 const char *token
= sipe_xml_attribute(xn_activity
, "token");
3976 const sipe_xml
*xn_custom
= sipe_xml_child(xn_activity
, "custom");
3979 if (!is_empty(token
)) {
3980 sbuddy
->activity
= g_strdup(sipe_get_activity_desc_by_token(token
));
3982 /* from custom element */
3984 char *custom
= sipe_xml_data(xn_custom
);
3986 if (!is_empty(custom
)) {
3987 sbuddy
->activity
= custom
;
3993 /* meeting_subject */
3994 g_free(sbuddy
->meeting_subject
);
3995 sbuddy
->meeting_subject
= NULL
;
3996 if (xn_meeting_subject
) {
3997 char *meeting_subject
= sipe_xml_data(xn_meeting_subject
);
3999 if (!is_empty(meeting_subject
)) {
4000 sbuddy
->meeting_subject
= meeting_subject
;
4001 meeting_subject
= NULL
;
4003 g_free(meeting_subject
);
4005 /* meeting_location */
4006 g_free(sbuddy
->meeting_location
);
4007 sbuddy
->meeting_location
= NULL
;
4008 if (xn_meeting_location
) {
4009 char *meeting_location
= sipe_xml_data(xn_meeting_location
);
4011 if (!is_empty(meeting_location
)) {
4012 sbuddy
->meeting_location
= meeting_location
;
4013 meeting_location
= NULL
;
4015 g_free(meeting_location
);
4018 status
= sipe_get_status_by_availability(availability
, &tmp
);
4019 if (sbuddy
->activity
&& tmp
) {
4020 char *tmp2
= sbuddy
->activity
;
4022 sbuddy
->activity
= g_strdup_printf("%s, %s", sbuddy
->activity
, tmp
);
4026 sbuddy
->activity
= tmp
;
4030 do_update_status
= TRUE
;
4033 else if(sipe_strequal(attrVar
, "calendarData"))
4035 struct sipe_buddy
*sbuddy
= uri
? g_hash_table_lookup(sipe_private
->buddies
, uri
) : NULL
;
4036 const sipe_xml
*xn_free_busy
= sipe_xml_child(xn_category
, "calendarData/freeBusy");
4037 const sipe_xml
*xn_working_hours
= sipe_xml_child(xn_category
, "calendarData/WorkingHours");
4039 if (sbuddy
&& xn_free_busy
) {
4040 if (!has_free_busy_cleaned
) {
4041 has_free_busy_cleaned
= TRUE
;
4043 g_free(sbuddy
->cal_start_time
);
4044 sbuddy
->cal_start_time
= NULL
;
4046 g_free(sbuddy
->cal_free_busy_base64
);
4047 sbuddy
->cal_free_busy_base64
= NULL
;
4049 g_free(sbuddy
->cal_free_busy
);
4050 sbuddy
->cal_free_busy
= NULL
;
4052 sbuddy
->cal_free_busy_published
= publish_time
;
4055 if (publish_time
>= sbuddy
->cal_free_busy_published
) {
4056 g_free(sbuddy
->cal_start_time
);
4057 sbuddy
->cal_start_time
= g_strdup(sipe_xml_attribute(xn_free_busy
, "startTime"));
4059 sbuddy
->cal_granularity
= sipe_strcase_equal(sipe_xml_attribute(xn_free_busy
, "granularity"), "PT15M") ?
4062 g_free(sbuddy
->cal_free_busy_base64
);
4063 sbuddy
->cal_free_busy_base64
= sipe_xml_data(xn_free_busy
);
4065 g_free(sbuddy
->cal_free_busy
);
4066 sbuddy
->cal_free_busy
= NULL
;
4068 sbuddy
->cal_free_busy_published
= publish_time
;
4070 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
);
4074 if (sbuddy
&& xn_working_hours
) {
4075 sipe_cal_parse_working_hours(xn_working_hours
, sbuddy
);
4080 if (do_update_status
) {
4081 if (!status
) { /* no status category in this update, using contact's current status */
4082 PurpleBuddy
*pbuddy
= purple_find_buddy((PurpleAccount
*)sip
->account
, uri
);
4083 const PurplePresence
*presence
= purple_buddy_get_presence(pbuddy
);
4084 const PurpleStatus
*pstatus
= purple_presence_get_active_status(presence
);
4085 status
= purple_status_get_id(pstatus
);
4088 SIPE_DEBUG_INFO("process_incoming_notify_rlmi: %s", status
);
4089 sipe_core_buddy_got_status(SIPE_CORE_PUBLIC
, uri
, status
);
4092 sipe_xml_free(xn_categories
);
4095 static void sipe_subscribe_poolfqdn_resource_uri(const char *host
,
4097 struct sipe_core_private
*sipe_private
)
4099 struct presence_batched_routed
*payload
= g_malloc(sizeof(struct presence_batched_routed
));
4100 SIPE_DEBUG_INFO("process_incoming_notify_rlmi_resub: pool(%s)", host
);
4101 payload
->host
= g_strdup(host
);
4102 payload
->buddies
= server
;
4103 sipe_subscribe_presence_batched_routed(sipe_private
,
4105 sipe_subscribe_presence_batched_routed_free(payload
);
4108 static void process_incoming_notify_rlmi_resub(struct sipe_core_private
*sipe_private
,
4109 const gchar
*data
, unsigned len
)
4112 const sipe_xml
*xn_resource
;
4113 GHashTable
*servers
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
4118 xn_list
= sipe_xml_parse(data
, len
);
4120 for (xn_resource
= sipe_xml_child(xn_list
, "resource");
4122 xn_resource
= sipe_xml_twin(xn_resource
) )
4124 const char *uri
, *state
;
4125 const sipe_xml
*xn_instance
;
4127 xn_instance
= sipe_xml_child(xn_resource
, "instance");
4128 if (!xn_instance
) continue;
4130 uri
= sipe_xml_attribute(xn_resource
, "uri");
4131 state
= sipe_xml_attribute(xn_instance
, "state");
4132 SIPE_DEBUG_INFO("process_incoming_notify_rlmi_resub: uri(%s),state(%s)", uri
, state
);
4134 if (strstr(state
, "resubscribe")) {
4135 const char *poolFqdn
= sipe_xml_attribute(xn_instance
, "poolFqdn");
4137 if (poolFqdn
) { //[MS-PRES] Section 3.4.5.1.3 Processing Details
4138 gchar
*user
= g_strdup(uri
);
4139 host
= g_strdup(poolFqdn
);
4140 server
= g_hash_table_lookup(servers
, host
);
4141 server
= g_slist_append(server
, user
);
4142 g_hash_table_insert(servers
, host
, server
);
4144 sipe_subscribe_presence_single(sipe_private
,
4150 /* Send out any deferred poolFqdn subscriptions */
4151 g_hash_table_foreach(servers
, (GHFunc
) sipe_subscribe_poolfqdn_resource_uri
, sipe_private
);
4152 g_hash_table_destroy(servers
);
4154 sipe_xml_free(xn_list
);
4157 static void process_incoming_notify_pidf(struct sipe_core_private
*sipe_private
,
4158 const gchar
*data
, unsigned len
)
4162 gchar
*activity
= NULL
;
4164 const sipe_xml
*basicstatus
= NULL
, *tuple
, *status
;
4165 gboolean isonline
= FALSE
;
4166 const sipe_xml
*display_name_node
;
4168 pidf
= sipe_xml_parse(data
, len
);
4170 SIPE_DEBUG_INFO("process_incoming_notify_pidf: no parseable pidf:%s", data
);
4174 if ((tuple
= sipe_xml_child(pidf
, "tuple")))
4176 if ((status
= sipe_xml_child(tuple
, "status"))) {
4177 basicstatus
= sipe_xml_child(status
, "basic");
4182 SIPE_DEBUG_INFO_NOFORMAT("process_incoming_notify_pidf: no basic found");
4183 sipe_xml_free(pidf
);
4187 getbasic
= sipe_xml_data(basicstatus
);
4189 SIPE_DEBUG_INFO_NOFORMAT("process_incoming_notify_pidf: no basic data found");
4190 sipe_xml_free(pidf
);
4194 SIPE_DEBUG_INFO("process_incoming_notify_pidf: basic-status(%s)", getbasic
);
4195 if (strstr(getbasic
, "open")) {
4200 uri
= sip_uri(sipe_xml_attribute(pidf
, "entity")); /* with 'sip:' prefix */ /* AOL comes without the prefix */
4202 display_name_node
= sipe_xml_child(pidf
, "display-name");
4203 if (display_name_node
) {
4204 char * display_name
= sipe_xml_data(display_name_node
);
4206 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_DISPLAY_NAME
, display_name
);
4207 g_free(display_name
);
4210 if ((tuple
= sipe_xml_child(pidf
, "tuple"))) {
4211 if ((status
= sipe_xml_child(tuple
, "status"))) {
4212 if ((basicstatus
= sipe_xml_child(status
, "activities"))) {
4213 if ((basicstatus
= sipe_xml_child(basicstatus
, "activity"))) {
4214 activity
= sipe_xml_data(basicstatus
);
4215 SIPE_DEBUG_INFO("process_incoming_notify_pidf: activity(%s)", activity
);
4222 const gchar
* status_id
= NULL
;
4224 if (sipe_strequal(activity
, sipe_activity_map
[SIPE_ACTIVITY_BUSY
].token
)) {
4225 status_id
= SIPE_STATUS_ID_BUSY
;
4226 } else if (sipe_strequal(activity
, sipe_activity_map
[SIPE_ACTIVITY_AWAY
].token
)) {
4227 status_id
= SIPE_STATUS_ID_AWAY
;
4232 status_id
= SIPE_STATUS_ID_AVAILABLE
;
4235 SIPE_DEBUG_INFO("process_incoming_notify_pidf: status_id(%s)", status_id
);
4236 sipe_core_buddy_got_status(SIPE_CORE_PUBLIC
, uri
, status_id
);
4238 sipe_core_buddy_got_status(SIPE_CORE_PUBLIC
, uri
, SIPE_STATUS_ID_OFFLINE
);
4243 sipe_xml_free(pidf
);
4248 sipe_user_info_has_updated(struct sipe_core_private
*sipe_private
,
4249 const sipe_xml
*xn_userinfo
)
4251 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
4252 const sipe_xml
*xn_states
;
4254 g_free(sip
->user_states
);
4255 sip
->user_states
= NULL
;
4256 if ((xn_states
= sipe_xml_child(xn_userinfo
, "states")) != NULL
) {
4257 gchar
*orig
= sip
->user_states
= sipe_xml_stringify(xn_states
);
4259 /* this is a hack-around to remove added newline after inner element,
4260 * state in this case, where it shouldn't be.
4261 * After several use of sipe_xml_stringify, amount of added newlines
4262 * grows significantly.
4265 gchar c
, *stripped
= orig
;
4266 while ((c
= *orig
++)) {
4267 if ((c
!= '\n') /* && (c != '\r') */) {
4275 /* Publish initial state if not yet.
4276 * Assuming this happens on initial responce to self subscription
4277 * so we've already updated our UserInfo.
4279 if (!sip
->initial_state_published
) {
4280 send_presence_soap(sipe_private
, FALSE
);
4282 sipe_schedule_seconds(sipe_private
,
4283 "<+update-calendar>",
4285 UPDATE_CALENDAR_DELAY
,
4286 (sipe_schedule_action
) sipe_core_update_calendar
,
4291 static void process_incoming_notify_msrtc(struct sipe_core_private
*sipe_private
,
4292 const gchar
*data
, unsigned len
)
4294 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
4295 char *activity
= NULL
;
4297 const char *status_id
= NULL
;
4300 char *self_uri
= sip_uri_self(sipe_private
);
4303 const char *device_name
= NULL
;
4304 const char *cal_start_time
= NULL
;
4305 const char *cal_granularity
= NULL
;
4306 char *cal_free_busy_base64
= NULL
;
4307 struct sipe_buddy
*sbuddy
;
4308 const sipe_xml
*node
;
4309 sipe_xml
*xn_presentity
;
4310 const sipe_xml
*xn_availability
;
4311 const sipe_xml
*xn_activity
;
4312 const sipe_xml
*xn_display_name
;
4313 const sipe_xml
*xn_email
;
4314 const sipe_xml
*xn_phone_number
;
4315 const sipe_xml
*xn_userinfo
;
4316 const sipe_xml
*xn_note
;
4317 const sipe_xml
*xn_oof
;
4318 const sipe_xml
*xn_state
;
4319 const sipe_xml
*xn_contact
;
4321 char *free_activity
;
4323 const char *user_avail_nil
;
4325 time_t user_avail_since
= 0;
4326 time_t activity_since
= 0;
4328 /* fix for Reuters environment on Linux */
4329 if (data
&& strstr(data
, "encoding=\"utf-16\"")) {
4331 tmp_data
= replace(data
, "encoding=\"utf-16\"", "encoding=\"utf-8\"");
4332 xn_presentity
= sipe_xml_parse(tmp_data
, strlen(tmp_data
));
4335 xn_presentity
= sipe_xml_parse(data
, len
);
4338 xn_availability
= sipe_xml_child(xn_presentity
, "availability");
4339 xn_activity
= sipe_xml_child(xn_presentity
, "activity");
4340 xn_display_name
= sipe_xml_child(xn_presentity
, "displayName");
4341 xn_email
= sipe_xml_child(xn_presentity
, "email");
4342 xn_phone_number
= sipe_xml_child(xn_presentity
, "phoneNumber");
4343 xn_userinfo
= sipe_xml_child(xn_presentity
, "userInfo");
4344 xn_oof
= xn_userinfo
? sipe_xml_child(xn_userinfo
, "oof") : NULL
;
4345 xn_state
= xn_userinfo
? sipe_xml_child(xn_userinfo
, "states/state"): NULL
;
4346 user_avail
= xn_state
? sipe_xml_int_attribute(xn_state
, "avail", 0) : 0;
4347 user_avail_since
= xn_state
? sipe_utils_str_to_time(sipe_xml_attribute(xn_state
, "since")) : 0;
4348 user_avail_nil
= xn_state
? sipe_xml_attribute(xn_state
, "nil") : NULL
;
4349 xn_contact
= xn_userinfo
? sipe_xml_child(xn_userinfo
, "contact") : NULL
;
4350 xn_note
= xn_userinfo
? sipe_xml_child(xn_userinfo
, "note") : NULL
;
4351 note
= xn_note
? sipe_xml_data(xn_note
) : NULL
;
4353 if (sipe_strequal(user_avail_nil
, "true")) { /* null-ed */
4355 user_avail_since
= 0;
4358 free_activity
= NULL
;
4360 name
= sipe_xml_attribute(xn_presentity
, "uri"); /* without 'sip:' prefix */
4361 uri
= sip_uri_from_name(name
);
4362 avl
= sipe_xml_int_attribute(xn_availability
, "aggregate", 0);
4363 epid
= sipe_xml_attribute(xn_availability
, "epid");
4364 act
= sipe_xml_int_attribute(xn_activity
, "aggregate", 0);
4366 status_id
= sipe_get_status_by_act_avail_2005(act
, avl
, &activity
);
4367 res_avail
= sipe_get_availability_by_status(status_id
, NULL
);
4368 if (user_avail
> res_avail
) {
4369 res_avail
= user_avail
;
4370 status_id
= sipe_get_status_by_availability(user_avail
, NULL
);
4373 if (xn_display_name
) {
4374 char *display_name
= g_strdup(sipe_xml_attribute(xn_display_name
, "displayName"));
4375 char *email
= xn_email
? g_strdup(sipe_xml_attribute(xn_email
, "email")) : NULL
;
4376 char *phone_label
= xn_phone_number
? g_strdup(sipe_xml_attribute(xn_phone_number
, "label")) : NULL
;
4377 char *phone_number
= xn_phone_number
? g_strdup(sipe_xml_attribute(xn_phone_number
, "number")) : NULL
;
4378 char *tel_uri
= sip_to_tel_uri(phone_number
);
4380 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_DISPLAY_NAME
, display_name
);
4381 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_EMAIL
, email
);
4382 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_WORK_PHONE
, tel_uri
);
4383 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY
, !is_empty(phone_label
) ? phone_label
: phone_number
);
4386 g_free(phone_label
);
4387 g_free(phone_number
);
4389 g_free(display_name
);
4394 for (node
= sipe_xml_child(xn_contact
, "tel"); node
; node
= sipe_xml_twin(node
))
4396 /* Ex.: <tel type="work">tel:+3222220000</tel> */
4397 const char *phone_type
= sipe_xml_attribute(node
, "type");
4398 char* phone
= sipe_xml_data(node
);
4400 sipe_update_user_phone(sipe_private
, uri
, phone_type
, phone
, NULL
);
4406 /* devicePresence */
4407 for (node
= sipe_xml_child(xn_presentity
, "devices/devicePresence"); node
; node
= sipe_xml_twin(node
)) {
4408 const sipe_xml
*xn_device_name
;
4409 const sipe_xml
*xn_calendar_info
;
4410 const sipe_xml
*xn_state
;
4414 if (sipe_strequal(sipe_xml_attribute(node
, "epid"), epid
)) {
4415 xn_device_name
= sipe_xml_child(node
, "deviceName");
4416 device_name
= xn_device_name
? sipe_xml_attribute(xn_device_name
, "name") : NULL
;
4420 xn_calendar_info
= sipe_xml_child(node
, "calendarInfo");
4421 if (xn_calendar_info
) {
4422 const char *cal_start_time_tmp
= sipe_xml_attribute(xn_calendar_info
, "startTime");
4424 if (cal_start_time
) {
4425 time_t cal_start_time_t
= sipe_utils_str_to_time(cal_start_time
);
4426 time_t cal_start_time_t_tmp
= sipe_utils_str_to_time(cal_start_time_tmp
);
4428 if (cal_start_time_t_tmp
> cal_start_time_t
) {
4429 cal_start_time
= cal_start_time_tmp
;
4430 cal_granularity
= sipe_xml_attribute(xn_calendar_info
, "granularity");
4431 g_free(cal_free_busy_base64
);
4432 cal_free_busy_base64
= sipe_xml_data(xn_calendar_info
);
4434 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
);
4437 cal_start_time
= cal_start_time_tmp
;
4438 cal_granularity
= sipe_xml_attribute(xn_calendar_info
, "granularity");
4439 g_free(cal_free_busy_base64
);
4440 cal_free_busy_base64
= sipe_xml_data(xn_calendar_info
);
4442 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
);
4447 xn_state
= sipe_xml_child(node
, "states/state");
4449 int dev_avail
= sipe_xml_int_attribute(xn_state
, "avail", 0);
4450 time_t dev_avail_since
= sipe_utils_str_to_time(sipe_xml_attribute(xn_state
, "since"));
4452 state
= sipe_xml_data(xn_state
);
4453 if (dev_avail_since
> user_avail_since
&&
4454 dev_avail
>= res_avail
)
4456 res_avail
= dev_avail
;
4457 if (!is_empty(state
))
4459 if (sipe_strequal(state
, sipe_activity_map
[SIPE_ACTIVITY_ON_PHONE
].token
)) {
4461 activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_ON_PHONE
));
4462 } else if (sipe_strequal(state
, "presenting")) {
4464 activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_IN_CONF
));
4469 activity_since
= dev_avail_since
;
4471 status_id
= sipe_get_status_by_availability(res_avail
, &activity
);
4478 if (xn_oof
&& res_avail
>= 15000) { /* 12000 in 2007 */
4480 activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_OOF
));
4484 sbuddy
= g_hash_table_lookup(sipe_private
->buddies
, uri
);
4487 g_free(sbuddy
->activity
);
4488 sbuddy
->activity
= activity
;
4491 sbuddy
->activity_since
= activity_since
;
4493 sbuddy
->user_avail
= user_avail
;
4494 sbuddy
->user_avail_since
= user_avail_since
;
4496 g_free(sbuddy
->note
);
4497 sbuddy
->note
= NULL
;
4498 if (!is_empty(note
)) { sbuddy
->note
= g_markup_escape_text(note
, -1); }
4500 sbuddy
->is_oof_note
= (xn_oof
!= NULL
);
4502 g_free(sbuddy
->device_name
);
4503 sbuddy
->device_name
= NULL
;
4504 if (!is_empty(device_name
)) { sbuddy
->device_name
= g_strdup(device_name
); }
4506 if (!is_empty(cal_free_busy_base64
)) {
4507 g_free(sbuddy
->cal_start_time
);
4508 sbuddy
->cal_start_time
= g_strdup(cal_start_time
);
4510 sbuddy
->cal_granularity
= sipe_strcase_equal(cal_granularity
, "PT15M") ? 15 : 0;
4512 g_free(sbuddy
->cal_free_busy_base64
);
4513 sbuddy
->cal_free_busy_base64
= cal_free_busy_base64
;
4514 cal_free_busy_base64
= NULL
;
4516 g_free(sbuddy
->cal_free_busy
);
4517 sbuddy
->cal_free_busy
= NULL
;
4520 sbuddy
->last_non_cal_status_id
= status_id
;
4521 g_free(sbuddy
->last_non_cal_activity
);
4522 sbuddy
->last_non_cal_activity
= g_strdup(sbuddy
->activity
);
4524 if (sipe_strcase_equal(sbuddy
->name
, self_uri
)) {
4525 if (!sipe_strequal(sbuddy
->note
, sip
->note
)) /* not same */
4527 sip
->is_oof_note
= sbuddy
->is_oof_note
;
4530 sip
->note
= g_strdup(sbuddy
->note
);
4532 sip
->note_since
= time(NULL
);
4535 g_free(sip
->status
);
4536 sip
->status
= g_strdup(sbuddy
->last_non_cal_status_id
);
4539 g_free(cal_free_busy_base64
);
4542 SIPE_DEBUG_INFO("process_incoming_notify_msrtc: status(%s)", status_id
);
4543 sipe_core_buddy_got_status(SIPE_CORE_PUBLIC
, uri
, status_id
);
4545 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
) && sipe_strcase_equal(self_uri
, uri
)) {
4546 sipe_user_info_has_updated(sipe_private
, xn_userinfo
);
4550 sipe_xml_free(xn_presentity
);
4555 static void sipe_presence_mime_cb(gpointer user_data
, /* sipe_core_private */
4556 const GSList
*fields
,
4560 const gchar
*type
= sipe_utils_nameval_find(fields
, "Content-Type");
4562 if (strstr(type
,"application/rlmi+xml")) {
4563 process_incoming_notify_rlmi_resub(user_data
, body
, length
);
4564 } else if (strstr(type
, "text/xml+msrtc.pidf")) {
4565 process_incoming_notify_msrtc(user_data
, body
, length
);
4567 process_incoming_notify_rlmi(user_data
, body
, length
);
4571 static void sipe_process_presence(struct sipe_core_private
*sipe_private
,
4574 const char *ctype
= sipmsg_find_header(msg
, "Content-Type");
4576 SIPE_DEBUG_INFO("sipe_process_presence: Content-Type: %s", ctype
? ctype
: "");
4579 (strstr(ctype
, "application/rlmi+xml") ||
4580 strstr(ctype
, "application/msrtc-event-categories+xml")))
4582 if (strstr(ctype
, "multipart"))
4584 sipe_mime_parts_foreach(ctype
, msg
->body
, sipe_presence_mime_cb
, sipe_private
);
4586 else if(strstr(ctype
, "application/msrtc-event-categories+xml") )
4588 process_incoming_notify_rlmi(sipe_private
, msg
->body
, msg
->bodylen
);
4590 else if(strstr(ctype
, "application/rlmi+xml"))
4592 process_incoming_notify_rlmi_resub(sipe_private
, msg
->body
, msg
->bodylen
);
4595 else if(ctype
&& strstr(ctype
, "text/xml+msrtc.pidf"))
4597 process_incoming_notify_msrtc(sipe_private
, msg
->body
, msg
->bodylen
);
4601 process_incoming_notify_pidf(sipe_private
, msg
->body
, msg
->bodylen
);
4605 static void sipe_presence_timeout_mime_cb(gpointer user_data
,
4606 SIPE_UNUSED_PARAMETER
const GSList
*fields
,
4610 GSList
**buddies
= user_data
;
4611 sipe_xml
*xml
= sipe_xml_parse(body
, length
);
4613 if (xml
&& !sipe_strequal(sipe_xml_name(xml
), "list")) {
4614 const gchar
*uri
= sipe_xml_attribute(xml
, "uri");
4615 const sipe_xml
*xn_category
;
4618 * automaton: presence is never expected to change
4620 * see: http://msdn.microsoft.com/en-us/library/ee354295(office.13).aspx
4622 for (xn_category
= sipe_xml_child(xml
, "category");
4624 xn_category
= sipe_xml_twin(xn_category
)) {
4625 if (sipe_strequal(sipe_xml_attribute(xn_category
, "name"),
4627 const sipe_xml
*node
= sipe_xml_child(xn_category
, "contactCard/automaton");
4629 char *boolean
= sipe_xml_data(node
);
4630 if (sipe_strequal(boolean
, "true")) {
4631 SIPE_DEBUG_INFO("sipe_process_presence_timeout: %s is an automaton: - not subscribing to presence updates",
4642 *buddies
= g_slist_append(*buddies
, sip_uri(uri
));
4649 static void sipe_process_presence_timeout(struct sipe_core_private
*sipe_private
,
4650 struct sipmsg
*msg
, gchar
*who
,
4653 const char *ctype
= sipmsg_find_header(msg
, "Content-Type");
4654 gchar
*action_name
= sipe_utils_presence_key(who
);
4656 SIPE_DEBUG_INFO("sipe_process_presence_timeout: Content-Type: %s", ctype
? ctype
: "");
4659 strstr(ctype
, "multipart") &&
4660 (strstr(ctype
, "application/rlmi+xml") ||
4661 strstr(ctype
, "application/msrtc-event-categories+xml"))) {
4662 GSList
*buddies
= NULL
;
4664 sipe_mime_parts_foreach(ctype
, msg
->body
, sipe_presence_timeout_mime_cb
, &buddies
);
4667 struct presence_batched_routed
*payload
= g_malloc(sizeof(struct presence_batched_routed
));
4668 payload
->host
= g_strdup(who
);
4669 payload
->buddies
= buddies
;
4670 sipe_schedule_seconds(sipe_private
,
4674 sipe_subscribe_presence_batched_routed
,
4675 sipe_subscribe_presence_batched_routed_free
);
4676 SIPE_DEBUG_INFO("Resubscription multiple contacts with batched support & route(%s) in %d", who
, timeout
);
4680 sipe_schedule_seconds(sipe_private
,
4684 sipe_subscribe_presence_single
,
4686 SIPE_DEBUG_INFO("Resubscription single contact with batched support(%s) in %d", who
, timeout
);
4688 g_free(action_name
);
4692 * Dispatcher for all incoming subscription information
4693 * whether it comes from NOTIFY, BENOTIFY requests or
4694 * piggy-backed to subscription's OK responce.
4696 * @param request whether initiated from BE/NOTIFY request or OK-response message.
4697 * @param benotify whether initiated from NOTIFY or BENOTIFY request.
4699 void process_incoming_notify(struct sipe_core_private
*sipe_private
,
4701 gboolean request
, gboolean benotify
)
4703 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
4704 const gchar
*content_type
= sipmsg_find_header(msg
, "Content-Type");
4705 const gchar
*event
= sipmsg_find_header(msg
, "Event");
4706 const gchar
*subscription_state
= sipmsg_find_header(msg
, "subscription-state");
4708 SIPE_DEBUG_INFO("process_incoming_notify: subscription_state: %s", subscription_state
? subscription_state
: "");
4710 /* implicit subscriptions */
4711 if (content_type
&& g_str_has_prefix(content_type
, "application/ms-imdn+xml")) {
4712 sipe_process_imdn(sipe_private
, msg
);
4716 /* for one off subscriptions (send with Expire: 0) */
4717 if (sipe_strcase_equal(event
, "vnd-microsoft-provisioning-v2"))
4719 sipe_process_provisioning_v2(sipe_private
, msg
);
4721 else if (sipe_strcase_equal(event
, "vnd-microsoft-provisioning"))
4723 sipe_process_provisioning(sipe_private
, msg
);
4725 else if (sipe_strcase_equal(event
, "presence"))
4727 sipe_process_presence(sipe_private
, msg
);
4729 else if (sipe_strcase_equal(event
, "registration-notify"))
4731 sipe_process_registration_notify(sipe_private
, msg
);
4734 if (!subscription_state
|| strstr(subscription_state
, "active"))
4736 if (sipe_strcase_equal(event
, "vnd-microsoft-roaming-contacts"))
4738 sipe_process_roaming_contacts(sipe_private
, msg
);
4740 else if (sipe_strcase_equal(event
, "vnd-microsoft-roaming-self"))
4742 sipe_process_roaming_self(sipe_private
, msg
);
4744 else if (sipe_strcase_equal(event
, "vnd-microsoft-roaming-ACL"))
4746 sipe_process_roaming_acl(sipe_private
, msg
);
4748 else if (sipe_strcase_equal(event
, "presence.wpending"))
4750 sipe_process_presence_wpending(sipe_private
, msg
);
4752 else if (sipe_strcase_equal(event
, "conference"))
4754 sipe_process_conference(sipe_private
, msg
);
4759 /* The server sends status 'terminated' */
4760 if (subscription_state
&& strstr(subscription_state
, "terminated") ) {
4761 gchar
*who
= parse_from(sipmsg_find_header(msg
, request
? "From" : "To"));
4762 gchar
*key
= sipe_utils_subscription_key(event
, who
);
4764 SIPE_DEBUG_INFO("process_incoming_notify: server says that subscription to %s was terminated.", who
);
4767 sipe_subscriptions_remove(sipe_private
, key
);
4771 if (!request
&& event
) {
4772 const gchar
*expires_header
= sipmsg_find_header(msg
, "Expires");
4773 int timeout
= expires_header
? strtol(expires_header
, NULL
, 10) : 0;
4774 SIPE_DEBUG_INFO("process_incoming_notify: subscription expires:%d", timeout
);
4777 /* 2 min ahead of expiration */
4778 timeout
= (timeout
- 120) > 120 ? (timeout
- 120) : timeout
;
4780 if (sipe_strcase_equal(event
, "presence.wpending") &&
4781 g_slist_find_custom(sip
->allow_events
, "presence.wpending", (GCompareFunc
)g_ascii_strcasecmp
))
4783 gchar
*action_name
= g_strdup_printf("<%s>", "presence.wpending");
4784 sipe_schedule_seconds(sipe_private
,
4788 sipe_subscribe_presence_wpending
,
4790 g_free(action_name
);
4792 else if (sipe_strcase_equal(event
, "presence") &&
4793 g_slist_find_custom(sip
->allow_events
, "presence", (GCompareFunc
)g_ascii_strcasecmp
))
4795 gchar
*who
= parse_from(sipmsg_find_header(msg
, "To"));
4796 gchar
*action_name
= sipe_utils_presence_key(who
);
4798 if (sip
->batched_support
) {
4799 sipe_process_presence_timeout(sipe_private
, msg
, who
, timeout
);
4802 sipe_schedule_seconds(sipe_private
,
4806 sipe_subscribe_presence_single
,
4808 SIPE_DEBUG_INFO("Resubscription single contact (%s) in %d", who
, timeout
);
4810 g_free(action_name
);
4816 /* The client responses on received a NOTIFY message */
4817 if (request
&& !benotify
)
4819 sip_transport_response(sipe_private
, msg
, 200, "OK", NULL
);
4824 * Whether user manually changed status or
4825 * it was changed automatically due to user
4826 * became inactive/active again
4829 sipe_is_user_state(struct sipe_core_private
*sipe_private
)
4831 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
4833 time_t now
= time(NULL
);
4835 SIPE_DEBUG_INFO("sipe_is_user_state: sip->idle_switch : %s", asctime(localtime(&(sip
->idle_switch
))));
4836 SIPE_DEBUG_INFO("sipe_is_user_state: now : %s", asctime(localtime(&now
)));
4838 res
= ((now
- SIPE_IDLE_SET_DELAY
* 2) >= sip
->idle_switch
);
4840 SIPE_DEBUG_INFO("sipe_is_user_state: res = %s", res
? "USER" : "MACHINE");
4845 send_presence_soap0(struct sipe_core_private
*sipe_private
,
4846 gboolean do_publish_calendar
,
4847 gboolean do_reset_status
)
4849 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
4850 struct sipe_calendar
* cal
= sip
->cal
;
4851 int availability
= 0;
4856 gchar
*res_note
= NULL
;
4857 gchar
*res_oof
= NULL
;
4858 const gchar
*note_pub
= NULL
;
4859 gchar
*states
= NULL
;
4860 gchar
*calendar_data
= NULL
;
4861 gchar
*epid
= get_epid(sipe_private
);
4862 time_t now
= time(NULL
);
4863 gchar
*since_time_str
= sipe_utils_time_to_str(now
);
4864 const gchar
*oof_note
= cal
? sipe_ews_get_oof_note(cal
) : NULL
;
4865 const char *user_input
;
4866 gboolean pub_oof
= cal
&& oof_note
&& (!sip
->note
|| cal
->updated
> sip
->note_since
);
4868 if (oof_note
&& sip
->note
) {
4869 SIPE_DEBUG_INFO("cal->oof_start : %s", asctime(localtime(&(cal
->oof_start
))));
4870 SIPE_DEBUG_INFO("sip->note_since : %s", asctime(localtime(&(sip
->note_since
))));
4873 SIPE_DEBUG_INFO("sip->note : %s", sip
->note
? sip
->note
: "");
4875 if (!sip
->initial_state_published
||
4878 g_free(sip
->status
);
4879 sip
->status
= g_strdup(SIPE_STATUS_ID_AVAILABLE
);
4882 sipe_get_act_avail_by_status_2005(sip
->status
, &activity
, &availability
);
4886 note_pub
= oof_note
;
4887 res_oof
= SIPE_SOAP_SET_PRESENCE_OOF_XML
;
4888 cal
->published
= TRUE
;
4889 } else if (sip
->note
) {
4890 if (sip
->is_oof_note
&& !oof_note
) { /* stale OOF note, as it's not present in cal already */
4893 sip
->is_oof_note
= FALSE
;
4894 sip
->note_since
= 0;
4896 note_pub
= sip
->note
;
4897 res_oof
= sip
->is_oof_note
? SIPE_SOAP_SET_PRESENCE_OOF_XML
: "";
4903 /* to protocol internal plain text format */
4904 tmp
= sipe_backend_markup_strip_html(note_pub
);
4905 res_note
= g_markup_printf_escaped(SIPE_SOAP_SET_PRESENCE_NOTE_XML
, tmp
);
4910 if (!do_reset_status
) {
4911 if (sipe_is_user_state(sipe_private
) && !do_publish_calendar
&& sip
->initial_state_published
)
4913 gchar
*activity_token
= NULL
;
4914 int avail_2007
= sipe_get_availability_by_status(sip
->status
, &activity_token
);
4916 states
= g_strdup_printf(SIPE_SOAP_SET_PRESENCE_STATES
,
4921 g_free(activity_token
);
4923 else /* preserve existing publication */
4925 if (sip
->user_states
) {
4926 states
= g_strdup(sip
->user_states
);
4930 /* do nothing - then User state will be erased */
4932 sip
->initial_state_published
= TRUE
;
4935 if (cal
&& (!is_empty(cal
->legacy_dn
) || !is_empty(cal
->email
)) && cal
->fb_start
&& !is_empty(cal
->free_busy
))
4937 char *fb_start_str
= sipe_utils_time_to_str(cal
->fb_start
);
4938 char *free_busy_base64
= sipe_cal_get_freebusy_base64(cal
->free_busy
);
4939 calendar_data
= g_strdup_printf(SIPE_SOAP_SET_PRESENCE_CALENDAR
,
4940 !is_empty(cal
->legacy_dn
) ? cal
->legacy_dn
: cal
->email
,
4943 g_free(fb_start_str
);
4944 g_free(free_busy_base64
);
4947 user_input
= (sipe_is_user_state(sipe_private
) ||
4948 sipe_strequal(sip
->status
, SIPE_STATUS_ID_AVAILABLE
)) ?
4951 /* forming resulting XML */
4952 body
= g_strdup_printf(SIPE_SOAP_SET_PRESENCE
,
4953 sipe_private
->username
,
4956 (tmp
= g_ascii_strup(g_get_host_name(), -1)),
4957 res_note
? res_note
: "",
4958 res_oof
? res_oof
: "",
4959 states
? states
: "",
4960 calendar_data
? calendar_data
: "",
4969 g_free(calendar_data
);
4971 send_soap_request(sipe_private
, body
);
4974 g_free(since_time_str
);
4979 send_presence_soap(struct sipe_core_private
*sipe_private
,
4980 gboolean do_publish_calendar
)
4982 return send_presence_soap0(sipe_private
, do_publish_calendar
, FALSE
);
4987 process_send_presence_category_publish_response(struct sipe_core_private
*sipe_private
,
4989 struct transaction
*trans
)
4991 const gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
4993 if (msg
->response
== 409 && g_str_has_prefix(contenttype
, "application/msrtc-fault+xml")) {
4995 const sipe_xml
*node
;
4999 gboolean has_device_publication
= FALSE
;
5001 xml
= sipe_xml_parse(msg
->body
, msg
->bodylen
);
5003 /* test if version mismatch fault */
5004 fault_code
= sipe_xml_data(sipe_xml_child(xml
, "Faultcode"));
5005 if (!sipe_strequal(fault_code
, "Client.BadCall.WrongDelta")) {
5006 SIPE_DEBUG_INFO("process_send_presence_category_publish_response: unsupported fault code:%s returning.", fault_code
);
5013 /* accumulating information about faulty versions */
5014 faults
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, g_free
);
5015 for (node
= sipe_xml_child(xml
, "details/operation");
5017 node
= sipe_xml_twin(node
))
5019 const gchar
*index
= sipe_xml_attribute(node
, "index");
5020 const gchar
*curVersion
= sipe_xml_attribute(node
, "curVersion");
5022 g_hash_table_insert(faults
, g_strdup(index
), g_strdup(curVersion
));
5023 SIPE_DEBUG_INFO("fault added: index:%s curVersion:%s", index
, curVersion
);
5027 /* here we are parsing our own request to figure out what publication
5028 * referenced here only by index went wrong
5030 xml
= sipe_xml_parse(trans
->msg
->body
, trans
->msg
->bodylen
);
5033 for (node
= sipe_xml_child(xml
, "publications/publication"),
5034 index_our
= 1; /* starts with 1 - our first publication */
5036 node
= sipe_xml_twin(node
), index_our
++)
5038 gchar
*idx
= g_strdup_printf("%d", index_our
);
5039 const gchar
*curVersion
= g_hash_table_lookup(faults
, idx
);
5040 const gchar
*categoryName
= sipe_xml_attribute(node
, "categoryName");
5043 if (sipe_strequal("device", categoryName
)) {
5044 has_device_publication
= TRUE
;
5047 if (curVersion
) { /* fault exist on this index */
5048 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
5049 const gchar
*container
= sipe_xml_attribute(node
, "container");
5050 const gchar
*instance
= sipe_xml_attribute(node
, "instance");
5051 /* key is <category><instance><container> */
5052 gchar
*key
= g_strdup_printf("<%s><%s><%s>", categoryName
, instance
, container
);
5053 GHashTable
*category
= g_hash_table_lookup(sip
->our_publications
, categoryName
);
5056 struct sipe_publication
*publication
=
5057 g_hash_table_lookup(category
, key
);
5059 SIPE_DEBUG_INFO("key is %s", key
);
5062 SIPE_DEBUG_INFO("Updating %s with version %s. Was %d before.",
5063 key
, curVersion
, publication
->version
);
5064 /* updating publication's version to the correct one */
5065 publication
->version
= atoi(curVersion
);
5068 /* We somehow lost this category from our publications... */
5069 struct sipe_publication
*publication
= g_new0(struct sipe_publication
, 1);
5070 publication
->category
= g_strdup(categoryName
);
5071 publication
->instance
= atoi(instance
);
5072 publication
->container
= atoi(container
);
5073 publication
->version
= atoi(curVersion
);
5074 category
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
5075 g_free
, (GDestroyNotify
)free_publication
);
5076 g_hash_table_insert(category
, g_strdup(key
), publication
);
5077 g_hash_table_insert(sip
->our_publications
, g_strdup(categoryName
), category
);
5078 SIPE_DEBUG_INFO("added lost category '%s' key '%s'", categoryName
, key
);
5084 g_hash_table_destroy(faults
);
5086 /* rebublishing with right versions */
5087 if (has_device_publication
) {
5088 send_publish_category_initial(sipe_private
);
5090 send_presence_status(sipe_private
, NULL
);
5097 * Returns 'device' XML part for publication.
5098 * Must be g_free'd after use.
5101 sipe_publish_get_category_device(struct sipe_core_private
*sipe_private
)
5103 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
5106 gchar
*epid
= get_epid(sipe_private
);
5107 gchar
*uuid
= generateUUIDfromEPID(epid
);
5108 guint device_instance
= sipe_get_pub_instance(sipe_private
, SIPE_PUB_DEVICE
);
5109 /* key is <category><instance><container> */
5110 gchar
*key
= g_strdup_printf("<%s><%u><%u>", "device", device_instance
, 2);
5111 struct sipe_publication
*publication
=
5112 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "device"), key
);
5117 uri
= sip_uri_self(sipe_private
);
5118 doc
= g_strdup_printf(SIPE_PUB_XML_DEVICE
,
5120 publication
? publication
->version
: 0,
5123 "00:00:00+01:00", /* @TODO make timezone real*/
5134 * A service method - use
5135 * - send_publish_get_category_state_machine and
5136 * - send_publish_get_category_state_user instead.
5137 * Must be g_free'd after use.
5140 sipe_publish_get_category_state(struct sipe_core_private
*sipe_private
,
5141 gboolean is_user_state
)
5143 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
5144 int availability
= sipe_get_availability_by_status(sip
->status
, NULL
);
5145 guint instance
= is_user_state
? sipe_get_pub_instance(sipe_private
, SIPE_PUB_STATE_USER
) :
5146 sipe_get_pub_instance(sipe_private
, SIPE_PUB_STATE_MACHINE
);
5147 /* key is <category><instance><container> */
5148 gchar
*key_2
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 2);
5149 gchar
*key_3
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 3);
5150 struct sipe_publication
*publication_2
=
5151 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "state"), key_2
);
5152 struct sipe_publication
*publication_3
=
5153 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "state"), key_3
);
5158 if (publication_2
&& (publication_2
->availability
== availability
))
5160 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_state: state has NOT changed. Exiting.");
5161 return NULL
; /* nothing to update */
5164 return g_strdup_printf( is_user_state
? SIPE_PUB_XML_STATE_USER
: SIPE_PUB_XML_STATE_MACHINE
,
5166 publication_2
? publication_2
->version
: 0,
5169 publication_3
? publication_3
->version
: 0,
5174 * Only Busy and OOF calendar event are published.
5175 * Different instances are used for that.
5177 * Must be g_free'd after use.
5180 sipe_publish_get_category_state_calendar(struct sipe_core_private
*sipe_private
,
5181 struct sipe_cal_event
*event
,
5185 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
5186 gchar
*start_time_str
;
5187 int availability
= 0;
5190 guint instance
= (cal_satus
== SIPE_CAL_OOF
) ?
5191 sipe_get_pub_instance(sipe_private
, SIPE_PUB_STATE_CALENDAR_OOF
) :
5192 sipe_get_pub_instance(sipe_private
, SIPE_PUB_STATE_CALENDAR
);
5194 /* key is <category><instance><container> */
5195 gchar
*key_2
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 2);
5196 gchar
*key_3
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 3);
5197 struct sipe_publication
*publication_2
=
5198 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "state"), key_2
);
5199 struct sipe_publication
*publication_3
=
5200 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "state"), key_3
);
5205 if (!publication_3
&& !event
) { /* was nothing, have nothing, exiting */
5206 SIPE_DEBUG_INFO("sipe_publish_get_category_state_calendar: "
5207 "Exiting as no publication and no event for cal_satus:%d", cal_satus
);
5213 (publication_3
->availability
== availability
) &&
5214 sipe_strequal(publication_3
->cal_event_hash
, (tmp
= sipe_cal_event_hash(event
))))
5217 SIPE_DEBUG_INFO("sipe_publish_get_category_state_calendar: "
5218 "cal state has NOT changed for cal_satus:%d. Exiting.", cal_satus
);
5219 return NULL
; /* nothing to update */
5224 (event
->cal_status
== SIPE_CAL_BUSY
||
5225 event
->cal_status
== SIPE_CAL_OOF
))
5227 gchar
*availability_xml_str
= NULL
;
5228 gchar
*activity_xml_str
= NULL
;
5229 gchar
*escaped_subject
= event
->subject
? g_markup_escape_text(event
->subject
, -1) : NULL
;
5230 gchar
*escaped_location
= event
->location
? g_markup_escape_text(event
->location
, -1) : NULL
;
5232 if (event
->cal_status
== SIPE_CAL_BUSY
) {
5233 availability_xml_str
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_AVAIL
, 6500);
5236 if (event
->cal_status
== SIPE_CAL_BUSY
&& event
->is_meeting
) {
5237 activity_xml_str
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY
,
5238 sipe_activity_map
[SIPE_ACTIVITY_IN_MEETING
].token
,
5239 "minAvailability=\"6500\"",
5240 "maxAvailability=\"8999\"");
5241 } else if (event
->cal_status
== SIPE_CAL_OOF
) {
5242 activity_xml_str
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY
,
5243 sipe_activity_map
[SIPE_ACTIVITY_OOF
].token
,
5244 "minAvailability=\"12000\"",
5247 start_time_str
= sipe_utils_time_to_str(event
->start_time
);
5249 res
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR
,
5251 publication_2
? publication_2
->version
: 0,
5254 availability_xml_str
? availability_xml_str
: "",
5255 activity_xml_str
? activity_xml_str
: "",
5256 escaped_subject
? escaped_subject
: "",
5257 escaped_location
? escaped_location
: "",
5260 publication_3
? publication_3
->version
: 0,
5263 availability_xml_str
? availability_xml_str
: "",
5264 activity_xml_str
? activity_xml_str
: "",
5265 escaped_subject
? escaped_subject
: "",
5266 escaped_location
? escaped_location
: ""
5268 g_free(escaped_location
);
5269 g_free(escaped_subject
);
5270 g_free(start_time_str
);
5271 g_free(availability_xml_str
);
5272 g_free(activity_xml_str
);
5275 else /* including !event, SIPE_CAL_FREE, SIPE_CAL_TENTATIVE */
5277 res
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_CLEAR
,
5279 publication_2
? publication_2
->version
: 0,
5282 publication_3
? publication_3
->version
: 0
5290 * Returns 'machineState' XML part for publication.
5291 * Must be g_free'd after use.
5294 sipe_publish_get_category_state_machine(struct sipe_core_private
*sipe_private
)
5296 return sipe_publish_get_category_state(sipe_private
, FALSE
);
5300 * Returns 'userState' XML part for publication.
5301 * Must be g_free'd after use.
5304 sipe_publish_get_category_state_user(struct sipe_core_private
*sipe_private
)
5306 return sipe_publish_get_category_state(sipe_private
, TRUE
);
5310 * Returns 'note' XML part for publication.
5311 * Must be g_free'd after use.
5313 * Protocol format for Note is plain text.
5315 * @param note a note in Sipe internal HTML format
5316 * @param note_type either personal or OOF
5319 sipe_publish_get_category_note(struct sipe_core_private
*sipe_private
,
5320 const char *note
, /* html */
5321 const char *note_type
,
5325 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
5326 guint instance
= sipe_strequal("OOF", note_type
) ? sipe_get_pub_instance(sipe_private
, SIPE_PUB_NOTE_OOF
) : 0;
5327 /* key is <category><instance><container> */
5328 gchar
*key_note_200
= g_strdup_printf("<%s><%u><%u>", "note", instance
, 200);
5329 gchar
*key_note_300
= g_strdup_printf("<%s><%u><%u>", "note", instance
, 300);
5330 gchar
*key_note_400
= g_strdup_printf("<%s><%u><%u>", "note", instance
, 400);
5332 struct sipe_publication
*publication_note_200
=
5333 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "note"), key_note_200
);
5334 struct sipe_publication
*publication_note_300
=
5335 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "note"), key_note_300
);
5336 struct sipe_publication
*publication_note_400
=
5337 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "note"), key_note_400
);
5339 char *tmp
= note
? sipe_backend_markup_strip_html(note
) : NULL
;
5340 char *n1
= tmp
? g_markup_escape_text(tmp
, -1) : NULL
;
5341 const char *n2
= publication_note_200
? publication_note_200
->note
: NULL
;
5342 char *res
, *tmp1
, *tmp2
, *tmp3
;
5343 char *start_time_attr
;
5344 char *end_time_attr
;
5348 g_free(key_note_200
);
5349 g_free(key_note_300
);
5350 g_free(key_note_400
);
5352 /* we even need to republish empty note */
5353 if (sipe_strequal(n1
, n2
))
5355 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_note: note has NOT changed. Exiting.");
5357 return NULL
; /* nothing to update */
5360 start_time_attr
= note_start
? g_strdup_printf(" startTime=\"%s\"", (tmp
= sipe_utils_time_to_str(note_start
))) : NULL
;
5363 end_time_attr
= note_end
? g_strdup_printf(" endTime=\"%s\"", (tmp
= sipe_utils_time_to_str(note_end
))) : NULL
;
5367 tmp1
= g_strdup_printf(SIPE_PUB_XML_NOTE
,
5370 publication_note_200
? publication_note_200
->version
: 0,
5372 start_time_attr
? start_time_attr
: "",
5373 end_time_attr
? end_time_attr
: "",
5376 tmp2
= g_strdup_printf(SIPE_PUB_XML_NOTE
,
5379 publication_note_300
? publication_note_300
->version
: 0,
5381 start_time_attr
? start_time_attr
: "",
5382 end_time_attr
? end_time_attr
: "",
5385 tmp3
= g_strdup_printf(SIPE_PUB_XML_NOTE
,
5388 publication_note_400
? publication_note_400
->version
: 0,
5390 start_time_attr
? start_time_attr
: "",
5391 end_time_attr
? end_time_attr
: "",
5394 tmp1
= g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR
,
5398 publication_note_200
? publication_note_200
->version
: 0,
5400 tmp2
= g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR
,
5404 publication_note_200
? publication_note_200
->version
: 0,
5406 tmp3
= g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR
,
5410 publication_note_200
? publication_note_200
->version
: 0,
5413 res
= g_strconcat(tmp1
, tmp2
, tmp3
, NULL
);
5415 g_free(start_time_attr
);
5416 g_free(end_time_attr
);
5426 * Returns 'calendarData' XML part with WorkingHours for publication.
5427 * Must be g_free'd after use.
5430 sipe_publish_get_category_cal_working_hours(struct sipe_core_private
*sipe_private
)
5432 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
5433 struct sipe_calendar
* cal
= sip
->cal
;
5435 /* key is <category><instance><container> */
5436 gchar
*key_cal_1
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 1);
5437 gchar
*key_cal_100
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 100);
5438 gchar
*key_cal_200
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 200);
5439 gchar
*key_cal_300
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 300);
5440 gchar
*key_cal_400
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 400);
5441 gchar
*key_cal_32000
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 32000);
5443 struct sipe_publication
*publication_cal_1
=
5444 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_1
);
5445 struct sipe_publication
*publication_cal_100
=
5446 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_100
);
5447 struct sipe_publication
*publication_cal_200
=
5448 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_200
);
5449 struct sipe_publication
*publication_cal_300
=
5450 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_300
);
5451 struct sipe_publication
*publication_cal_400
=
5452 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_400
);
5453 struct sipe_publication
*publication_cal_32000
=
5454 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_32000
);
5456 const char *n1
= cal
? cal
->working_hours_xml_str
: NULL
;
5457 const char *n2
= publication_cal_300
? publication_cal_300
->working_hours_xml_str
: NULL
;
5460 g_free(key_cal_100
);
5461 g_free(key_cal_200
);
5462 g_free(key_cal_300
);
5463 g_free(key_cal_400
);
5464 g_free(key_cal_32000
);
5466 if (!cal
|| is_empty(cal
->email
) || is_empty(cal
->working_hours_xml_str
)) {
5467 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_working_hours: no data to publish, exiting");
5471 if (sipe_strequal(n1
, n2
))
5473 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_working_hours: WorkingHours has NOT changed. Exiting.");
5474 return NULL
; /* nothing to update */
5477 return g_strdup_printf(SIPE_PUB_XML_WORKING_HOURS
,
5479 publication_cal_1
? publication_cal_1
->version
: 0,
5481 cal
->working_hours_xml_str
,
5483 publication_cal_100
? publication_cal_100
->version
: 0,
5485 publication_cal_200
? publication_cal_200
->version
: 0,
5487 cal
->working_hours_xml_str
,
5489 publication_cal_300
? publication_cal_300
->version
: 0,
5491 cal
->working_hours_xml_str
,
5492 /* 400 - Personal */
5493 publication_cal_400
? publication_cal_400
->version
: 0,
5495 cal
->working_hours_xml_str
,
5496 /* 32000 - Blocked */
5497 publication_cal_32000
? publication_cal_32000
->version
: 0
5502 * Returns 'calendarData' XML part with FreeBusy for publication.
5503 * Must be g_free'd after use.
5506 sipe_publish_get_category_cal_free_busy(struct sipe_core_private
*sipe_private
)
5508 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
5509 struct sipe_calendar
* cal
= sip
->cal
;
5510 guint cal_data_instance
= sipe_get_pub_instance(sipe_private
, SIPE_PUB_CALENDAR_DATA
);
5512 char *free_busy_base64
;
5517 /* key is <category><instance><container> */
5518 gchar
*key_cal_1
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 1);
5519 gchar
*key_cal_100
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 100);
5520 gchar
*key_cal_200
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 200);
5521 gchar
*key_cal_300
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 300);
5522 gchar
*key_cal_400
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 400);
5523 gchar
*key_cal_32000
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 32000);
5525 struct sipe_publication
*publication_cal_1
=
5526 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_1
);
5527 struct sipe_publication
*publication_cal_100
=
5528 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_100
);
5529 struct sipe_publication
*publication_cal_200
=
5530 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_200
);
5531 struct sipe_publication
*publication_cal_300
=
5532 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_300
);
5533 struct sipe_publication
*publication_cal_400
=
5534 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_400
);
5535 struct sipe_publication
*publication_cal_32000
=
5536 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_32000
);
5539 g_free(key_cal_100
);
5540 g_free(key_cal_200
);
5541 g_free(key_cal_300
);
5542 g_free(key_cal_400
);
5543 g_free(key_cal_32000
);
5545 if (!cal
|| is_empty(cal
->email
) || !cal
->fb_start
|| is_empty(cal
->free_busy
)) {
5546 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_free_busy: no data to publish, exiting");
5550 fb_start_str
= sipe_utils_time_to_str(cal
->fb_start
);
5551 free_busy_base64
= sipe_cal_get_freebusy_base64(cal
->free_busy
);
5553 st
= publication_cal_300
? publication_cal_300
->fb_start_str
: NULL
;
5554 fb
= publication_cal_300
? publication_cal_300
->free_busy_base64
: NULL
;
5556 /* we will rebuplish the same data to refresh publication time,
5557 * so if data from multiple sources, most recent will be choosen
5559 //if (sipe_strequal(st, fb_start_str) && sipe_strequal(fb, free_busy_base64))
5561 // SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_free_busy: FreeBusy has NOT changed. Exiting.");
5562 // g_free(fb_start_str);
5563 // g_free(free_busy_base64);
5564 // return NULL; /* nothing to update */
5567 res
= g_strdup_printf(SIPE_PUB_XML_FREE_BUSY
,
5570 publication_cal_1
? publication_cal_1
->version
: 0,
5573 publication_cal_100
? publication_cal_100
->version
: 0,
5576 publication_cal_200
? publication_cal_200
->version
: 0,
5582 publication_cal_300
? publication_cal_300
->version
: 0,
5586 /* 400 - Personal */
5588 publication_cal_400
? publication_cal_400
->version
: 0,
5592 /* 32000 - Blocked */
5594 publication_cal_32000
? publication_cal_32000
->version
: 0
5597 g_free(fb_start_str
);
5598 g_free(free_busy_base64
);
5602 static void send_presence_publish(struct sipe_core_private
*sipe_private
,
5603 const char *publications
)
5610 uri
= sip_uri_self(sipe_private
);
5611 doc
= g_strdup_printf(SIPE_SEND_PRESENCE
,
5615 tmp
= get_contact(sipe_private
);
5616 hdr
= g_strdup_printf("Contact: %s\r\n"
5617 "Content-Type: application/msrtc-category-publish+xml\r\n", tmp
);
5619 sip_transport_service(sipe_private
,
5623 process_send_presence_category_publish_response
);
5632 send_publish_category_initial(struct sipe_core_private
*sipe_private
)
5634 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
5635 gchar
*pub_device
= sipe_publish_get_category_device(sipe_private
);
5637 gchar
*publications
;
5639 g_free(sip
->status
);
5640 sip
->status
= g_strdup(SIPE_STATUS_ID_AVAILABLE
); /* our initial state */
5642 pub_machine
= sipe_publish_get_category_state_machine(sipe_private
);
5643 publications
= g_strdup_printf("%s%s",
5645 pub_machine
? pub_machine
: "");
5647 g_free(pub_machine
);
5649 send_presence_publish(sipe_private
, publications
);
5650 g_free(publications
);
5654 send_presence_category_publish(struct sipe_core_private
*sipe_private
)
5656 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
5657 gchar
*pub_state
= sipe_is_user_state(sipe_private
) ?
5658 sipe_publish_get_category_state_user(sipe_private
) :
5659 sipe_publish_get_category_state_machine(sipe_private
);
5660 gchar
*pub_note
= sipe_publish_get_category_note(sipe_private
,
5662 sip
->is_oof_note
? "OOF" : "personal",
5665 gchar
*publications
;
5667 if (!pub_state
&& !pub_note
) {
5668 SIPE_DEBUG_INFO_NOFORMAT("send_presence_category_publish: nothing has changed. Exiting.");
5672 publications
= g_strdup_printf("%s%s",
5673 pub_state
? pub_state
: "",
5674 pub_note
? pub_note
: "");
5679 send_presence_publish(sipe_private
, publications
);
5680 g_free(publications
);
5684 * Publishes self status
5685 * based on own calendar information.
5690 publish_calendar_status_self(struct sipe_core_private
*sipe_private
,
5691 SIPE_UNUSED_PARAMETER
void *unused
)
5693 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
5694 struct sipe_cal_event
* event
= NULL
;
5695 gchar
*pub_cal_working_hours
= NULL
;
5696 gchar
*pub_cal_free_busy
= NULL
;
5697 gchar
*pub_calendar
= NULL
;
5698 gchar
*pub_calendar2
= NULL
;
5699 gchar
*pub_oof_note
= NULL
;
5700 const gchar
*oof_note
;
5701 time_t oof_start
= 0;
5705 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self() no calendar data.");
5709 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self() started.");
5710 if (sip
->cal
->cal_events
) {
5711 event
= sipe_cal_get_event(sip
->cal
->cal_events
, time(NULL
));
5715 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self: current event is NULL");
5717 char *desc
= sipe_cal_event_describe(event
);
5718 SIPE_DEBUG_INFO("publish_calendar_status_self: current event is:\n%s", desc
? desc
: "");
5724 OOF publish, Busy clean
5726 OOF clean, Busy publish
5728 OOF clean, Busy clean
5730 if (event
&& event
->cal_status
== SIPE_CAL_OOF
) {
5731 pub_calendar
= sipe_publish_get_category_state_calendar(sipe_private
, event
, sip
->cal
->email
, SIPE_CAL_OOF
);
5732 pub_calendar2
= sipe_publish_get_category_state_calendar(sipe_private
, NULL
, sip
->cal
->email
, SIPE_CAL_BUSY
);
5733 } else if (event
&& event
->cal_status
== SIPE_CAL_BUSY
) {
5734 pub_calendar
= sipe_publish_get_category_state_calendar(sipe_private
, NULL
, sip
->cal
->email
, SIPE_CAL_OOF
);
5735 pub_calendar2
= sipe_publish_get_category_state_calendar(sipe_private
, event
, sip
->cal
->email
, SIPE_CAL_BUSY
);
5737 pub_calendar
= sipe_publish_get_category_state_calendar(sipe_private
, NULL
, sip
->cal
->email
, SIPE_CAL_OOF
);
5738 pub_calendar2
= sipe_publish_get_category_state_calendar(sipe_private
, NULL
, sip
->cal
->email
, SIPE_CAL_BUSY
);
5741 oof_note
= sipe_ews_get_oof_note(sip
->cal
);
5742 if (sipe_strequal("Scheduled", sip
->cal
->oof_state
)) {
5743 oof_start
= sip
->cal
->oof_start
;
5744 oof_end
= sip
->cal
->oof_end
;
5746 pub_oof_note
= sipe_publish_get_category_note(sipe_private
, oof_note
, "OOF", oof_start
, oof_end
);
5748 pub_cal_working_hours
= sipe_publish_get_category_cal_working_hours(sipe_private
);
5749 pub_cal_free_busy
= sipe_publish_get_category_cal_free_busy(sipe_private
);
5751 if (!pub_cal_working_hours
&& !pub_cal_free_busy
&& !pub_calendar
&& !pub_calendar2
&& !pub_oof_note
) {
5752 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self: nothing has changed.");
5754 gchar
*publications
= g_strdup_printf("%s%s%s%s%s",
5755 pub_cal_working_hours
? pub_cal_working_hours
: "",
5756 pub_cal_free_busy
? pub_cal_free_busy
: "",
5757 pub_calendar
? pub_calendar
: "",
5758 pub_calendar2
? pub_calendar2
: "",
5759 pub_oof_note
? pub_oof_note
: "");
5761 send_presence_publish(sipe_private
, publications
);
5762 g_free(publications
);
5765 g_free(pub_cal_working_hours
);
5766 g_free(pub_cal_free_busy
);
5767 g_free(pub_calendar
);
5768 g_free(pub_calendar2
);
5769 g_free(pub_oof_note
);
5771 /* repeat scheduling */
5772 sipe_sched_calendar_status_self_publish(sipe_private
, time(NULL
));
5775 static void send_presence_status(struct sipe_core_private
*sipe_private
,
5776 SIPE_UNUSED_PARAMETER
void *unused
)
5778 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
5779 PurpleStatus
* status
= purple_account_get_active_status(sip
->account
);
5781 if (!status
) return;
5783 SIPE_DEBUG_INFO("send_presence_status: status: %s (%s)",
5784 purple_status_get_id(status
) ? purple_status_get_id(status
) : "",
5785 sipe_is_user_state(sipe_private
) ? "USER" : "MACHINE");
5787 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
5788 send_presence_category_publish(sipe_private
);
5790 send_presence_soap(sipe_private
, FALSE
);
5794 static guint
sipe_ht_hash_nick(const char *nick
)
5796 char *lc
= g_utf8_strdown(nick
, -1);
5797 guint bucket
= g_str_hash(lc
);
5803 static gboolean
sipe_ht_equals_nick(const char *nick1
, const char *nick2
)
5805 char *nick1_norm
= NULL
;
5806 char *nick2_norm
= NULL
;
5809 if (nick1
== NULL
&& nick2
== NULL
) return TRUE
;
5810 if (nick1
== NULL
|| nick2
== NULL
||
5811 !g_utf8_validate(nick1
, -1, NULL
) ||
5812 !g_utf8_validate(nick2
, -1, NULL
)) return FALSE
;
5814 nick1_norm
= g_utf8_casefold(nick1
, -1);
5815 nick2_norm
= g_utf8_casefold(nick2
, -1);
5816 equal
= g_utf8_collate(nick1_norm
, nick2_norm
) == 0;
5823 /* temporary function */
5824 void sipe_purple_setup(struct sipe_core_public
*sipe_public
,
5825 PurpleConnection
*gc
)
5827 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA
;
5829 sip
->account
= purple_connection_get_account(gc
);
5832 struct sipe_core_public
*sipe_core_allocate(const gchar
*signin_name
,
5833 const gchar
*login_domain
,
5834 const gchar
*login_account
,
5835 const gchar
*password
,
5837 const gchar
*email_url
,
5838 const gchar
**errmsg
)
5840 struct sipe_core_private
*sipe_private
;
5841 struct sipe_account_data
*sip
;
5842 gchar
**user_domain
;
5844 SIPE_DEBUG_INFO("sipe_core_allocate: signin_name '%s'", signin_name
);
5846 /* ensure that sign-in name doesn't contain invalid characters */
5847 if (strpbrk(signin_name
, "\t\v\r\n") != NULL
) {
5848 *errmsg
= _("SIP Exchange user name contains invalid characters");
5852 /* ensure that sign-in name format is name@domain */
5853 if (!strchr(signin_name
, '@') ||
5854 g_str_has_prefix(signin_name
, "@") ||
5855 g_str_has_suffix(signin_name
, "@")) {
5856 *errmsg
= _("User name should be a valid SIP URI\nExample: user@company.com");
5860 /* ensure that email format is name@domain (if provided) */
5861 if (!is_empty(email
) &&
5862 (!strchr(email
, '@') ||
5863 g_str_has_prefix(email
, "@") ||
5864 g_str_has_suffix(email
, "@")))
5866 *errmsg
= _("Email address should be valid if provided\nExample: user@company.com");
5870 /* ensure that user name doesn't contain spaces */
5871 user_domain
= g_strsplit(signin_name
, "@", 2);
5872 SIPE_DEBUG_INFO("sipe_core_allocate: user '%s' domain '%s'", user_domain
[0], user_domain
[1]);
5873 if (strchr(user_domain
[0], ' ') != NULL
) {
5874 g_strfreev(user_domain
);
5875 *errmsg
= _("SIP Exchange user name contains whitespace");
5879 /* ensure that email_url is in proper format if enabled (if provided).
5880 * Example (Exchange): https://server.company.com/EWS/Exchange.asmx
5881 * Example (Domino) : https://[domino_server]/[mail_database_name].nsf
5883 if (!is_empty(email_url
)) {
5884 char *tmp
= g_ascii_strdown(email_url
, -1);
5885 if (!g_str_has_prefix(tmp
, "https://"))
5888 g_strfreev(user_domain
);
5889 *errmsg
= _("Email services URL should be valid if provided\n"
5890 "Example: https://exchange.corp.com/EWS/Exchange.asmx\n"
5891 "Example: https://domino.corp.com/maildatabase.nsf");
5897 sipe_private
= g_new0(struct sipe_core_private
, 1);
5898 sipe_private
->temporary
= sip
= g_new0(struct sipe_account_data
, 1);
5899 sip
->subscribed_buddies
= FALSE
;
5900 sip
->initial_state_published
= FALSE
;
5901 sipe_private
->username
= g_strdup(signin_name
);
5902 sip
->email
= is_empty(email
) ? g_strdup(signin_name
) : g_strdup(email
);
5903 sip
->authdomain
= is_empty(login_domain
) ? NULL
: g_strdup(login_domain
);
5904 sip
->authuser
= is_empty(login_account
) ? NULL
: g_strdup(login_account
);
5905 sip
->password
= g_strdup(password
);
5906 sipe_private
->public.sip_name
= g_strdup(user_domain
[0]);
5907 sipe_private
->public.sip_domain
= g_strdup(user_domain
[1]);
5908 g_strfreev(user_domain
);
5910 sipe_private
->buddies
= g_hash_table_new((GHashFunc
)sipe_ht_hash_nick
, (GEqualFunc
)sipe_ht_equals_nick
);
5911 sip
->our_publications
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
5912 g_free
, (GDestroyNotify
)g_hash_table_destroy
);
5913 sipe_subscriptions_init(sipe_private
);
5914 sip
->status
= g_strdup(SIPE_STATUS_ID_UNKNOWN
);
5916 return((struct sipe_core_public
*)sipe_private
);
5920 sipe_blist_menu_free_containers(struct sipe_core_private
*sipe_private
);
5922 void sipe_connection_cleanup(struct sipe_core_private
*sipe_private
)
5924 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
5926 g_free(sipe_private
->epid
);
5927 sipe_private
->epid
= NULL
;
5929 sip_transport_disconnect(sipe_private
);
5931 sipe_schedule_cancel_all(sipe_private
);
5933 if (sip
->allow_events
) {
5934 GSList
*entry
= sip
->allow_events
;
5936 g_free(entry
->data
);
5937 entry
= entry
->next
;
5940 g_slist_free(sip
->allow_events
);
5942 if (sip
->containers
) {
5943 GSList
*entry
= sip
->containers
;
5945 free_container((struct sipe_container
*)entry
->data
);
5946 entry
= entry
->next
;
5949 g_slist_free(sip
->containers
);
5951 /* libpurple memory leak workaround */
5952 sipe_blist_menu_free_containers(sipe_private
);
5954 if (sipe_private
->contact
)
5955 g_free(sipe_private
->contact
);
5956 sipe_private
->contact
= NULL
;
5958 g_free(sip
->regcallid
);
5959 sip
->regcallid
= NULL
;
5961 if (sipe_private
->focus_factory_uri
)
5962 g_free(sipe_private
->focus_factory_uri
);
5963 sipe_private
->focus_factory_uri
= NULL
;
5966 sipe_cal_calendar_free(sip
->cal
);
5970 sipe_groupchat_free(sipe_private
);
5974 * A callback for g_hash_table_foreach_remove
5976 static gboolean
sipe_buddy_remove(SIPE_UNUSED_PARAMETER gpointer key
, gpointer buddy
,
5977 SIPE_UNUSED_PARAMETER gpointer user_data
)
5979 sipe_free_buddy((struct sipe_buddy
*) buddy
);
5981 /* We must return TRUE as the key/value have already been deleted */
5985 void sipe_buddy_free_all(struct sipe_core_private
*sipe_private
)
5987 g_hash_table_foreach_steal(sipe_private
->buddies
, sipe_buddy_remove
, NULL
);
5990 static void sipe_searchresults_im_buddy(PurpleConnection
*gc
, GList
*row
,
5991 SIPE_UNUSED_PARAMETER
void *user_data
)
5993 PurpleAccount
*acct
= purple_connection_get_account(gc
);
5994 char *id
= sip_uri_from_name((gchar
*)g_list_nth_data(row
, 0));
5995 PurpleConversation
*conv
= purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM
, id
, acct
);
5997 conv
= purple_conversation_new(PURPLE_CONV_TYPE_IM
, acct
, id
);
5998 purple_conversation_present(conv
);
6002 static void sipe_searchresults_add_buddy(PurpleConnection
*gc
, GList
*row
,
6003 SIPE_UNUSED_PARAMETER
void *user_data
)
6006 purple_blist_request_add_buddy(purple_connection_get_account(gc
),
6007 g_list_nth_data(row
, 0), _("Other Contacts"), g_list_nth_data(row
, 1));
6010 static gboolean
process_search_contact_response(struct sipe_core_private
*sipe_private
,
6012 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
6014 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
6015 PurpleNotifySearchResults
*results
;
6016 PurpleNotifySearchColumn
*column
;
6017 sipe_xml
*searchResults
;
6018 const sipe_xml
*mrow
;
6019 int match_count
= 0;
6020 gboolean more
= FALSE
;
6023 SIPE_DEBUG_INFO("process_search_contact_response: body:\n%s", msg
->body
? msg
->body
: "");
6025 searchResults
= sipe_xml_parse(msg
->body
, msg
->bodylen
);
6026 if (!searchResults
) {
6027 SIPE_DEBUG_INFO_NOFORMAT("process_search_contact_response: no parseable searchResults");
6031 results
= purple_notify_searchresults_new();
6033 if (results
== NULL
) {
6034 SIPE_DEBUG_ERROR_NOFORMAT("purple_parse_searchreply: Unable to display the search results.");
6035 purple_notify_error(sip
->gc
, NULL
, _("Unable to display the search results"), NULL
);
6037 sipe_xml_free(searchResults
);
6041 column
= purple_notify_searchresults_column_new(_("User name"));
6042 purple_notify_searchresults_column_add(results
, column
);
6044 column
= purple_notify_searchresults_column_new(_("Name"));
6045 purple_notify_searchresults_column_add(results
, column
);
6047 column
= purple_notify_searchresults_column_new(_("Company"));
6048 purple_notify_searchresults_column_add(results
, column
);
6050 column
= purple_notify_searchresults_column_new(_("Country"));
6051 purple_notify_searchresults_column_add(results
, column
);
6053 column
= purple_notify_searchresults_column_new(_("Email"));
6054 purple_notify_searchresults_column_add(results
, column
);
6056 for (mrow
= sipe_xml_child(searchResults
, "Body/Array/row"); mrow
; mrow
= sipe_xml_twin(mrow
)) {
6059 gchar
**uri_parts
= g_strsplit(sipe_xml_attribute(mrow
, "uri"), ":", 2);
6060 row
= g_list_append(row
, g_strdup(uri_parts
[1]));
6061 g_strfreev(uri_parts
);
6063 row
= g_list_append(row
, g_strdup(sipe_xml_attribute(mrow
, "displayName")));
6064 row
= g_list_append(row
, g_strdup(sipe_xml_attribute(mrow
, "company")));
6065 row
= g_list_append(row
, g_strdup(sipe_xml_attribute(mrow
, "country")));
6066 row
= g_list_append(row
, g_strdup(sipe_xml_attribute(mrow
, "email")));
6068 purple_notify_searchresults_row_add(results
, row
);
6072 if ((mrow
= sipe_xml_child(searchResults
, "Body/directorySearch/moreAvailable")) != NULL
) {
6073 char *data
= sipe_xml_data(mrow
);
6074 more
= (g_strcasecmp(data
, "true") == 0);
6078 secondary
= g_strdup_printf(
6079 dngettext(PACKAGE_NAME
,
6080 "Found %d contact%s:",
6081 "Found %d contacts%s:", match_count
),
6082 match_count
, more
? _(" (more matched your query)") : "");
6084 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_IM
, sipe_searchresults_im_buddy
);
6085 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_ADD
, sipe_searchresults_add_buddy
);
6086 purple_notify_searchresults(sip
->gc
, NULL
, NULL
, secondary
, results
, NULL
, NULL
);
6089 sipe_xml_free(searchResults
);
6093 void sipe_search_contact_with_cb(PurpleConnection
*gc
, PurpleRequestFields
*fields
)
6095 GList
*entries
= purple_request_field_group_get_fields(purple_request_fields_get_groups(fields
)->data
);
6096 gchar
**attrs
= g_new(gchar
*, g_list_length(entries
) + 1);
6102 PurpleRequestField
*field
= entries
->data
;
6103 const char *id
= purple_request_field_get_id(field
);
6104 const char *value
= purple_request_field_string_get_value(field
);
6106 SIPE_DEBUG_INFO("sipe_search_contact_with_cb: %s = '%s'", id
, value
? value
: "");
6108 if (value
!= NULL
) attrs
[i
++] = g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW
, id
, value
);
6109 } while ((entries
= g_list_next(entries
)) != NULL
);
6113 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
6114 gchar
*domain_uri
= sip_uri_from_name(sipe_private
->public.sip_domain
);
6115 gchar
*query
= g_strjoinv(NULL
, attrs
);
6116 gchar
*body
= g_strdup_printf(SIPE_SOAP_SEARCH_CONTACT
, 100, query
);
6117 SIPE_DEBUG_INFO("sipe_search_contact_with_cb: body:\n%s", body
? body
: "");
6118 send_soap_request_with_cb(sipe_private
, domain_uri
, body
,
6119 process_search_contact_response
, NULL
);
6128 static void sipe_publish_get_cat_state_user_to_clear(SIPE_UNUSED_PARAMETER
const char *name
,
6132 struct sipe_publication
*publication
= value
;
6134 g_string_append_printf( str
,
6135 SIPE_PUB_XML_PUBLICATION_CLEAR
,
6136 publication
->category
,
6137 publication
->instance
,
6138 publication
->container
,
6139 publication
->version
,
6143 void sipe_core_reset_status(struct sipe_core_public
*sipe_public
)
6145 struct sipe_core_private
*sipe_private
= SIPE_CORE_PRIVATE
;
6146 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA
;
6147 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) /* 2007+ */
6149 GString
* str
= g_string_new(NULL
);
6150 gchar
*publications
;
6152 if (!sip
->user_state_publications
|| g_hash_table_size(sip
->user_state_publications
) == 0) {
6153 SIPE_DEBUG_INFO_NOFORMAT("sipe_reset_status: no userState publications, exiting.");
6157 g_hash_table_foreach(sip
->user_state_publications
, (GHFunc
)sipe_publish_get_cat_state_user_to_clear
, str
);
6158 publications
= g_string_free(str
, FALSE
);
6160 send_presence_publish(sipe_private
, publications
);
6161 g_free(publications
);
6165 send_presence_soap0(sipe_private
, FALSE
, TRUE
);
6169 /** for Access levels menu */
6170 #define INDENT_FMT " %s"
6172 /** Member is directly placed to access level container.
6173 * For example SIP URI of user is in the container.
6175 #define INDENT_MARKED_FMT "* %s"
6177 /** Member is indirectly belong to access level container.
6178 * For example 'sameEnterprise' is in the container and user
6179 * belongs to that same enterprise.
6181 #define INDENT_MARKED_INHERITED_FMT "= %s"
6183 GSList
*sipe_core_buddy_info(struct sipe_core_public
*sipe_public
,
6185 const gchar
*status_name
,
6188 struct sipe_core_private
*sipe_private
= SIPE_CORE_PRIVATE
;
6190 gboolean is_oof_note
= FALSE
;
6191 const gchar
*activity
= NULL
;
6192 gchar
*calendar
= NULL
;
6193 const gchar
*meeting_subject
= NULL
;
6194 const gchar
*meeting_location
= NULL
;
6195 gchar
*access_text
= NULL
;
6196 GSList
*info
= NULL
;
6198 #define SIPE_ADD_BUDDY_INFO_COMMON(l, t) \
6200 struct sipe_buddy_info *sbi = g_malloc(sizeof(struct sipe_buddy_info)); \
6203 info = g_slist_append(info, sbi); \
6205 #define SIPE_ADD_BUDDY_INFO(l, t) SIPE_ADD_BUDDY_INFO_COMMON((l), g_markup_escape_text((t), -1))
6206 #define SIPE_ADD_BUDDY_INFO_NOESCAPE(l, t) SIPE_ADD_BUDDY_INFO_COMMON((l), (t))
6208 if (sipe_public
) { //happens on pidgin exit
6209 struct sipe_buddy
*sbuddy
= g_hash_table_lookup(sipe_private
->buddies
, name
);
6211 note
= sbuddy
->note
;
6212 is_oof_note
= sbuddy
->is_oof_note
;
6213 activity
= sbuddy
->activity
;
6214 calendar
= sipe_cal_get_description(sbuddy
);
6215 meeting_subject
= sbuddy
->meeting_subject
;
6216 meeting_location
= sbuddy
->meeting_location
;
6218 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
6219 gboolean is_group_access
= FALSE
;
6220 const int container_id
= sipe_find_access_level(sipe_private
, "user", sipe_get_no_sip_uri(name
), &is_group_access
);
6221 const char *access_level
= sipe_get_access_level_name(container_id
);
6222 access_text
= is_group_access
?
6223 g_strdup(access_level
) :
6224 g_strdup_printf(INDENT_MARKED_FMT
, access_level
);
6231 const gchar
*status_str
= activity
? activity
: status_name
;
6233 SIPE_ADD_BUDDY_INFO(_("Status"), status_str
);
6235 if (is_online
&& !is_empty(calendar
))
6237 SIPE_ADD_BUDDY_INFO(_("Calendar"), calendar
);
6240 if (!is_empty(meeting_location
))
6242 SIPE_DEBUG_INFO("sipe_tooltip_text: %s meeting location: '%s'", name
, meeting_location
);
6243 SIPE_ADD_BUDDY_INFO(_("Meeting in"), meeting_location
);
6245 if (!is_empty(meeting_subject
))
6247 SIPE_DEBUG_INFO("sipe_tooltip_text: %s meeting subject: '%s'", name
, meeting_subject
);
6248 SIPE_ADD_BUDDY_INFO(_("Meeting about"), meeting_subject
);
6252 SIPE_DEBUG_INFO("sipe_tooltip_text: %s note: '%s'", name
, note
);
6253 SIPE_ADD_BUDDY_INFO_NOESCAPE(is_oof_note
? _("Out of office note") : _("Note"),
6254 g_strdup_printf("<i>%s</i>", note
));
6257 SIPE_ADD_BUDDY_INFO(_("Access level"), access_text
);
6258 g_free(access_text
);
6264 static PurpleBuddy
*
6265 purple_blist_add_buddy_clone(PurpleGroup
* group
, PurpleBuddy
* buddy
)
6268 const gchar
*server_alias
, *email
;
6269 const PurpleStatus
*status
= purple_presence_get_active_status(purple_buddy_get_presence(buddy
));
6271 clone
= purple_buddy_new(buddy
->account
, buddy
->name
, buddy
->alias
);
6273 purple_blist_add_buddy(clone
, NULL
, group
, NULL
);
6275 server_alias
= purple_buddy_get_server_alias(buddy
);
6277 purple_blist_server_alias_buddy(clone
, server_alias
);
6280 email
= purple_blist_node_get_string(&buddy
->node
, EMAIL_PROP
);
6282 purple_blist_node_set_string(&clone
->node
, EMAIL_PROP
, email
);
6285 purple_presence_set_status_active(purple_buddy_get_presence(clone
), purple_status_get_id(status
), TRUE
);
6287 purple_prpl_got_user_status(clone
->account
, clone
->name
, purple_status_get_id(status
), NULL
);
6292 sipe_buddy_menu_copy_to_cb(PurpleBlistNode
*node
, const char *group_name
)
6294 PurpleBuddy
*buddy
, *b
;
6295 PurpleConnection
*gc
;
6296 PurpleGroup
* group
= purple_find_group(group_name
);
6298 g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node
));
6300 buddy
= (PurpleBuddy
*)node
;
6302 SIPE_DEBUG_INFO("sipe_buddy_menu_copy_to_cb: copying %s to %s", buddy
->name
, group_name
);
6303 gc
= purple_account_get_connection(buddy
->account
);
6305 b
= purple_find_buddy_in_group(buddy
->account
, buddy
->name
, group
);
6307 b
= purple_blist_add_buddy_clone(group
, buddy
);
6310 sipe_add_buddy(gc
, b
, group
);
6314 sipe_buddy_menu_chat_new_cb(PurpleBuddy
*buddy
)
6316 struct sipe_core_private
*sipe_private
= PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE
;
6318 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_new_cb: buddy->name=%s", buddy
->name
);
6320 /* 2007+ conference */
6321 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
))
6323 sipe_conf_add(sipe_private
, buddy
->name
);
6325 else /* 2005- multiparty chat */
6327 gchar
*self
= sip_uri_self(sipe_private
);
6328 struct sip_session
*session
;
6330 session
= sipe_session_add_chat(sipe_private
,
6334 session
->chat_session
->backend
= sipe_backend_chat_create(SIPE_CORE_PUBLIC
,
6335 session
->chat_session
,
6336 session
->chat_session
->title
,
6340 sipe_invite(sipe_private
, session
, buddy
->name
, NULL
, NULL
, NULL
, FALSE
);
6345 * For 2007+ conference only.
6348 sipe_buddy_menu_chat_make_leader_cb(PurpleBuddy
*buddy
,
6349 struct sipe_chat_session
*chat_session
)
6351 struct sipe_core_private
*sipe_private
= PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE
;
6352 struct sip_session
*session
;
6354 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_make_leader_cb: buddy->name=%s", buddy
->name
);
6355 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_make_leader_cb: chat_title=%s", chat_session
->title
);
6357 session
= sipe_session_find_chat(sipe_private
, chat_session
);
6359 sipe_conf_modify_user_role(sipe_private
, session
, buddy
->name
);
6363 * For 2007+ conference only.
6366 sipe_buddy_menu_chat_remove_cb(PurpleBuddy
*buddy
,
6367 struct sipe_chat_session
*chat_session
)
6369 struct sipe_core_private
*sipe_private
= PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE
;
6370 struct sip_session
*session
;
6372 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_remove_cb: buddy->name=%s", buddy
->name
);
6373 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_remove_cb: chat_title=%s", chat_session
->title
);
6375 session
= sipe_session_find_chat(sipe_private
, chat_session
);
6377 sipe_conf_delete_user(sipe_private
, session
, buddy
->name
);
6381 sipe_buddy_menu_chat_invite_cb(PurpleBuddy
*buddy
,
6382 struct sipe_chat_session
*chat_session
)
6384 struct sipe_core_private
*sipe_private
= PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE
;
6386 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_invite_cb: buddy->name=%s", buddy
->name
);
6387 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_invite_cb: chat_title=%s", chat_session
->title
);
6389 sipe_core_chat_invite(SIPE_CORE_PUBLIC
, chat_session
, buddy
->name
);
6393 sipe_buddy_menu_make_call_cb(PurpleBuddy
*buddy
, const char *phone
)
6395 struct sipe_core_private
*sipe_private
= PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE
;
6397 SIPE_DEBUG_INFO("sipe_buddy_menu_make_call_cb: buddy->name=%s", buddy
->name
);
6399 char *tel_uri
= sip_to_tel_uri(phone
);
6401 SIPE_DEBUG_INFO("sipe_buddy_menu_make_call_cb: going to call number: %s", tel_uri
? tel_uri
: "");
6402 sip_csta_make_call(sipe_private
, tel_uri
);
6409 sipe_buddy_menu_access_level_help_cb(PurpleBuddy
*buddy
)
6411 /** Translators: replace with URL to localized page
6412 * If it doesn't exist copy the original URL */
6413 purple_notify_uri(buddy
->account
->gc
, _("https://sourceforge.net/apps/mediawiki/sipe/index.php?title=Access_Levels"));
6417 sipe_buddy_menu_send_email_cb(PurpleBuddy
*buddy
)
6420 SIPE_DEBUG_INFO("sipe_buddy_menu_send_email_cb: buddy->name=%s", buddy
->name
);
6422 email
= purple_blist_node_get_string(&buddy
->node
, EMAIL_PROP
);
6425 char *command_line
= g_strdup_printf(
6431 " mailto:%s", email
);
6432 SIPE_DEBUG_INFO("sipe_buddy_menu_send_email_cb: going to call email client: %s", command_line
);
6434 g_spawn_command_line_async(command_line
, NULL
);
6435 g_free(command_line
);
6439 SIPE_DEBUG_INFO("sipe_buddy_menu_send_email_cb: no email address stored for buddy=%s", buddy
->name
);
6444 sipe_buddy_menu_access_level_cb(PurpleBuddy
*buddy
,
6445 struct sipe_container
*container
)
6447 struct sipe_core_private
*sipe_private
= PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE
;
6448 struct sipe_container_member
*member
;
6450 if (!container
|| !container
->members
) return;
6452 member
= ((struct sipe_container_member
*)container
->members
->data
);
6454 if (!member
->type
) return;
6456 SIPE_DEBUG_INFO("sipe_buddy_menu_access_level_cb: container->id=%d, member->type=%s, member->value=%s",
6457 container
->id
, member
->type
, member
->value
? member
->value
: "");
6459 sipe_change_access_level(sipe_private
, container
->id
, member
->type
, member
->value
);
6463 sipe_get_access_control_menu(struct sipe_core_private
*sipe_private
,
6467 * A menu which appear when right-clicking on buddy in contact list.
6470 sipe_buddy_menu(PurpleBuddy
*buddy
)
6472 PurpleBlistNode
*g_node
;
6473 PurpleGroup
*gr_parent
;
6474 PurpleMenuAction
*act
;
6476 GList
*menu_groups
= NULL
;
6477 struct sipe_core_private
*sipe_private
= PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE
;
6478 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
6480 gchar
*self
= sip_uri_self(sipe_private
);
6482 SIPE_SESSION_FOREACH
{
6483 if (!sipe_strcase_equal(self
, buddy
->name
) && session
->chat_session
)
6485 struct sipe_chat_session
*chat_session
= session
->chat_session
;
6486 gboolean is_conf
= (chat_session
->type
== SIPE_CHAT_TYPE_CONFERENCE
);
6488 if (sipe_backend_chat_find(chat_session
->backend
, buddy
->name
))
6490 gboolean conf_op
= sipe_backend_chat_is_operator(chat_session
->backend
, self
);
6493 && !sipe_backend_chat_is_operator(chat_session
->backend
, buddy
->name
) /* Not conf OP */
6494 && conf_op
) /* We are a conf OP */
6496 gchar
*label
= g_strdup_printf(_("Make leader of '%s'"),
6497 chat_session
->title
);
6498 act
= purple_menu_action_new(label
,
6499 PURPLE_CALLBACK(sipe_buddy_menu_chat_make_leader_cb
),
6500 chat_session
, NULL
);
6502 menu
= g_list_prepend(menu
, act
);
6506 && conf_op
) /* We are a conf OP */
6508 gchar
*label
= g_strdup_printf(_("Remove from '%s'"),
6509 chat_session
->title
);
6510 act
= purple_menu_action_new(label
,
6511 PURPLE_CALLBACK(sipe_buddy_menu_chat_remove_cb
),
6512 chat_session
, NULL
);
6514 menu
= g_list_prepend(menu
, act
);
6520 || (is_conf
&& !session
->locked
))
6522 gchar
*label
= g_strdup_printf(_("Invite to '%s'"),
6523 chat_session
->title
);
6524 act
= purple_menu_action_new(label
,
6525 PURPLE_CALLBACK(sipe_buddy_menu_chat_invite_cb
),
6526 chat_session
, NULL
);
6528 menu
= g_list_prepend(menu
, act
);
6532 } SIPE_SESSION_FOREACH_END
;
6534 act
= purple_menu_action_new(_("New chat"),
6535 PURPLE_CALLBACK(sipe_buddy_menu_chat_new_cb
),
6537 menu
= g_list_prepend(menu
, act
);
6539 if (sip
->csta
&& !sip
->csta
->line_status
) {
6541 const char *phone_disp_str
;
6544 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_PROP
);
6545 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_DISPLAY_PROP
);
6547 gchar
*label
= g_strdup_printf(_("Work %s"),
6548 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
6549 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
6553 menu
= g_list_prepend(menu
, act
);
6557 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_MOBILE_PROP
);
6558 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_MOBILE_DISPLAY_PROP
);
6560 gchar
*label
= g_strdup_printf(_("Mobile %s"),
6561 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
6562 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
6566 menu
= g_list_prepend(menu
, act
);
6570 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_HOME_PROP
);
6571 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_HOME_DISPLAY_PROP
);
6573 gchar
*label
= g_strdup_printf(_("Home %s"),
6574 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
6575 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
6579 menu
= g_list_prepend(menu
, act
);
6583 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_OTHER_PROP
);
6584 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_OTHER_DISPLAY_PROP
);
6586 gchar
*label
= g_strdup_printf(_("Other %s"),
6587 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
6588 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
6592 menu
= g_list_prepend(menu
, act
);
6596 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_CUSTOM1_PROP
);
6597 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_CUSTOM1_DISPLAY_PROP
);
6599 gchar
*label
= g_strdup_printf(_("Custom1 %s"),
6600 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
6601 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
6605 menu
= g_list_prepend(menu
, act
);
6609 email
= purple_blist_node_get_string(&buddy
->node
, EMAIL_PROP
);
6611 act
= purple_menu_action_new(_("Send email..."),
6612 PURPLE_CALLBACK(sipe_buddy_menu_send_email_cb
),
6614 menu
= g_list_prepend(menu
, act
);
6618 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
6619 GList
*menu_access_levels
= sipe_get_access_control_menu(sipe_private
, buddy
->name
);
6621 act
= purple_menu_action_new(_("Access level"),
6623 NULL
, menu_access_levels
);
6624 menu
= g_list_prepend(menu
, act
);
6628 gr_parent
= purple_buddy_get_group(buddy
);
6629 for (g_node
= purple_blist_get_root(); g_node
; g_node
= g_node
->next
) {
6632 if (g_node
->type
!= PURPLE_BLIST_GROUP_NODE
)
6635 group
= (PurpleGroup
*)g_node
;
6636 if (group
== gr_parent
)
6639 if (purple_find_buddy_in_group(buddy
->account
, buddy
->name
, group
))
6642 act
= purple_menu_action_new(purple_group_get_name(group
),
6643 PURPLE_CALLBACK(sipe_buddy_menu_copy_to_cb
),
6645 menu_groups
= g_list_prepend(menu_groups
, act
);
6647 menu_groups
= g_list_reverse(menu_groups
);
6649 act
= purple_menu_action_new(_("Copy to"),
6652 menu
= g_list_prepend(menu
, act
);
6654 menu
= g_list_reverse(menu
);
6661 sipe_ask_access_domain_cb(PurpleConnection
*gc
, PurpleRequestFields
*fields
)
6663 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
6664 const char *domain
= purple_request_fields_get_string(fields
, "access_domain");
6665 int index
= purple_request_fields_get_choice(fields
, "container_id");
6666 /* move Blocked first */
6667 int i
= (index
== 4) ? 0 : index
+ 1;
6668 int container_id
= containers
[i
];
6670 SIPE_DEBUG_INFO("sipe_ask_access_domain_cb: domain=%s, container_id=(%d)%d", domain
? domain
: "", index
, container_id
);
6672 sipe_change_access_level(sipe_private
, container_id
, "domain", domain
);
6676 sipe_ask_access_domain(struct sipe_core_private
*sipe_private
)
6678 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
6679 PurpleAccount
*account
= sip
->account
;
6680 PurpleConnection
*gc
= sip
->gc
;
6681 PurpleRequestFields
*fields
;
6682 PurpleRequestFieldGroup
*g
;
6683 PurpleRequestField
*f
;
6685 fields
= purple_request_fields_new();
6687 g
= purple_request_field_group_new(NULL
);
6688 f
= purple_request_field_string_new("access_domain", _("Domain"), "partner-company.com", FALSE
);
6689 purple_request_field_set_required(f
, TRUE
);
6690 purple_request_field_group_add_field(g
, f
);
6692 f
= purple_request_field_choice_new("container_id", _("Access level"), 0);
6693 purple_request_field_choice_add(f
, _("Personal")); /* index 0 */
6694 purple_request_field_choice_add(f
, _("Team"));
6695 purple_request_field_choice_add(f
, _("Company"));
6696 purple_request_field_choice_add(f
, _("Public"));
6697 purple_request_field_choice_add(f
, _("Blocked")); /* index 4 */
6698 purple_request_field_choice_set_default_value(f
, 3); /* index */
6699 purple_request_field_set_required(f
, TRUE
);
6700 purple_request_field_group_add_field(g
, f
);
6702 purple_request_fields_add_group(fields
, g
);
6704 purple_request_fields(gc
, _("Add new domain"),
6705 _("Add new domain"), NULL
, fields
,
6706 _("Add"), G_CALLBACK(sipe_ask_access_domain_cb
),
6708 account
, NULL
, NULL
, gc
);
6712 sipe_buddy_menu_access_level_add_domain_cb(PurpleBuddy
*buddy
)
6714 sipe_ask_access_domain(PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE
);
6718 * Workaround for missing libpurple API to release resources allocated
6719 * during blist_node_menu() callback. See also:
6721 * <http://developer.pidgin.im/ticket/12597>
6723 * We remember all memory blocks in a list and deallocate them when
6725 * - the next time we enter the callback, or
6726 * - the account is disconnected
6728 * That means that after the buddy menu has been closed we have unused
6729 * resources but at least we don't leak them anymore...
6732 sipe_blist_menu_free_containers(struct sipe_core_private
*sipe_private
)
6734 GSList
*entry
= sipe_private
->blist_menu_containers
;
6736 free_container(entry
->data
);
6737 entry
= entry
->next
;
6739 g_slist_free(sipe_private
->blist_menu_containers
);
6740 sipe_private
->blist_menu_containers
= NULL
;
6744 sipe_blist_menu_remember_container(struct sipe_core_private
*sipe_private
,
6745 struct sipe_container
*container
)
6747 sipe_private
->blist_menu_containers
= g_slist_prepend(sipe_private
->blist_menu_containers
,
6752 sipe_get_access_levels_menu(struct sipe_core_private
*sipe_private
,
6753 const char* member_type
,
6754 const char* member_value
,
6755 const gboolean extra_menu
)
6757 GList
*menu_access_levels
= NULL
;
6760 PurpleMenuAction
*act
;
6761 struct sipe_container
*container
;
6762 struct sipe_container_member
*member
;
6763 gboolean is_group_access
= FALSE
;
6764 int container_id
= sipe_find_access_level(sipe_private
, member_type
, member_value
, &is_group_access
);
6766 for (i
= 1; i
<= CONTAINERS_LEN
; i
++) {
6767 /* to put Blocked level last in menu list.
6768 * Blocked should remaim in the first place in the containers[] array.
6770 unsigned int j
= (i
== CONTAINERS_LEN
) ? 0 : i
;
6771 const char *acc_level_name
= sipe_get_access_level_name(containers
[j
]);
6773 container
= g_new0(struct sipe_container
, 1);
6774 member
= g_new0(struct sipe_container_member
, 1);
6775 container
->id
= containers
[j
];
6776 container
->members
= g_slist_append(container
->members
, member
);
6777 member
->type
= g_strdup(member_type
);
6778 member
->value
= g_strdup(member_value
);
6780 /* libpurple memory leak workaround */
6781 sipe_blist_menu_remember_container(sipe_private
, container
);
6783 /* current container/access level */
6784 if (((int)containers
[j
]) == container_id
) {
6785 menu_name
= is_group_access
?
6786 g_strdup_printf(INDENT_MARKED_INHERITED_FMT
, acc_level_name
) :
6787 g_strdup_printf(INDENT_MARKED_FMT
, acc_level_name
);
6789 menu_name
= g_strdup_printf(INDENT_FMT
, acc_level_name
);
6792 act
= purple_menu_action_new(menu_name
,
6793 PURPLE_CALLBACK(sipe_buddy_menu_access_level_cb
),
6796 menu_access_levels
= g_list_prepend(menu_access_levels
, act
);
6799 if (extra_menu
&& (container_id
>= 0)) {
6801 act
= purple_menu_action_new(" --------------", NULL
, NULL
, NULL
);
6802 menu_access_levels
= g_list_prepend(menu_access_levels
, act
);
6804 if (!is_group_access
) {
6805 container
= g_new0(struct sipe_container
, 1);
6806 member
= g_new0(struct sipe_container_member
, 1);
6808 container
->members
= g_slist_append(container
->members
, member
);
6809 member
->type
= g_strdup(member_type
);
6810 member
->value
= g_strdup(member_value
);
6812 /* libpurple memory leak workaround */
6813 sipe_blist_menu_remember_container(sipe_private
, container
);
6815 /* Translators: remove (clear) previously assigned access level */
6816 menu_name
= g_strdup_printf(INDENT_FMT
, _("Unspecify"));
6817 act
= purple_menu_action_new(menu_name
,
6818 PURPLE_CALLBACK(sipe_buddy_menu_access_level_cb
),
6821 menu_access_levels
= g_list_prepend(menu_access_levels
, act
);
6825 menu_access_levels
= g_list_reverse(menu_access_levels
);
6826 return menu_access_levels
;
6830 sipe_get_access_groups_menu(struct sipe_core_private
*sipe_private
)
6832 GList
*menu_access_groups
= NULL
;
6833 PurpleMenuAction
*act
;
6834 GSList
*access_domains
= NULL
;
6839 act
= purple_menu_action_new(_("People in my company"),
6841 NULL
, sipe_get_access_levels_menu(sipe_private
, "sameEnterprise", NULL
, FALSE
));
6842 menu_access_groups
= g_list_prepend(menu_access_groups
, act
);
6844 /* this is original name, don't edit */
6845 act
= purple_menu_action_new(_("People in domains connected with my company"),
6847 NULL
, sipe_get_access_levels_menu(sipe_private
, "federated", NULL
, FALSE
));
6848 menu_access_groups
= g_list_prepend(menu_access_groups
, act
);
6850 act
= purple_menu_action_new(_("People in public domains"),
6852 NULL
, sipe_get_access_levels_menu(sipe_private
, "publicCloud", NULL
, TRUE
));
6853 menu_access_groups
= g_list_prepend(menu_access_groups
, act
);
6855 access_domains
= sipe_get_access_domains(sipe_private
);
6856 entry
= access_domains
;
6858 domain
= entry
->data
;
6860 menu_name
= g_strdup_printf(_("People at %s"), domain
);
6861 act
= purple_menu_action_new(menu_name
,
6863 NULL
, sipe_get_access_levels_menu(sipe_private
, "domain", g_strdup(domain
), TRUE
));
6864 menu_access_groups
= g_list_prepend(menu_access_groups
, act
);
6867 entry
= entry
->next
;
6871 /* People in domains connected with my company */
6872 act
= purple_menu_action_new("-------------------------------------------", NULL
, NULL
, NULL
);
6873 menu_access_groups
= g_list_prepend(menu_access_groups
, act
);
6875 act
= purple_menu_action_new(_("Add new domain..."),
6876 PURPLE_CALLBACK(sipe_buddy_menu_access_level_add_domain_cb
),
6878 menu_access_groups
= g_list_prepend(menu_access_groups
, act
);
6880 menu_access_groups
= g_list_reverse(menu_access_groups
);
6882 return menu_access_groups
;
6886 sipe_get_access_control_menu(struct sipe_core_private
*sipe_private
,
6889 GList
*menu_access_levels
= NULL
;
6890 GList
*menu_access_groups
= NULL
;
6892 PurpleMenuAction
*act
;
6894 /* libpurple memory leak workaround */
6895 sipe_blist_menu_free_containers(sipe_private
);
6897 menu_access_levels
= sipe_get_access_levels_menu(sipe_private
, "user", sipe_get_no_sip_uri(uri
), TRUE
);
6899 menu_access_groups
= sipe_get_access_groups_menu(sipe_private
);
6901 menu_name
= g_strdup_printf(INDENT_FMT
, _("Access groups"));
6902 act
= purple_menu_action_new(menu_name
,
6904 NULL
, menu_access_groups
);
6906 menu_access_levels
= g_list_append(menu_access_levels
, act
);
6908 menu_name
= g_strdup_printf(INDENT_FMT
, _("Online help..."));
6909 act
= purple_menu_action_new(menu_name
,
6910 PURPLE_CALLBACK(sipe_buddy_menu_access_level_help_cb
),
6913 menu_access_levels
= g_list_append(menu_access_levels
, act
);
6915 return menu_access_levels
;
6919 process_get_info_response(struct sipe_core_private
*sipe_private
,
6920 struct sipmsg
*msg
, struct transaction
*trans
)
6922 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
6923 char *uri
= trans
->payload
->data
;
6925 PurpleNotifyUserInfo
*info
;
6926 PurpleBuddy
*pbuddy
= NULL
;
6927 struct sipe_buddy
*sbuddy
;
6928 const char *alias
= NULL
;
6929 char *device_name
= NULL
;
6930 char *server_alias
= NULL
;
6931 char *phone_number
= NULL
;
6934 char *first_name
= NULL
;
6935 char *last_name
= NULL
;
6937 if (!sip
) return FALSE
;
6939 SIPE_DEBUG_INFO("Fetching %s's user info for %s", uri
, sipe_private
->username
);
6941 pbuddy
= purple_find_buddy((PurpleAccount
*)sip
->account
, uri
);
6942 alias
= purple_buddy_get_local_alias(pbuddy
);
6944 //will query buddy UA's capabilities and send answer to log
6945 sipe_options_request(sipe_private
, uri
);
6947 sbuddy
= g_hash_table_lookup(sipe_private
->buddies
, uri
);
6949 device_name
= sbuddy
->device_name
? g_strdup(sbuddy
->device_name
) : NULL
;
6952 info
= purple_notify_user_info_new();
6954 if (msg
->response
!= 200) {
6955 SIPE_DEBUG_INFO("process_get_info_response: SERVICE response is %d", msg
->response
);
6957 sipe_xml
*searchResults
;
6958 const sipe_xml
*mrow
;
6960 SIPE_DEBUG_INFO("process_get_info_response: body:\n%s", msg
->body
? msg
->body
: "");
6961 searchResults
= sipe_xml_parse(msg
->body
, msg
->bodylen
);
6962 if (!searchResults
) {
6963 SIPE_DEBUG_INFO_NOFORMAT("process_get_info_response: no parseable searchResults");
6964 } else if ((mrow
= sipe_xml_child(searchResults
, "Body/Array/row"))) {
6966 server_alias
= g_strdup(sipe_xml_attribute(mrow
, "displayName"));
6967 email
= g_strdup(sipe_xml_attribute(mrow
, "email"));
6968 phone_number
= g_strdup(sipe_xml_attribute(mrow
, "phone"));
6970 /* For 2007 system we will take this from ContactCard -
6971 * it has cleaner tel: URIs at least
6973 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
6974 char *tel_uri
= sip_to_tel_uri(phone_number
);
6975 /* trims its parameters, so call first */
6976 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_DISPLAY_NAME
, server_alias
);
6977 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_EMAIL
, email
);
6978 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_WORK_PHONE
, tel_uri
);
6979 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY
, phone_number
);
6983 if (server_alias
&& strlen(server_alias
) > 0) {
6984 purple_notify_user_info_add_pair(info
, _("Display name"), server_alias
);
6986 if ((value
= sipe_xml_attribute(mrow
, "title")) && strlen(value
) > 0) {
6987 purple_notify_user_info_add_pair(info
, _("Job title"), value
);
6989 if ((value
= sipe_xml_attribute(mrow
, "office")) && strlen(value
) > 0) {
6990 purple_notify_user_info_add_pair(info
, _("Office"), value
);
6992 if (phone_number
&& strlen(phone_number
) > 0) {
6993 purple_notify_user_info_add_pair(info
, _("Business phone"), phone_number
);
6995 if ((value
= sipe_xml_attribute(mrow
, "company")) && strlen(value
) > 0) {
6996 purple_notify_user_info_add_pair(info
, _("Company"), value
);
6998 if ((value
= sipe_xml_attribute(mrow
, "city")) && strlen(value
) > 0) {
6999 purple_notify_user_info_add_pair(info
, _("City"), value
);
7001 if ((value
= sipe_xml_attribute(mrow
, "state")) && strlen(value
) > 0) {
7002 purple_notify_user_info_add_pair(info
, _("State"), value
);
7004 if ((value
= sipe_xml_attribute(mrow
, "country")) && strlen(value
) > 0) {
7005 purple_notify_user_info_add_pair(info
, _("Country"), value
);
7007 if (email
&& strlen(email
) > 0) {
7008 purple_notify_user_info_add_pair(info
, _("Email address"), email
);
7012 sipe_xml_free(searchResults
);
7015 purple_notify_user_info_add_section_break(info
);
7017 if (is_empty(server_alias
)) {
7018 g_free(server_alias
);
7019 server_alias
= g_strdup(purple_buddy_get_server_alias(pbuddy
));
7021 purple_notify_user_info_add_pair(info
, _("Display name"), server_alias
);
7025 /* present alias if it differs from server alias */
7026 if (alias
&& !sipe_strequal(alias
, server_alias
))
7028 purple_notify_user_info_add_pair(info
, _("Alias"), alias
);
7031 if (is_empty(email
)) {
7033 email
= g_strdup(purple_blist_node_get_string(&pbuddy
->node
, EMAIL_PROP
));
7035 purple_notify_user_info_add_pair(info
, _("Email address"), email
);
7039 site
= purple_blist_node_get_string(&pbuddy
->node
, SITE_PROP
);
7041 purple_notify_user_info_add_pair(info
, _("Site"), site
);
7044 sipe_get_first_last_names(sipe_private
, uri
, &first_name
, &last_name
);
7045 if (first_name
&& last_name
) {
7046 char *link
= g_strconcat("http://www.linkedin.com/pub/dir/", first_name
, "/", last_name
, NULL
);
7048 purple_notify_user_info_add_pair(info
, _("Find on LinkedIn"), link
);
7055 purple_notify_user_info_add_pair(info
, _("Device"), device_name
);
7058 /* show a buddy's user info in a nice dialog box */
7059 purple_notify_userinfo(sip
->gc
, /* connection the buddy info came through */
7060 uri
, /* buddy's URI */
7062 NULL
, /* callback called when dialog closed */
7063 NULL
); /* userdata for callback */
7065 g_free(phone_number
);
7066 g_free(server_alias
);
7068 g_free(device_name
);
7074 * AD search first, LDAP based
7076 void sipe_get_info(PurpleConnection
*gc
, const char *username
)
7078 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
7079 gchar
*domain_uri
= sip_uri_from_name(sipe_private
->public.sip_domain
);
7080 char *row
= g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW
, "msRTCSIP-PrimaryUserAddress", username
);
7081 gchar
*body
= g_strdup_printf(SIPE_SOAP_SEARCH_CONTACT
, 1, row
);
7082 struct transaction_payload
*payload
= g_new0(struct transaction_payload
, 1);
7084 payload
->destroy
= g_free
;
7085 payload
->data
= g_strdup(username
);
7087 SIPE_DEBUG_INFO("sipe_get_contact_data: body:\n%s", body
? body
: "");
7088 send_soap_request_with_cb(sipe_private
, domain_uri
, body
,
7089 process_get_info_response
, payload
);