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
);
3107 static void sipe_send_message(struct sipe_core_private
*sipe_private
,
3108 struct sip_dialog
*dialog
,
3109 const char *msg
, const char *content_type
)
3113 char *msgtext
= NULL
;
3114 const gchar
*msgr
= "";
3117 if (content_type
== NULL
)
3118 content_type
= "text/plain";
3120 if (!g_str_has_prefix(content_type
, "text/x-msmsgsinvite")) {
3124 sipe_parse_html(msg
, &msgformat
, &msgtext
);
3125 SIPE_DEBUG_INFO("sipe_send_message: msgformat=%s", msgformat
);
3127 msgr_value
= sipmsg_get_msgr_string(msgformat
);
3130 msgr
= tmp2
= g_strdup_printf(";msgr=%s", msgr_value
);
3134 msgtext
= g_strdup(msg
);
3137 tmp
= get_contact(sipe_private
);
3138 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8\r\n");
3139 //hdr = g_strdup("Content-Type: text/rtf\r\n");
3140 //hdr = g_strdup("Content-Type: text/plain; charset=UTF-8;msgr=WAAtAE0ATQBTAC....AoADQA\r\nSupported: timer\r\n");
3142 hdr
= g_strdup_printf("Contact: %s\r\nContent-Type: %s; charset=UTF-8%s\r\n", tmp
, content_type
, msgr
);
3146 sip_transport_request(sipe_private
,
3153 process_message_response
);
3160 sipe_im_process_queue (struct sipe_core_private
*sipe_private
,
3161 struct sip_session
* session
)
3163 GSList
*entry2
= session
->outgoing_message_queue
;
3165 struct queued_message
*msg
= entry2
->data
;
3167 /* for multiparty chat or conference */
3168 if (session
->chat_session
) {
3169 gchar
*who
= sip_uri_self(sipe_private
);
3170 sipe_backend_chat_message(SIPE_CORE_PUBLIC
,
3171 session
->chat_session
->backend
,
3177 SIPE_DIALOG_FOREACH
{
3179 struct queued_message
*message
;
3181 if (dialog
->outgoing_invite
) continue; /* do not send messages as INVITE is not responded. */
3183 message
= g_new0(struct queued_message
,1);
3184 message
->body
= g_strdup(msg
->body
);
3185 if (msg
->content_type
!= NULL
)
3186 message
->content_type
= g_strdup(msg
->content_type
);
3188 key
= g_strdup_printf("<%s><%d><MESSAGE><%s>", dialog
->callid
, (dialog
->cseq
) + 1, dialog
->with
);
3189 g_hash_table_insert(session
->unconfirmed_messages
, g_strdup(key
), message
);
3190 SIPE_DEBUG_INFO("sipe_im_process_queue: added message %s to unconfirmed_messages(count=%d)",
3191 key
, g_hash_table_size(session
->unconfirmed_messages
));
3194 sipe_send_message(sipe_private
, dialog
, msg
->body
, msg
->content_type
);
3195 } SIPE_DIALOG_FOREACH_END
;
3197 entry2
= sipe_session_dequeue_message(session
);
3202 sipe_refer_notify(struct sipe_core_private
*sipe_private
,
3203 struct sip_session
*session
,
3210 struct sip_dialog
*dialog
= sipe_dialog_find(session
, who
);
3212 hdr
= g_strdup_printf(
3214 "Subscription-State: %s\r\n"
3215 "Content-Type: message/sipfrag\r\n",
3216 status
>= 200 ? "terminated" : "active");
3218 body
= g_strdup_printf(
3219 "SIP/2.0 %d %s\r\n",
3222 sip_transport_request(sipe_private
,
3236 process_invite_response(struct sipe_core_private
*sipe_private
,
3237 struct sipmsg
*msg
, struct transaction
*trans
)
3239 gchar
*with
= parse_from(sipmsg_find_header(msg
, "To"));
3240 struct sip_session
*session
;
3241 struct sip_dialog
*dialog
;
3244 struct queued_message
*message
;
3245 struct sipmsg
*request_msg
= trans
->msg
;
3247 const gchar
*callid
= sipmsg_find_header(msg
, "Call-ID");
3250 session
= sipe_session_find_chat_or_im(sipe_private
, callid
, with
);
3252 SIPE_DEBUG_INFO_NOFORMAT("process_invite_response: unable to find IM session");
3257 dialog
= sipe_dialog_find(session
, with
);
3259 SIPE_DEBUG_INFO_NOFORMAT("process_invite_response: session outgoing dialog is NULL");
3264 sipe_dialog_parse(dialog
, msg
, TRUE
);
3266 cseq
= sipmsg_find_part_of_header(sipmsg_find_header(msg
, "CSeq"), NULL
, " ", NULL
);
3267 key
= g_strdup_printf("<%s><%d><INVITE>", dialog
->callid
, atoi(cseq
));
3269 message
= g_hash_table_lookup(session
->unconfirmed_messages
, key
);
3271 if (msg
->response
!= 200) {
3272 sipe_backend_buddy pbuddy
;
3273 gchar
*alias
= g_strdup(with
);
3274 const char *warn_hdr
= sipmsg_find_header(msg
, "Warning");
3277 SIPE_DEBUG_INFO_NOFORMAT("process_invite_response: INVITE response not 200");
3280 gchar
**parts
= g_strsplit(warn_hdr
, " ", 2);
3282 warning
= atoi(parts
[0]);
3287 /* cancel file transfer as rejected by server */
3288 if (msg
->response
== 606 && /* Not acceptable all. */
3289 warning
== 309 && /* Message contents not allowed by policy */
3290 message
&& g_str_has_prefix(message
->content_type
, "text/x-msmsgsinvite"))
3292 GSList
*parsed_body
= sipe_ft_parse_msg_body(message
->body
);
3293 sipe_ft_incoming_cancel(dialog
, parsed_body
);
3294 sipe_utils_nameval_free(parsed_body
);
3297 if ((pbuddy
= sipe_backend_buddy_find(SIPE_CORE_PUBLIC
, with
, NULL
))) {
3299 alias
= sipe_backend_buddy_get_alias(SIPE_CORE_PUBLIC
, pbuddy
);
3303 sipe_present_message_undelivered_err(sipe_private
, session
, msg
->response
, warning
, alias
, message
->body
);
3305 gchar
*tmp_msg
= g_strdup_printf(_("Failed to invite %s"), alias
);
3306 sipe_present_err(sipe_private
, session
, tmp_msg
);
3310 sipe_dialog_remove(session
, with
);
3312 if (session
->is_groupchat
) {
3313 sipe_groupchat_invite_failed(sipe_private
, session
);
3323 sip_transport_ack(sipe_private
, dialog
);
3324 dialog
->outgoing_invite
= NULL
;
3325 dialog
->is_established
= TRUE
;
3327 referred_by
= parse_from(sipmsg_find_header(request_msg
, "Referred-By"));
3329 sipe_refer_notify(sipe_private
, session
, referred_by
, 200, "OK");
3330 g_free(referred_by
);
3333 /* add user to chat if it is a multiparty session */
3334 if (session
->chat_session
&&
3335 (session
->chat_session
->type
== SIPE_CHAT_TYPE_MULTIPARTY
)) {
3336 sipe_backend_chat_add(session
->chat_session
->backend
,
3341 if (session
->is_groupchat
) {
3342 sipe_groupchat_invite_response(sipe_private
, dialog
);
3345 if(g_slist_find_custom(dialog
->supported
, "ms-text-format", (GCompareFunc
)g_ascii_strcasecmp
)) {
3346 SIPE_DEBUG_INFO_NOFORMAT("process_invite_response: remote system accepted message in INVITE");
3347 sipe_session_dequeue_message(session
);
3350 sipe_im_process_queue(sipe_private
, session
);
3352 g_hash_table_remove(session
->unconfirmed_messages
, key
);
3353 SIPE_DEBUG_INFO("process_invite_response: removed message %s from unconfirmed_messages(count=%d)",
3354 key
, g_hash_table_size(session
->unconfirmed_messages
));
3363 sipe_invite(struct sipe_core_private
*sipe_private
,
3364 struct sip_session
*session
,
3366 const gchar
*msg_body
,
3367 const gchar
*msg_content_type
,
3368 const gchar
*referred_by
,
3369 const gboolean is_triggered
)
3376 char *ms_text_format
= NULL
;
3377 char *ms_conversation_id
= NULL
;
3378 gchar
*roster_manager
;
3380 gchar
*referred_by_str
;
3381 gboolean is_multiparty
=
3382 session
->chat_session
&&
3383 (session
->chat_session
->type
== SIPE_CHAT_TYPE_MULTIPARTY
);
3384 struct sip_dialog
*dialog
= sipe_dialog_find(session
, who
);
3386 if (dialog
&& dialog
->is_established
) {
3387 SIPE_DEBUG_INFO("session with %s already has a dialog open", who
);
3392 dialog
= sipe_dialog_add(session
);
3393 dialog
->callid
= session
->callid
? g_strdup(session
->callid
) : gencallid();
3394 dialog
->with
= g_strdup(who
);
3397 if (!(dialog
->ourtag
)) {
3398 dialog
->ourtag
= gentag();
3404 char *msgtext
= NULL
;
3406 const gchar
*msgr
= "";
3408 struct queued_message
*message
;
3411 if (!g_str_has_prefix(msg_content_type
, "text/x-msmsgsinvite")) {
3415 sipe_parse_html(msg_body
, &msgformat
, &msgtext
);
3416 SIPE_DEBUG_INFO("sipe_invite: msgformat=%s", msgformat
);
3418 msgr_value
= sipmsg_get_msgr_string(msgformat
);
3421 msgr
= tmp
= g_strdup_printf(";msgr=%s", msgr_value
);
3425 /* When Sipe reconnects after a crash, we are not able
3426 * to send messages to contacts with which we had open
3427 * conversations when the crash occured. Server sends
3428 * error response with reason="This client has an IM
3429 * session with the same conversation ID"
3431 * Setting random Ms-Conversation-ID prevents this problem
3432 * so we can continue the conversation. */
3433 ms_conversation_id
= g_strdup_printf("Ms-Conversation-ID: %u\r\n",
3434 rand() % 1000000000);
3436 msgtext
= g_strdup(msg_body
);
3439 base64_msg
= g_base64_encode((guchar
*) msgtext
, strlen(msgtext
));
3440 ms_text_format
= g_strdup_printf(SIPE_INVITE_TEXT
,
3441 msg_content_type
? msg_content_type
: "text/plain",
3448 message
= g_new0(struct queued_message
,1);
3449 message
->body
= g_strdup(msg_body
);
3450 if (msg_content_type
!= NULL
)
3451 message
->content_type
= g_strdup(msg_content_type
);
3453 key
= g_strdup_printf("<%s><%d><INVITE>", dialog
->callid
, (dialog
->cseq
) + 1);
3454 g_hash_table_insert(session
->unconfirmed_messages
, g_strdup(key
), message
);
3455 SIPE_DEBUG_INFO("sipe_invite: added message %s to unconfirmed_messages(count=%d)",
3456 key
, g_hash_table_size(session
->unconfirmed_messages
));
3460 contact
= get_contact(sipe_private
);
3461 end_points
= get_end_points(sipe_private
, session
);
3462 self
= sip_uri_self(sipe_private
);
3463 roster_manager
= g_strdup_printf(
3464 "Roster-Manager: %s\r\n"
3465 "EndPoints: %s\r\n",
3468 referred_by_str
= referred_by
?
3470 "Referred-By: %s\r\n",
3473 hdr
= g_strdup_printf(
3474 "Supported: ms-sender\r\n"
3481 "Content-Type: application/sdp\r\n",
3482 is_multiparty
&& sipe_strcase_equal(session
->chat_session
->id
, self
) ? roster_manager
: "",
3484 is_triggered
? "TriggeredInvite: TRUE\r\n" : "",
3485 is_triggered
|| is_multiparty
? "Require: com.microsoft.rtc-multiparty\r\n" : "",
3487 ms_text_format
? ms_text_format
: "",
3488 ms_conversation_id
? ms_conversation_id
: "");
3489 g_free(ms_text_format
);
3490 g_free(ms_conversation_id
);
3493 body
= g_strdup_printf(
3495 "o=- 0 0 IN IP4 %s\r\n"
3499 "m=%s %d sip null\r\n"
3500 "a=accept-types:" SDP_ACCEPT_TYPES
"\r\n",
3501 sipe_backend_network_ip_address(),
3502 sipe_backend_network_ip_address(),
3503 SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
) ? "message" : "x-ms-message",
3504 sip_transport_port(sipe_private
));
3506 dialog
->outgoing_invite
= sip_transport_request(sipe_private
,
3513 process_invite_response
);
3516 g_free(roster_manager
);
3518 g_free(referred_by_str
);
3525 sipe_convo_closed(PurpleConnection
* gc
, const char *who
)
3527 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
3529 SIPE_DEBUG_INFO("conversation with %s closed", who
);
3530 sipe_session_close(sipe_private
,
3531 sipe_session_find_im(sipe_private
, who
));
3534 int sipe_im_send(PurpleConnection
*gc
, const char *who
, const char *what
,
3535 SIPE_UNUSED_PARAMETER PurpleMessageFlags flags
)
3537 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
3538 struct sip_session
*session
;
3539 struct sip_dialog
*dialog
;
3540 gchar
*uri
= sip_uri(who
);
3542 SIPE_DEBUG_INFO("sipe_im_send what='%s'", what
);
3544 session
= sipe_session_find_or_add_im(sipe_private
, uri
);
3545 dialog
= sipe_dialog_find(session
, uri
);
3547 // Queue the message
3548 sipe_session_enqueue_message(session
, what
, NULL
);
3550 if (dialog
&& !dialog
->outgoing_invite
) {
3551 sipe_im_process_queue(sipe_private
, session
);
3552 } else if (!dialog
|| !dialog
->outgoing_invite
) {
3553 // Need to send the INVITE to get the outgoing dialog setup
3554 sipe_invite(sipe_private
, session
, uri
, what
, NULL
, NULL
, FALSE
);
3562 * Returns 2005-style activity and Availability.
3564 * @param status Sipe statis id.
3567 sipe_get_act_avail_by_status_2005(const char *status
,
3571 int avail
= 300; /* online */
3572 int act
= 400; /* Available */
3574 if (sipe_strequal(status
, SIPE_STATUS_ID_AWAY
)) {
3576 //} else if (sipe_strequal(status, SIPE_STATUS_ID_LUNCH)) {
3578 } else if (sipe_strequal(status
, SIPE_STATUS_ID_BRB
)) {
3580 } else if (sipe_strequal(status
, SIPE_STATUS_ID_AVAILABLE
)) {
3582 //} else if (sipe_strequal(status, SIPE_STATUS_ID_ON_PHONE)) {
3584 } else if (sipe_strequal(status
, SIPE_STATUS_ID_BUSY
) ||
3585 sipe_strequal(status
, SIPE_STATUS_ID_DND
)) {
3587 } else if (sipe_strequal(status
, SIPE_STATUS_ID_INVISIBLE
) ||
3588 sipe_strequal(status
, SIPE_STATUS_ID_OFFLINE
)) {
3589 avail
= 0; /* offline */
3592 act
= 400; /* Available */
3595 if (activity
) *activity
= act
;
3596 if (availability
) *availability
= avail
;
3602 * @param activity 2005 aggregated activity. Ex.: 600
3603 * @param availablity 2005 aggregated availablity. Ex.: 300
3606 sipe_get_status_by_act_avail_2005(const int activity
,
3607 const int availablity
,
3608 char **activity_desc
)
3610 const char *status_id
= NULL
;
3611 const char *act
= NULL
;
3613 if (activity
< 150) {
3614 status_id
= SIPE_STATUS_ID_AWAY
;
3615 } else if (activity
< 200) {
3616 //status_id = SIPE_STATUS_ID_LUNCH;
3617 status_id
= SIPE_STATUS_ID_AWAY
;
3618 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_LUNCH
);
3619 } else if (activity
< 300) {
3620 //status_id = SIPE_STATUS_ID_IDLE;
3621 status_id
= SIPE_STATUS_ID_AWAY
;
3622 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_INACTIVE
);
3623 } else if (activity
< 400) {
3624 status_id
= SIPE_STATUS_ID_BRB
;
3625 } else if (activity
< 500) {
3626 status_id
= SIPE_STATUS_ID_AVAILABLE
;
3627 } else if (activity
< 600) {
3628 //status_id = SIPE_STATUS_ID_ON_PHONE;
3629 status_id
= SIPE_STATUS_ID_BUSY
;
3630 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_ON_PHONE
);
3631 } else if (activity
< 700) {
3632 status_id
= SIPE_STATUS_ID_BUSY
;
3633 } else if (activity
< 800) {
3634 status_id
= SIPE_STATUS_ID_AWAY
;
3636 status_id
= SIPE_STATUS_ID_AVAILABLE
;
3639 if (availablity
< 100)
3640 status_id
= SIPE_STATUS_ID_OFFLINE
;
3642 if (activity_desc
&& act
) {
3643 g_free(*activity_desc
);
3644 *activity_desc
= g_strdup(act
);
3651 * [MS-PRES] Table 3: Conversion of legacyInterop elements and attributes to MSRTC elements and attributes.
3654 sipe_get_status_by_availability(int avail
,
3655 char** activity_desc
)
3658 const char *act
= NULL
;
3661 status
= SIPE_STATUS_ID_OFFLINE
;
3662 } else if (avail
< 4500) {
3663 status
= SIPE_STATUS_ID_AVAILABLE
;
3664 } else if (avail
< 6000) {
3665 //status = SIPE_STATUS_ID_IDLE;
3666 status
= SIPE_STATUS_ID_AVAILABLE
;
3667 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_INACTIVE
);
3668 } else if (avail
< 7500) {
3669 status
= SIPE_STATUS_ID_BUSY
;
3670 } else if (avail
< 9000) {
3671 //status = SIPE_STATUS_ID_BUSYIDLE;
3672 status
= SIPE_STATUS_ID_BUSY
;
3673 act
= SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_BUSYIDLE
);
3674 } else if (avail
< 12000) {
3675 status
= SIPE_STATUS_ID_DND
;
3676 } else if (avail
< 15000) {
3677 status
= SIPE_STATUS_ID_BRB
;
3678 } else if (avail
< 18000) {
3679 status
= SIPE_STATUS_ID_AWAY
;
3681 status
= SIPE_STATUS_ID_OFFLINE
;
3684 if (activity_desc
&& act
) {
3685 g_free(*activity_desc
);
3686 *activity_desc
= g_strdup(act
);
3693 * Returns 2007-style availability value
3695 * @param sipe_status_id (in)
3696 * @param activity_token (out) Must be g_free()'d after use if consumed.
3699 sipe_get_availability_by_status(const char* sipe_status_id
, char** activity_token
)
3702 sipe_activity activity
= SIPE_ACTIVITY_UNSET
;
3704 if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_AWAY
)) {
3705 availability
= 15500;
3706 if (!activity_token
|| !(*activity_token
)) {
3707 activity
= SIPE_ACTIVITY_AWAY
;
3709 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_BRB
)) {
3710 availability
= 12500;
3711 activity
= SIPE_ACTIVITY_BRB
;
3712 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_DND
)) {
3713 availability
= 9500;
3714 activity
= SIPE_ACTIVITY_DND
;
3715 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_BUSY
)) {
3716 availability
= 6500;
3717 if (!activity_token
|| !(*activity_token
)) {
3718 activity
= SIPE_ACTIVITY_BUSY
;
3720 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_AVAILABLE
)) {
3721 availability
= 3500;
3722 activity
= SIPE_ACTIVITY_ONLINE
;
3723 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_UNKNOWN
)) {
3726 // Offline or invisible
3727 availability
= 18500;
3728 activity
= SIPE_ACTIVITY_OFFLINE
;
3731 if (activity_token
) {
3732 *activity_token
= g_strdup(sipe_activity_map
[activity
].token
);
3734 return availability
;
3737 static void process_incoming_notify_rlmi(struct sipe_core_private
*sipe_private
,
3738 const gchar
*data
, unsigned len
)
3740 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
3742 sipe_xml
*xn_categories
;
3743 const sipe_xml
*xn_category
;
3744 const char *status
= NULL
;
3745 gboolean do_update_status
= FALSE
;
3746 gboolean has_note_cleaned
= FALSE
;
3747 gboolean has_free_busy_cleaned
= FALSE
;
3749 xn_categories
= sipe_xml_parse(data
, len
);
3750 uri
= sipe_xml_attribute(xn_categories
, "uri"); /* with 'sip:' prefix */
3752 for (xn_category
= sipe_xml_child(xn_categories
, "category");
3754 xn_category
= sipe_xml_twin(xn_category
) )
3756 const sipe_xml
*xn_node
;
3758 const char *attrVar
= sipe_xml_attribute(xn_category
, "name");
3759 time_t publish_time
= (tmp
= sipe_xml_attribute(xn_category
, "publishTime")) ?
3760 sipe_utils_str_to_time(tmp
) : 0;
3763 if (sipe_strequal(attrVar
, "contactCard"))
3765 const sipe_xml
*card
= sipe_xml_child(xn_category
, "contactCard");
3768 const sipe_xml
*node
;
3769 /* identity - Display Name and email */
3770 node
= sipe_xml_child(card
, "identity");
3772 char* display_name
= sipe_xml_data(
3773 sipe_xml_child(node
, "name/displayName"));
3774 char* email
= sipe_xml_data(
3775 sipe_xml_child(node
, "email"));
3777 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_DISPLAY_NAME
, display_name
);
3778 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_EMAIL
, email
);
3780 g_free(display_name
);
3784 node
= sipe_xml_child(card
, "company");
3786 char* company
= sipe_xml_data(node
);
3787 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_COMPANY
, company
);
3791 node
= sipe_xml_child(card
, "department");
3793 char* department
= sipe_xml_data(node
);
3794 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_DEPARTMENT
, department
);
3798 node
= sipe_xml_child(card
, "title");
3800 char* title
= sipe_xml_data(node
);
3801 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_JOB_TITLE
, title
);
3805 node
= sipe_xml_child(card
, "office");
3807 char* office
= sipe_xml_data(node
);
3808 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_OFFICE
, office
);
3812 node
= sipe_xml_child(card
, "url");
3814 char* site
= sipe_xml_data(node
);
3815 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_SITE
, site
);
3819 for (node
= sipe_xml_child(card
, "phone");
3821 node
= sipe_xml_twin(node
))
3823 const char *phone_type
= sipe_xml_attribute(node
, "type");
3824 char* phone
= sipe_xml_data(sipe_xml_child(node
, "uri"));
3825 char* phone_display_string
= sipe_xml_data(sipe_xml_child(node
, "displayString"));
3827 sipe_update_user_phone(sipe_private
, uri
, phone_type
, phone
, phone_display_string
);
3830 g_free(phone_display_string
);
3833 for (node
= sipe_xml_child(card
, "address");
3835 node
= sipe_xml_twin(node
))
3837 if (sipe_strequal(sipe_xml_attribute(node
, "type"), "work")) {
3838 char* street
= sipe_xml_data(sipe_xml_child(node
, "street"));
3839 char* city
= sipe_xml_data(sipe_xml_child(node
, "city"));
3840 char* state
= sipe_xml_data(sipe_xml_child(node
, "state"));
3841 char* zipcode
= sipe_xml_data(sipe_xml_child(node
, "zipcode"));
3842 char* country_code
= sipe_xml_data(sipe_xml_child(node
, "countryCode"));
3844 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_STREET
, street
);
3845 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_CITY
, city
);
3846 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_STATE
, state
);
3847 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_ZIPCODE
, zipcode
);
3848 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_COUNTRY
, country_code
);
3854 g_free(country_code
);
3862 else if (sipe_strequal(attrVar
, "note"))
3865 struct sipe_buddy
*sbuddy
= g_hash_table_lookup(sipe_private
->buddies
, uri
);
3867 if (!has_note_cleaned
) {
3868 has_note_cleaned
= TRUE
;
3870 g_free(sbuddy
->note
);
3871 sbuddy
->note
= NULL
;
3872 sbuddy
->is_oof_note
= FALSE
;
3873 sbuddy
->note_since
= publish_time
;
3875 do_update_status
= TRUE
;
3877 if (sbuddy
&& (publish_time
>= sbuddy
->note_since
)) {
3878 /* clean up in case no 'note' element is supplied
3879 * which indicate note removal in client
3881 g_free(sbuddy
->note
);
3882 sbuddy
->note
= NULL
;
3883 sbuddy
->is_oof_note
= FALSE
;
3884 sbuddy
->note_since
= publish_time
;
3886 xn_node
= sipe_xml_child(xn_category
, "note/body");
3889 sbuddy
->note
= g_markup_escape_text((tmp
= sipe_xml_data(xn_node
)), -1);
3891 sbuddy
->is_oof_note
= sipe_strequal(sipe_xml_attribute(xn_node
, "type"), "OOF");
3892 sbuddy
->note_since
= publish_time
;
3894 SIPE_DEBUG_INFO("process_incoming_notify_rlmi: uri(%s), note(%s)",
3895 uri
, sbuddy
->note
? sbuddy
->note
: "");
3897 /* to trigger UI refresh in case no status info is supplied in this update */
3898 do_update_status
= TRUE
;
3903 else if(sipe_strequal(attrVar
, "state"))
3907 const sipe_xml
*xn_availability
;
3908 const sipe_xml
*xn_activity
;
3909 const sipe_xml
*xn_meeting_subject
;
3910 const sipe_xml
*xn_meeting_location
;
3911 struct sipe_buddy
*sbuddy
= uri
? g_hash_table_lookup(sipe_private
->buddies
, uri
) : NULL
;
3913 xn_node
= sipe_xml_child(xn_category
, "state");
3914 if (!xn_node
) continue;
3915 xn_availability
= sipe_xml_child(xn_node
, "availability");
3916 if (!xn_availability
) continue;
3917 xn_activity
= sipe_xml_child(xn_node
, "activity");
3918 xn_meeting_subject
= sipe_xml_child(xn_node
, "meetingSubject");
3919 xn_meeting_location
= sipe_xml_child(xn_node
, "meetingLocation");
3921 tmp
= sipe_xml_data(xn_availability
);
3922 availability
= atoi(tmp
);
3925 /* activity, meeting_subject, meeting_location */
3930 g_free(sbuddy
->activity
);
3931 sbuddy
->activity
= NULL
;
3933 const char *token
= sipe_xml_attribute(xn_activity
, "token");
3934 const sipe_xml
*xn_custom
= sipe_xml_child(xn_activity
, "custom");
3937 if (!is_empty(token
)) {
3938 sbuddy
->activity
= g_strdup(sipe_get_activity_desc_by_token(token
));
3940 /* from custom element */
3942 char *custom
= sipe_xml_data(xn_custom
);
3944 if (!is_empty(custom
)) {
3945 sbuddy
->activity
= custom
;
3951 /* meeting_subject */
3952 g_free(sbuddy
->meeting_subject
);
3953 sbuddy
->meeting_subject
= NULL
;
3954 if (xn_meeting_subject
) {
3955 char *meeting_subject
= sipe_xml_data(xn_meeting_subject
);
3957 if (!is_empty(meeting_subject
)) {
3958 sbuddy
->meeting_subject
= meeting_subject
;
3959 meeting_subject
= NULL
;
3961 g_free(meeting_subject
);
3963 /* meeting_location */
3964 g_free(sbuddy
->meeting_location
);
3965 sbuddy
->meeting_location
= NULL
;
3966 if (xn_meeting_location
) {
3967 char *meeting_location
= sipe_xml_data(xn_meeting_location
);
3969 if (!is_empty(meeting_location
)) {
3970 sbuddy
->meeting_location
= meeting_location
;
3971 meeting_location
= NULL
;
3973 g_free(meeting_location
);
3976 status
= sipe_get_status_by_availability(availability
, &tmp
);
3977 if (sbuddy
->activity
&& tmp
) {
3978 char *tmp2
= sbuddy
->activity
;
3980 sbuddy
->activity
= g_strdup_printf("%s, %s", sbuddy
->activity
, tmp
);
3984 sbuddy
->activity
= tmp
;
3988 do_update_status
= TRUE
;
3991 else if(sipe_strequal(attrVar
, "calendarData"))
3993 struct sipe_buddy
*sbuddy
= uri
? g_hash_table_lookup(sipe_private
->buddies
, uri
) : NULL
;
3994 const sipe_xml
*xn_free_busy
= sipe_xml_child(xn_category
, "calendarData/freeBusy");
3995 const sipe_xml
*xn_working_hours
= sipe_xml_child(xn_category
, "calendarData/WorkingHours");
3997 if (sbuddy
&& xn_free_busy
) {
3998 if (!has_free_busy_cleaned
) {
3999 has_free_busy_cleaned
= TRUE
;
4001 g_free(sbuddy
->cal_start_time
);
4002 sbuddy
->cal_start_time
= NULL
;
4004 g_free(sbuddy
->cal_free_busy_base64
);
4005 sbuddy
->cal_free_busy_base64
= NULL
;
4007 g_free(sbuddy
->cal_free_busy
);
4008 sbuddy
->cal_free_busy
= NULL
;
4010 sbuddy
->cal_free_busy_published
= publish_time
;
4013 if (publish_time
>= sbuddy
->cal_free_busy_published
) {
4014 g_free(sbuddy
->cal_start_time
);
4015 sbuddy
->cal_start_time
= g_strdup(sipe_xml_attribute(xn_free_busy
, "startTime"));
4017 sbuddy
->cal_granularity
= sipe_strcase_equal(sipe_xml_attribute(xn_free_busy
, "granularity"), "PT15M") ?
4020 g_free(sbuddy
->cal_free_busy_base64
);
4021 sbuddy
->cal_free_busy_base64
= sipe_xml_data(xn_free_busy
);
4023 g_free(sbuddy
->cal_free_busy
);
4024 sbuddy
->cal_free_busy
= NULL
;
4026 sbuddy
->cal_free_busy_published
= publish_time
;
4028 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
);
4032 if (sbuddy
&& xn_working_hours
) {
4033 sipe_cal_parse_working_hours(xn_working_hours
, sbuddy
);
4038 if (do_update_status
) {
4039 if (!status
) { /* no status category in this update, using contact's current status */
4040 PurpleBuddy
*pbuddy
= purple_find_buddy((PurpleAccount
*)sip
->account
, uri
);
4041 const PurplePresence
*presence
= purple_buddy_get_presence(pbuddy
);
4042 const PurpleStatus
*pstatus
= purple_presence_get_active_status(presence
);
4043 status
= purple_status_get_id(pstatus
);
4046 SIPE_DEBUG_INFO("process_incoming_notify_rlmi: %s", status
);
4047 sipe_core_buddy_got_status(SIPE_CORE_PUBLIC
, uri
, status
);
4050 sipe_xml_free(xn_categories
);
4053 static void sipe_subscribe_poolfqdn_resource_uri(const char *host
,
4055 struct sipe_core_private
*sipe_private
)
4057 struct presence_batched_routed
*payload
= g_malloc(sizeof(struct presence_batched_routed
));
4058 SIPE_DEBUG_INFO("process_incoming_notify_rlmi_resub: pool(%s)", host
);
4059 payload
->host
= g_strdup(host
);
4060 payload
->buddies
= server
;
4061 sipe_subscribe_presence_batched_routed(sipe_private
,
4063 sipe_subscribe_presence_batched_routed_free(payload
);
4066 static void process_incoming_notify_rlmi_resub(struct sipe_core_private
*sipe_private
,
4067 const gchar
*data
, unsigned len
)
4070 const sipe_xml
*xn_resource
;
4071 GHashTable
*servers
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
4076 xn_list
= sipe_xml_parse(data
, len
);
4078 for (xn_resource
= sipe_xml_child(xn_list
, "resource");
4080 xn_resource
= sipe_xml_twin(xn_resource
) )
4082 const char *uri
, *state
;
4083 const sipe_xml
*xn_instance
;
4085 xn_instance
= sipe_xml_child(xn_resource
, "instance");
4086 if (!xn_instance
) continue;
4088 uri
= sipe_xml_attribute(xn_resource
, "uri");
4089 state
= sipe_xml_attribute(xn_instance
, "state");
4090 SIPE_DEBUG_INFO("process_incoming_notify_rlmi_resub: uri(%s),state(%s)", uri
, state
);
4092 if (strstr(state
, "resubscribe")) {
4093 const char *poolFqdn
= sipe_xml_attribute(xn_instance
, "poolFqdn");
4095 if (poolFqdn
) { //[MS-PRES] Section 3.4.5.1.3 Processing Details
4096 gchar
*user
= g_strdup(uri
);
4097 host
= g_strdup(poolFqdn
);
4098 server
= g_hash_table_lookup(servers
, host
);
4099 server
= g_slist_append(server
, user
);
4100 g_hash_table_insert(servers
, host
, server
);
4102 sipe_subscribe_presence_single(sipe_private
,
4108 /* Send out any deferred poolFqdn subscriptions */
4109 g_hash_table_foreach(servers
, (GHFunc
) sipe_subscribe_poolfqdn_resource_uri
, sipe_private
);
4110 g_hash_table_destroy(servers
);
4112 sipe_xml_free(xn_list
);
4115 static void process_incoming_notify_pidf(struct sipe_core_private
*sipe_private
,
4116 const gchar
*data
, unsigned len
)
4120 gchar
*activity
= NULL
;
4122 const sipe_xml
*basicstatus
= NULL
, *tuple
, *status
;
4123 gboolean isonline
= FALSE
;
4124 const sipe_xml
*display_name_node
;
4126 pidf
= sipe_xml_parse(data
, len
);
4128 SIPE_DEBUG_INFO("process_incoming_notify_pidf: no parseable pidf:%s", data
);
4132 if ((tuple
= sipe_xml_child(pidf
, "tuple")))
4134 if ((status
= sipe_xml_child(tuple
, "status"))) {
4135 basicstatus
= sipe_xml_child(status
, "basic");
4140 SIPE_DEBUG_INFO_NOFORMAT("process_incoming_notify_pidf: no basic found");
4141 sipe_xml_free(pidf
);
4145 getbasic
= sipe_xml_data(basicstatus
);
4147 SIPE_DEBUG_INFO_NOFORMAT("process_incoming_notify_pidf: no basic data found");
4148 sipe_xml_free(pidf
);
4152 SIPE_DEBUG_INFO("process_incoming_notify_pidf: basic-status(%s)", getbasic
);
4153 if (strstr(getbasic
, "open")) {
4158 uri
= sip_uri(sipe_xml_attribute(pidf
, "entity")); /* with 'sip:' prefix */ /* AOL comes without the prefix */
4160 display_name_node
= sipe_xml_child(pidf
, "display-name");
4161 if (display_name_node
) {
4162 char * display_name
= sipe_xml_data(display_name_node
);
4164 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_DISPLAY_NAME
, display_name
);
4165 g_free(display_name
);
4168 if ((tuple
= sipe_xml_child(pidf
, "tuple"))) {
4169 if ((status
= sipe_xml_child(tuple
, "status"))) {
4170 if ((basicstatus
= sipe_xml_child(status
, "activities"))) {
4171 if ((basicstatus
= sipe_xml_child(basicstatus
, "activity"))) {
4172 activity
= sipe_xml_data(basicstatus
);
4173 SIPE_DEBUG_INFO("process_incoming_notify_pidf: activity(%s)", activity
);
4180 const gchar
* status_id
= NULL
;
4182 if (sipe_strequal(activity
, sipe_activity_map
[SIPE_ACTIVITY_BUSY
].token
)) {
4183 status_id
= SIPE_STATUS_ID_BUSY
;
4184 } else if (sipe_strequal(activity
, sipe_activity_map
[SIPE_ACTIVITY_AWAY
].token
)) {
4185 status_id
= SIPE_STATUS_ID_AWAY
;
4190 status_id
= SIPE_STATUS_ID_AVAILABLE
;
4193 SIPE_DEBUG_INFO("process_incoming_notify_pidf: status_id(%s)", status_id
);
4194 sipe_core_buddy_got_status(SIPE_CORE_PUBLIC
, uri
, status_id
);
4196 sipe_core_buddy_got_status(SIPE_CORE_PUBLIC
, uri
, SIPE_STATUS_ID_OFFLINE
);
4201 sipe_xml_free(pidf
);
4206 sipe_user_info_has_updated(struct sipe_core_private
*sipe_private
,
4207 const sipe_xml
*xn_userinfo
)
4209 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
4210 const sipe_xml
*xn_states
;
4212 g_free(sip
->user_states
);
4213 sip
->user_states
= NULL
;
4214 if ((xn_states
= sipe_xml_child(xn_userinfo
, "states")) != NULL
) {
4215 gchar
*orig
= sip
->user_states
= sipe_xml_stringify(xn_states
);
4217 /* this is a hack-around to remove added newline after inner element,
4218 * state in this case, where it shouldn't be.
4219 * After several use of sipe_xml_stringify, amount of added newlines
4220 * grows significantly.
4223 gchar c
, *stripped
= orig
;
4224 while ((c
= *orig
++)) {
4225 if ((c
!= '\n') /* && (c != '\r') */) {
4233 /* Publish initial state if not yet.
4234 * Assuming this happens on initial responce to self subscription
4235 * so we've already updated our UserInfo.
4237 if (!sip
->initial_state_published
) {
4238 send_presence_soap(sipe_private
, FALSE
);
4240 sipe_schedule_seconds(sipe_private
,
4241 "<+update-calendar>",
4243 UPDATE_CALENDAR_DELAY
,
4244 (sipe_schedule_action
) sipe_core_update_calendar
,
4249 static void process_incoming_notify_msrtc(struct sipe_core_private
*sipe_private
,
4250 const gchar
*data
, unsigned len
)
4252 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
4253 char *activity
= NULL
;
4255 const char *status_id
= NULL
;
4258 char *self_uri
= sip_uri_self(sipe_private
);
4261 const char *device_name
= NULL
;
4262 const char *cal_start_time
= NULL
;
4263 const char *cal_granularity
= NULL
;
4264 char *cal_free_busy_base64
= NULL
;
4265 struct sipe_buddy
*sbuddy
;
4266 const sipe_xml
*node
;
4267 sipe_xml
*xn_presentity
;
4268 const sipe_xml
*xn_availability
;
4269 const sipe_xml
*xn_activity
;
4270 const sipe_xml
*xn_display_name
;
4271 const sipe_xml
*xn_email
;
4272 const sipe_xml
*xn_phone_number
;
4273 const sipe_xml
*xn_userinfo
;
4274 const sipe_xml
*xn_note
;
4275 const sipe_xml
*xn_oof
;
4276 const sipe_xml
*xn_state
;
4277 const sipe_xml
*xn_contact
;
4279 char *free_activity
;
4281 const char *user_avail_nil
;
4283 time_t user_avail_since
= 0;
4284 time_t activity_since
= 0;
4286 /* fix for Reuters environment on Linux */
4287 if (data
&& strstr(data
, "encoding=\"utf-16\"")) {
4289 tmp_data
= replace(data
, "encoding=\"utf-16\"", "encoding=\"utf-8\"");
4290 xn_presentity
= sipe_xml_parse(tmp_data
, strlen(tmp_data
));
4293 xn_presentity
= sipe_xml_parse(data
, len
);
4296 xn_availability
= sipe_xml_child(xn_presentity
, "availability");
4297 xn_activity
= sipe_xml_child(xn_presentity
, "activity");
4298 xn_display_name
= sipe_xml_child(xn_presentity
, "displayName");
4299 xn_email
= sipe_xml_child(xn_presentity
, "email");
4300 xn_phone_number
= sipe_xml_child(xn_presentity
, "phoneNumber");
4301 xn_userinfo
= sipe_xml_child(xn_presentity
, "userInfo");
4302 xn_oof
= xn_userinfo
? sipe_xml_child(xn_userinfo
, "oof") : NULL
;
4303 xn_state
= xn_userinfo
? sipe_xml_child(xn_userinfo
, "states/state"): NULL
;
4304 user_avail
= xn_state
? sipe_xml_int_attribute(xn_state
, "avail", 0) : 0;
4305 user_avail_since
= xn_state
? sipe_utils_str_to_time(sipe_xml_attribute(xn_state
, "since")) : 0;
4306 user_avail_nil
= xn_state
? sipe_xml_attribute(xn_state
, "nil") : NULL
;
4307 xn_contact
= xn_userinfo
? sipe_xml_child(xn_userinfo
, "contact") : NULL
;
4308 xn_note
= xn_userinfo
? sipe_xml_child(xn_userinfo
, "note") : NULL
;
4309 note
= xn_note
? sipe_xml_data(xn_note
) : NULL
;
4311 if (sipe_strequal(user_avail_nil
, "true")) { /* null-ed */
4313 user_avail_since
= 0;
4316 free_activity
= NULL
;
4318 name
= sipe_xml_attribute(xn_presentity
, "uri"); /* without 'sip:' prefix */
4319 uri
= sip_uri_from_name(name
);
4320 avl
= sipe_xml_int_attribute(xn_availability
, "aggregate", 0);
4321 epid
= sipe_xml_attribute(xn_availability
, "epid");
4322 act
= sipe_xml_int_attribute(xn_activity
, "aggregate", 0);
4324 status_id
= sipe_get_status_by_act_avail_2005(act
, avl
, &activity
);
4325 res_avail
= sipe_get_availability_by_status(status_id
, NULL
);
4326 if (user_avail
> res_avail
) {
4327 res_avail
= user_avail
;
4328 status_id
= sipe_get_status_by_availability(user_avail
, NULL
);
4331 if (xn_display_name
) {
4332 char *display_name
= g_strdup(sipe_xml_attribute(xn_display_name
, "displayName"));
4333 char *email
= xn_email
? g_strdup(sipe_xml_attribute(xn_email
, "email")) : NULL
;
4334 char *phone_label
= xn_phone_number
? g_strdup(sipe_xml_attribute(xn_phone_number
, "label")) : NULL
;
4335 char *phone_number
= xn_phone_number
? g_strdup(sipe_xml_attribute(xn_phone_number
, "number")) : NULL
;
4336 char *tel_uri
= sip_to_tel_uri(phone_number
);
4338 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_DISPLAY_NAME
, display_name
);
4339 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_EMAIL
, email
);
4340 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_WORK_PHONE
, tel_uri
);
4341 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY
, !is_empty(phone_label
) ? phone_label
: phone_number
);
4344 g_free(phone_label
);
4345 g_free(phone_number
);
4347 g_free(display_name
);
4352 for (node
= sipe_xml_child(xn_contact
, "tel"); node
; node
= sipe_xml_twin(node
))
4354 /* Ex.: <tel type="work">tel:+3222220000</tel> */
4355 const char *phone_type
= sipe_xml_attribute(node
, "type");
4356 char* phone
= sipe_xml_data(node
);
4358 sipe_update_user_phone(sipe_private
, uri
, phone_type
, phone
, NULL
);
4364 /* devicePresence */
4365 for (node
= sipe_xml_child(xn_presentity
, "devices/devicePresence"); node
; node
= sipe_xml_twin(node
)) {
4366 const sipe_xml
*xn_device_name
;
4367 const sipe_xml
*xn_calendar_info
;
4368 const sipe_xml
*xn_state
;
4372 if (sipe_strequal(sipe_xml_attribute(node
, "epid"), epid
)) {
4373 xn_device_name
= sipe_xml_child(node
, "deviceName");
4374 device_name
= xn_device_name
? sipe_xml_attribute(xn_device_name
, "name") : NULL
;
4378 xn_calendar_info
= sipe_xml_child(node
, "calendarInfo");
4379 if (xn_calendar_info
) {
4380 const char *cal_start_time_tmp
= sipe_xml_attribute(xn_calendar_info
, "startTime");
4382 if (cal_start_time
) {
4383 time_t cal_start_time_t
= sipe_utils_str_to_time(cal_start_time
);
4384 time_t cal_start_time_t_tmp
= sipe_utils_str_to_time(cal_start_time_tmp
);
4386 if (cal_start_time_t_tmp
> cal_start_time_t
) {
4387 cal_start_time
= cal_start_time_tmp
;
4388 cal_granularity
= sipe_xml_attribute(xn_calendar_info
, "granularity");
4389 g_free(cal_free_busy_base64
);
4390 cal_free_busy_base64
= sipe_xml_data(xn_calendar_info
);
4392 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
);
4395 cal_start_time
= cal_start_time_tmp
;
4396 cal_granularity
= sipe_xml_attribute(xn_calendar_info
, "granularity");
4397 g_free(cal_free_busy_base64
);
4398 cal_free_busy_base64
= sipe_xml_data(xn_calendar_info
);
4400 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
);
4405 xn_state
= sipe_xml_child(node
, "states/state");
4407 int dev_avail
= sipe_xml_int_attribute(xn_state
, "avail", 0);
4408 time_t dev_avail_since
= sipe_utils_str_to_time(sipe_xml_attribute(xn_state
, "since"));
4410 state
= sipe_xml_data(xn_state
);
4411 if (dev_avail_since
> user_avail_since
&&
4412 dev_avail
>= res_avail
)
4414 res_avail
= dev_avail
;
4415 if (!is_empty(state
))
4417 if (sipe_strequal(state
, sipe_activity_map
[SIPE_ACTIVITY_ON_PHONE
].token
)) {
4419 activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_ON_PHONE
));
4420 } else if (sipe_strequal(state
, "presenting")) {
4422 activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_IN_CONF
));
4427 activity_since
= dev_avail_since
;
4429 status_id
= sipe_get_status_by_availability(res_avail
, &activity
);
4436 if (xn_oof
&& res_avail
>= 15000) { /* 12000 in 2007 */
4438 activity
= g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_OOF
));
4442 sbuddy
= g_hash_table_lookup(sipe_private
->buddies
, uri
);
4445 g_free(sbuddy
->activity
);
4446 sbuddy
->activity
= activity
;
4449 sbuddy
->activity_since
= activity_since
;
4451 sbuddy
->user_avail
= user_avail
;
4452 sbuddy
->user_avail_since
= user_avail_since
;
4454 g_free(sbuddy
->note
);
4455 sbuddy
->note
= NULL
;
4456 if (!is_empty(note
)) { sbuddy
->note
= g_markup_escape_text(note
, -1); }
4458 sbuddy
->is_oof_note
= (xn_oof
!= NULL
);
4460 g_free(sbuddy
->device_name
);
4461 sbuddy
->device_name
= NULL
;
4462 if (!is_empty(device_name
)) { sbuddy
->device_name
= g_strdup(device_name
); }
4464 if (!is_empty(cal_free_busy_base64
)) {
4465 g_free(sbuddy
->cal_start_time
);
4466 sbuddy
->cal_start_time
= g_strdup(cal_start_time
);
4468 sbuddy
->cal_granularity
= sipe_strcase_equal(cal_granularity
, "PT15M") ? 15 : 0;
4470 g_free(sbuddy
->cal_free_busy_base64
);
4471 sbuddy
->cal_free_busy_base64
= cal_free_busy_base64
;
4472 cal_free_busy_base64
= NULL
;
4474 g_free(sbuddy
->cal_free_busy
);
4475 sbuddy
->cal_free_busy
= NULL
;
4478 sbuddy
->last_non_cal_status_id
= status_id
;
4479 g_free(sbuddy
->last_non_cal_activity
);
4480 sbuddy
->last_non_cal_activity
= g_strdup(sbuddy
->activity
);
4482 if (sipe_strcase_equal(sbuddy
->name
, self_uri
)) {
4483 if (!sipe_strequal(sbuddy
->note
, sip
->note
)) /* not same */
4485 sip
->is_oof_note
= sbuddy
->is_oof_note
;
4488 sip
->note
= g_strdup(sbuddy
->note
);
4490 sip
->note_since
= time(NULL
);
4493 g_free(sip
->status
);
4494 sip
->status
= g_strdup(sbuddy
->last_non_cal_status_id
);
4497 g_free(cal_free_busy_base64
);
4500 SIPE_DEBUG_INFO("process_incoming_notify_msrtc: status(%s)", status_id
);
4501 sipe_core_buddy_got_status(SIPE_CORE_PUBLIC
, uri
, status_id
);
4503 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
) && sipe_strcase_equal(self_uri
, uri
)) {
4504 sipe_user_info_has_updated(sipe_private
, xn_userinfo
);
4508 sipe_xml_free(xn_presentity
);
4513 static void sipe_presence_mime_cb(gpointer user_data
, /* sipe_core_private */
4514 const GSList
*fields
,
4518 const gchar
*type
= sipe_utils_nameval_find(fields
, "Content-Type");
4520 if (strstr(type
,"application/rlmi+xml")) {
4521 process_incoming_notify_rlmi_resub(user_data
, body
, length
);
4522 } else if (strstr(type
, "text/xml+msrtc.pidf")) {
4523 process_incoming_notify_msrtc(user_data
, body
, length
);
4525 process_incoming_notify_rlmi(user_data
, body
, length
);
4529 static void sipe_process_presence(struct sipe_core_private
*sipe_private
,
4532 const char *ctype
= sipmsg_find_header(msg
, "Content-Type");
4534 SIPE_DEBUG_INFO("sipe_process_presence: Content-Type: %s", ctype
? ctype
: "");
4537 (strstr(ctype
, "application/rlmi+xml") ||
4538 strstr(ctype
, "application/msrtc-event-categories+xml")))
4540 if (strstr(ctype
, "multipart"))
4542 sipe_mime_parts_foreach(ctype
, msg
->body
, sipe_presence_mime_cb
, sipe_private
);
4544 else if(strstr(ctype
, "application/msrtc-event-categories+xml") )
4546 process_incoming_notify_rlmi(sipe_private
, msg
->body
, msg
->bodylen
);
4548 else if(strstr(ctype
, "application/rlmi+xml"))
4550 process_incoming_notify_rlmi_resub(sipe_private
, msg
->body
, msg
->bodylen
);
4553 else if(ctype
&& strstr(ctype
, "text/xml+msrtc.pidf"))
4555 process_incoming_notify_msrtc(sipe_private
, msg
->body
, msg
->bodylen
);
4559 process_incoming_notify_pidf(sipe_private
, msg
->body
, msg
->bodylen
);
4563 static void sipe_presence_timeout_mime_cb(gpointer user_data
,
4564 SIPE_UNUSED_PARAMETER
const GSList
*fields
,
4568 GSList
**buddies
= user_data
;
4569 sipe_xml
*xml
= sipe_xml_parse(body
, length
);
4571 if (xml
&& !sipe_strequal(sipe_xml_name(xml
), "list")) {
4572 const gchar
*uri
= sipe_xml_attribute(xml
, "uri");
4573 const sipe_xml
*xn_category
;
4576 * automaton: presence is never expected to change
4578 * see: http://msdn.microsoft.com/en-us/library/ee354295(office.13).aspx
4580 for (xn_category
= sipe_xml_child(xml
, "category");
4582 xn_category
= sipe_xml_twin(xn_category
)) {
4583 if (sipe_strequal(sipe_xml_attribute(xn_category
, "name"),
4585 const sipe_xml
*node
= sipe_xml_child(xn_category
, "contactCard/automaton");
4587 char *boolean
= sipe_xml_data(node
);
4588 if (sipe_strequal(boolean
, "true")) {
4589 SIPE_DEBUG_INFO("sipe_process_presence_timeout: %s is an automaton: - not subscribing to presence updates",
4600 *buddies
= g_slist_append(*buddies
, sip_uri(uri
));
4607 static void sipe_process_presence_timeout(struct sipe_core_private
*sipe_private
,
4608 struct sipmsg
*msg
, gchar
*who
,
4611 const char *ctype
= sipmsg_find_header(msg
, "Content-Type");
4612 gchar
*action_name
= sipe_utils_presence_key(who
);
4614 SIPE_DEBUG_INFO("sipe_process_presence_timeout: Content-Type: %s", ctype
? ctype
: "");
4617 strstr(ctype
, "multipart") &&
4618 (strstr(ctype
, "application/rlmi+xml") ||
4619 strstr(ctype
, "application/msrtc-event-categories+xml"))) {
4620 GSList
*buddies
= NULL
;
4622 sipe_mime_parts_foreach(ctype
, msg
->body
, sipe_presence_timeout_mime_cb
, &buddies
);
4625 struct presence_batched_routed
*payload
= g_malloc(sizeof(struct presence_batched_routed
));
4626 payload
->host
= g_strdup(who
);
4627 payload
->buddies
= buddies
;
4628 sipe_schedule_seconds(sipe_private
,
4632 sipe_subscribe_presence_batched_routed
,
4633 sipe_subscribe_presence_batched_routed_free
);
4634 SIPE_DEBUG_INFO("Resubscription multiple contacts with batched support & route(%s) in %d", who
, timeout
);
4638 sipe_schedule_seconds(sipe_private
,
4642 sipe_subscribe_presence_single
,
4644 SIPE_DEBUG_INFO("Resubscription single contact with batched support(%s) in %d", who
, timeout
);
4646 g_free(action_name
);
4650 * Dispatcher for all incoming subscription information
4651 * whether it comes from NOTIFY, BENOTIFY requests or
4652 * piggy-backed to subscription's OK responce.
4654 * @param request whether initiated from BE/NOTIFY request or OK-response message.
4655 * @param benotify whether initiated from NOTIFY or BENOTIFY request.
4657 void process_incoming_notify(struct sipe_core_private
*sipe_private
,
4659 gboolean request
, gboolean benotify
)
4661 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
4662 const gchar
*content_type
= sipmsg_find_header(msg
, "Content-Type");
4663 const gchar
*event
= sipmsg_find_header(msg
, "Event");
4664 const gchar
*subscription_state
= sipmsg_find_header(msg
, "subscription-state");
4666 SIPE_DEBUG_INFO("process_incoming_notify: subscription_state: %s", subscription_state
? subscription_state
: "");
4668 /* implicit subscriptions */
4669 if (content_type
&& g_str_has_prefix(content_type
, "application/ms-imdn+xml")) {
4670 sipe_process_imdn(sipe_private
, msg
);
4674 /* for one off subscriptions (send with Expire: 0) */
4675 if (sipe_strcase_equal(event
, "vnd-microsoft-provisioning-v2"))
4677 sipe_process_provisioning_v2(sipe_private
, msg
);
4679 else if (sipe_strcase_equal(event
, "vnd-microsoft-provisioning"))
4681 sipe_process_provisioning(sipe_private
, msg
);
4683 else if (sipe_strcase_equal(event
, "presence"))
4685 sipe_process_presence(sipe_private
, msg
);
4687 else if (sipe_strcase_equal(event
, "registration-notify"))
4689 sipe_process_registration_notify(sipe_private
, msg
);
4692 if (!subscription_state
|| strstr(subscription_state
, "active"))
4694 if (sipe_strcase_equal(event
, "vnd-microsoft-roaming-contacts"))
4696 sipe_process_roaming_contacts(sipe_private
, msg
);
4698 else if (sipe_strcase_equal(event
, "vnd-microsoft-roaming-self"))
4700 sipe_process_roaming_self(sipe_private
, msg
);
4702 else if (sipe_strcase_equal(event
, "vnd-microsoft-roaming-ACL"))
4704 sipe_process_roaming_acl(sipe_private
, msg
);
4706 else if (sipe_strcase_equal(event
, "presence.wpending"))
4708 sipe_process_presence_wpending(sipe_private
, msg
);
4710 else if (sipe_strcase_equal(event
, "conference"))
4712 sipe_process_conference(sipe_private
, msg
);
4717 /* The server sends status 'terminated' */
4718 if (subscription_state
&& strstr(subscription_state
, "terminated") ) {
4719 gchar
*who
= parse_from(sipmsg_find_header(msg
, request
? "From" : "To"));
4720 gchar
*key
= sipe_utils_subscription_key(event
, who
);
4722 SIPE_DEBUG_INFO("process_incoming_notify: server says that subscription to %s was terminated.", who
);
4725 sipe_subscriptions_remove(sipe_private
, key
);
4729 if (!request
&& event
) {
4730 const gchar
*expires_header
= sipmsg_find_header(msg
, "Expires");
4731 int timeout
= expires_header
? strtol(expires_header
, NULL
, 10) : 0;
4732 SIPE_DEBUG_INFO("process_incoming_notify: subscription expires:%d", timeout
);
4735 /* 2 min ahead of expiration */
4736 timeout
= (timeout
- 120) > 120 ? (timeout
- 120) : timeout
;
4738 if (sipe_strcase_equal(event
, "presence.wpending") &&
4739 g_slist_find_custom(sip
->allow_events
, "presence.wpending", (GCompareFunc
)g_ascii_strcasecmp
))
4741 gchar
*action_name
= g_strdup_printf("<%s>", "presence.wpending");
4742 sipe_schedule_seconds(sipe_private
,
4746 sipe_subscribe_presence_wpending
,
4748 g_free(action_name
);
4750 else if (sipe_strcase_equal(event
, "presence") &&
4751 g_slist_find_custom(sip
->allow_events
, "presence", (GCompareFunc
)g_ascii_strcasecmp
))
4753 gchar
*who
= parse_from(sipmsg_find_header(msg
, "To"));
4754 gchar
*action_name
= sipe_utils_presence_key(who
);
4756 if (sip
->batched_support
) {
4757 sipe_process_presence_timeout(sipe_private
, msg
, who
, timeout
);
4760 sipe_schedule_seconds(sipe_private
,
4764 sipe_subscribe_presence_single
,
4766 SIPE_DEBUG_INFO("Resubscription single contact (%s) in %d", who
, timeout
);
4768 g_free(action_name
);
4774 /* The client responses on received a NOTIFY message */
4775 if (request
&& !benotify
)
4777 sip_transport_response(sipe_private
, msg
, 200, "OK", NULL
);
4782 * Whether user manually changed status or
4783 * it was changed automatically due to user
4784 * became inactive/active again
4787 sipe_is_user_state(struct sipe_core_private
*sipe_private
)
4789 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
4791 time_t now
= time(NULL
);
4793 SIPE_DEBUG_INFO("sipe_is_user_state: sip->idle_switch : %s", asctime(localtime(&(sip
->idle_switch
))));
4794 SIPE_DEBUG_INFO("sipe_is_user_state: now : %s", asctime(localtime(&now
)));
4796 res
= ((now
- SIPE_IDLE_SET_DELAY
* 2) >= sip
->idle_switch
);
4798 SIPE_DEBUG_INFO("sipe_is_user_state: res = %s", res
? "USER" : "MACHINE");
4803 send_presence_soap0(struct sipe_core_private
*sipe_private
,
4804 gboolean do_publish_calendar
,
4805 gboolean do_reset_status
)
4807 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
4808 struct sipe_calendar
* cal
= sip
->cal
;
4809 int availability
= 0;
4814 gchar
*res_note
= NULL
;
4815 gchar
*res_oof
= NULL
;
4816 const gchar
*note_pub
= NULL
;
4817 gchar
*states
= NULL
;
4818 gchar
*calendar_data
= NULL
;
4819 gchar
*epid
= get_epid(sipe_private
);
4820 time_t now
= time(NULL
);
4821 gchar
*since_time_str
= sipe_utils_time_to_str(now
);
4822 const gchar
*oof_note
= cal
? sipe_ews_get_oof_note(cal
) : NULL
;
4823 const char *user_input
;
4824 gboolean pub_oof
= cal
&& oof_note
&& (!sip
->note
|| cal
->updated
> sip
->note_since
);
4826 if (oof_note
&& sip
->note
) {
4827 SIPE_DEBUG_INFO("cal->oof_start : %s", asctime(localtime(&(cal
->oof_start
))));
4828 SIPE_DEBUG_INFO("sip->note_since : %s", asctime(localtime(&(sip
->note_since
))));
4831 SIPE_DEBUG_INFO("sip->note : %s", sip
->note
? sip
->note
: "");
4833 if (!sip
->initial_state_published
||
4836 g_free(sip
->status
);
4837 sip
->status
= g_strdup(SIPE_STATUS_ID_AVAILABLE
);
4840 sipe_get_act_avail_by_status_2005(sip
->status
, &activity
, &availability
);
4844 note_pub
= oof_note
;
4845 res_oof
= SIPE_SOAP_SET_PRESENCE_OOF_XML
;
4846 cal
->published
= TRUE
;
4847 } else if (sip
->note
) {
4848 if (sip
->is_oof_note
&& !oof_note
) { /* stale OOF note, as it's not present in cal already */
4851 sip
->is_oof_note
= FALSE
;
4852 sip
->note_since
= 0;
4854 note_pub
= sip
->note
;
4855 res_oof
= sip
->is_oof_note
? SIPE_SOAP_SET_PRESENCE_OOF_XML
: "";
4861 /* to protocol internal plain text format */
4862 tmp
= sipe_backend_markup_strip_html(note_pub
);
4863 res_note
= g_markup_printf_escaped(SIPE_SOAP_SET_PRESENCE_NOTE_XML
, tmp
);
4868 if (!do_reset_status
) {
4869 if (sipe_is_user_state(sipe_private
) && !do_publish_calendar
&& sip
->initial_state_published
)
4871 gchar
*activity_token
= NULL
;
4872 int avail_2007
= sipe_get_availability_by_status(sip
->status
, &activity_token
);
4874 states
= g_strdup_printf(SIPE_SOAP_SET_PRESENCE_STATES
,
4879 g_free(activity_token
);
4881 else /* preserve existing publication */
4883 if (sip
->user_states
) {
4884 states
= g_strdup(sip
->user_states
);
4888 /* do nothing - then User state will be erased */
4890 sip
->initial_state_published
= TRUE
;
4893 if (cal
&& (!is_empty(cal
->legacy_dn
) || !is_empty(cal
->email
)) && cal
->fb_start
&& !is_empty(cal
->free_busy
))
4895 char *fb_start_str
= sipe_utils_time_to_str(cal
->fb_start
);
4896 char *free_busy_base64
= sipe_cal_get_freebusy_base64(cal
->free_busy
);
4897 calendar_data
= g_strdup_printf(SIPE_SOAP_SET_PRESENCE_CALENDAR
,
4898 !is_empty(cal
->legacy_dn
) ? cal
->legacy_dn
: cal
->email
,
4901 g_free(fb_start_str
);
4902 g_free(free_busy_base64
);
4905 user_input
= (sipe_is_user_state(sipe_private
) ||
4906 sipe_strequal(sip
->status
, SIPE_STATUS_ID_AVAILABLE
)) ?
4909 /* forming resulting XML */
4910 body
= g_strdup_printf(SIPE_SOAP_SET_PRESENCE
,
4911 sipe_private
->username
,
4914 (tmp
= g_ascii_strup(g_get_host_name(), -1)),
4915 res_note
? res_note
: "",
4916 res_oof
? res_oof
: "",
4917 states
? states
: "",
4918 calendar_data
? calendar_data
: "",
4927 g_free(calendar_data
);
4929 send_soap_request(sipe_private
, body
);
4932 g_free(since_time_str
);
4937 send_presence_soap(struct sipe_core_private
*sipe_private
,
4938 gboolean do_publish_calendar
)
4940 return send_presence_soap0(sipe_private
, do_publish_calendar
, FALSE
);
4945 process_send_presence_category_publish_response(struct sipe_core_private
*sipe_private
,
4947 struct transaction
*trans
)
4949 const gchar
*contenttype
= sipmsg_find_header(msg
, "Content-Type");
4951 if (msg
->response
== 409 && g_str_has_prefix(contenttype
, "application/msrtc-fault+xml")) {
4953 const sipe_xml
*node
;
4957 gboolean has_device_publication
= FALSE
;
4959 xml
= sipe_xml_parse(msg
->body
, msg
->bodylen
);
4961 /* test if version mismatch fault */
4962 fault_code
= sipe_xml_data(sipe_xml_child(xml
, "Faultcode"));
4963 if (!sipe_strequal(fault_code
, "Client.BadCall.WrongDelta")) {
4964 SIPE_DEBUG_INFO("process_send_presence_category_publish_response: unsupported fault code:%s returning.", fault_code
);
4971 /* accumulating information about faulty versions */
4972 faults
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, g_free
);
4973 for (node
= sipe_xml_child(xml
, "details/operation");
4975 node
= sipe_xml_twin(node
))
4977 const gchar
*index
= sipe_xml_attribute(node
, "index");
4978 const gchar
*curVersion
= sipe_xml_attribute(node
, "curVersion");
4980 g_hash_table_insert(faults
, g_strdup(index
), g_strdup(curVersion
));
4981 SIPE_DEBUG_INFO("fault added: index:%s curVersion:%s", index
, curVersion
);
4985 /* here we are parsing our own request to figure out what publication
4986 * referenced here only by index went wrong
4988 xml
= sipe_xml_parse(trans
->msg
->body
, trans
->msg
->bodylen
);
4991 for (node
= sipe_xml_child(xml
, "publications/publication"),
4992 index_our
= 1; /* starts with 1 - our first publication */
4994 node
= sipe_xml_twin(node
), index_our
++)
4996 gchar
*idx
= g_strdup_printf("%d", index_our
);
4997 const gchar
*curVersion
= g_hash_table_lookup(faults
, idx
);
4998 const gchar
*categoryName
= sipe_xml_attribute(node
, "categoryName");
5001 if (sipe_strequal("device", categoryName
)) {
5002 has_device_publication
= TRUE
;
5005 if (curVersion
) { /* fault exist on this index */
5006 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
5007 const gchar
*container
= sipe_xml_attribute(node
, "container");
5008 const gchar
*instance
= sipe_xml_attribute(node
, "instance");
5009 /* key is <category><instance><container> */
5010 gchar
*key
= g_strdup_printf("<%s><%s><%s>", categoryName
, instance
, container
);
5011 GHashTable
*category
= g_hash_table_lookup(sip
->our_publications
, categoryName
);
5014 struct sipe_publication
*publication
=
5015 g_hash_table_lookup(category
, key
);
5017 SIPE_DEBUG_INFO("key is %s", key
);
5020 SIPE_DEBUG_INFO("Updating %s with version %s. Was %d before.",
5021 key
, curVersion
, publication
->version
);
5022 /* updating publication's version to the correct one */
5023 publication
->version
= atoi(curVersion
);
5026 /* We somehow lost this category from our publications... */
5027 struct sipe_publication
*publication
= g_new0(struct sipe_publication
, 1);
5028 publication
->category
= g_strdup(categoryName
);
5029 publication
->instance
= atoi(instance
);
5030 publication
->container
= atoi(container
);
5031 publication
->version
= atoi(curVersion
);
5032 category
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
5033 g_free
, (GDestroyNotify
)free_publication
);
5034 g_hash_table_insert(category
, g_strdup(key
), publication
);
5035 g_hash_table_insert(sip
->our_publications
, g_strdup(categoryName
), category
);
5036 SIPE_DEBUG_INFO("added lost category '%s' key '%s'", categoryName
, key
);
5042 g_hash_table_destroy(faults
);
5044 /* rebublishing with right versions */
5045 if (has_device_publication
) {
5046 send_publish_category_initial(sipe_private
);
5048 send_presence_status(sipe_private
, NULL
);
5055 * Returns 'device' XML part for publication.
5056 * Must be g_free'd after use.
5059 sipe_publish_get_category_device(struct sipe_core_private
*sipe_private
)
5061 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
5064 gchar
*epid
= get_epid(sipe_private
);
5065 gchar
*uuid
= generateUUIDfromEPID(epid
);
5066 guint device_instance
= sipe_get_pub_instance(sipe_private
, SIPE_PUB_DEVICE
);
5067 /* key is <category><instance><container> */
5068 gchar
*key
= g_strdup_printf("<%s><%u><%u>", "device", device_instance
, 2);
5069 struct sipe_publication
*publication
=
5070 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "device"), key
);
5075 uri
= sip_uri_self(sipe_private
);
5076 doc
= g_strdup_printf(SIPE_PUB_XML_DEVICE
,
5078 publication
? publication
->version
: 0,
5081 "00:00:00+01:00", /* @TODO make timezone real*/
5092 * A service method - use
5093 * - send_publish_get_category_state_machine and
5094 * - send_publish_get_category_state_user instead.
5095 * Must be g_free'd after use.
5098 sipe_publish_get_category_state(struct sipe_core_private
*sipe_private
,
5099 gboolean is_user_state
)
5101 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
5102 int availability
= sipe_get_availability_by_status(sip
->status
, NULL
);
5103 guint instance
= is_user_state
? sipe_get_pub_instance(sipe_private
, SIPE_PUB_STATE_USER
) :
5104 sipe_get_pub_instance(sipe_private
, SIPE_PUB_STATE_MACHINE
);
5105 /* key is <category><instance><container> */
5106 gchar
*key_2
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 2);
5107 gchar
*key_3
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 3);
5108 struct sipe_publication
*publication_2
=
5109 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "state"), key_2
);
5110 struct sipe_publication
*publication_3
=
5111 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "state"), key_3
);
5116 if (publication_2
&& (publication_2
->availability
== availability
))
5118 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_state: state has NOT changed. Exiting.");
5119 return NULL
; /* nothing to update */
5122 return g_strdup_printf( is_user_state
? SIPE_PUB_XML_STATE_USER
: SIPE_PUB_XML_STATE_MACHINE
,
5124 publication_2
? publication_2
->version
: 0,
5127 publication_3
? publication_3
->version
: 0,
5132 * Only Busy and OOF calendar event are published.
5133 * Different instances are used for that.
5135 * Must be g_free'd after use.
5138 sipe_publish_get_category_state_calendar(struct sipe_core_private
*sipe_private
,
5139 struct sipe_cal_event
*event
,
5143 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
5144 gchar
*start_time_str
;
5145 int availability
= 0;
5148 guint instance
= (cal_satus
== SIPE_CAL_OOF
) ?
5149 sipe_get_pub_instance(sipe_private
, SIPE_PUB_STATE_CALENDAR_OOF
) :
5150 sipe_get_pub_instance(sipe_private
, SIPE_PUB_STATE_CALENDAR
);
5152 /* key is <category><instance><container> */
5153 gchar
*key_2
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 2);
5154 gchar
*key_3
= g_strdup_printf("<%s><%u><%u>", "state", instance
, 3);
5155 struct sipe_publication
*publication_2
=
5156 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "state"), key_2
);
5157 struct sipe_publication
*publication_3
=
5158 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "state"), key_3
);
5163 if (!publication_3
&& !event
) { /* was nothing, have nothing, exiting */
5164 SIPE_DEBUG_INFO("sipe_publish_get_category_state_calendar: "
5165 "Exiting as no publication and no event for cal_satus:%d", cal_satus
);
5171 (publication_3
->availability
== availability
) &&
5172 sipe_strequal(publication_3
->cal_event_hash
, (tmp
= sipe_cal_event_hash(event
))))
5175 SIPE_DEBUG_INFO("sipe_publish_get_category_state_calendar: "
5176 "cal state has NOT changed for cal_satus:%d. Exiting.", cal_satus
);
5177 return NULL
; /* nothing to update */
5182 (event
->cal_status
== SIPE_CAL_BUSY
||
5183 event
->cal_status
== SIPE_CAL_OOF
))
5185 gchar
*availability_xml_str
= NULL
;
5186 gchar
*activity_xml_str
= NULL
;
5187 gchar
*escaped_subject
= event
->subject
? g_markup_escape_text(event
->subject
, -1) : NULL
;
5188 gchar
*escaped_location
= event
->location
? g_markup_escape_text(event
->location
, -1) : NULL
;
5190 if (event
->cal_status
== SIPE_CAL_BUSY
) {
5191 availability_xml_str
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_AVAIL
, 6500);
5194 if (event
->cal_status
== SIPE_CAL_BUSY
&& event
->is_meeting
) {
5195 activity_xml_str
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY
,
5196 sipe_activity_map
[SIPE_ACTIVITY_IN_MEETING
].token
,
5197 "minAvailability=\"6500\"",
5198 "maxAvailability=\"8999\"");
5199 } else if (event
->cal_status
== SIPE_CAL_OOF
) {
5200 activity_xml_str
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY
,
5201 sipe_activity_map
[SIPE_ACTIVITY_OOF
].token
,
5202 "minAvailability=\"12000\"",
5205 start_time_str
= sipe_utils_time_to_str(event
->start_time
);
5207 res
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR
,
5209 publication_2
? publication_2
->version
: 0,
5212 availability_xml_str
? availability_xml_str
: "",
5213 activity_xml_str
? activity_xml_str
: "",
5214 escaped_subject
? escaped_subject
: "",
5215 escaped_location
? escaped_location
: "",
5218 publication_3
? publication_3
->version
: 0,
5221 availability_xml_str
? availability_xml_str
: "",
5222 activity_xml_str
? activity_xml_str
: "",
5223 escaped_subject
? escaped_subject
: "",
5224 escaped_location
? escaped_location
: ""
5226 g_free(escaped_location
);
5227 g_free(escaped_subject
);
5228 g_free(start_time_str
);
5229 g_free(availability_xml_str
);
5230 g_free(activity_xml_str
);
5233 else /* including !event, SIPE_CAL_FREE, SIPE_CAL_TENTATIVE */
5235 res
= g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_CLEAR
,
5237 publication_2
? publication_2
->version
: 0,
5240 publication_3
? publication_3
->version
: 0
5248 * Returns 'machineState' XML part for publication.
5249 * Must be g_free'd after use.
5252 sipe_publish_get_category_state_machine(struct sipe_core_private
*sipe_private
)
5254 return sipe_publish_get_category_state(sipe_private
, FALSE
);
5258 * Returns 'userState' XML part for publication.
5259 * Must be g_free'd after use.
5262 sipe_publish_get_category_state_user(struct sipe_core_private
*sipe_private
)
5264 return sipe_publish_get_category_state(sipe_private
, TRUE
);
5268 * Returns 'note' XML part for publication.
5269 * Must be g_free'd after use.
5271 * Protocol format for Note is plain text.
5273 * @param note a note in Sipe internal HTML format
5274 * @param note_type either personal or OOF
5277 sipe_publish_get_category_note(struct sipe_core_private
*sipe_private
,
5278 const char *note
, /* html */
5279 const char *note_type
,
5283 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
5284 guint instance
= sipe_strequal("OOF", note_type
) ? sipe_get_pub_instance(sipe_private
, SIPE_PUB_NOTE_OOF
) : 0;
5285 /* key is <category><instance><container> */
5286 gchar
*key_note_200
= g_strdup_printf("<%s><%u><%u>", "note", instance
, 200);
5287 gchar
*key_note_300
= g_strdup_printf("<%s><%u><%u>", "note", instance
, 300);
5288 gchar
*key_note_400
= g_strdup_printf("<%s><%u><%u>", "note", instance
, 400);
5290 struct sipe_publication
*publication_note_200
=
5291 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "note"), key_note_200
);
5292 struct sipe_publication
*publication_note_300
=
5293 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "note"), key_note_300
);
5294 struct sipe_publication
*publication_note_400
=
5295 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "note"), key_note_400
);
5297 char *tmp
= note
? sipe_backend_markup_strip_html(note
) : NULL
;
5298 char *n1
= tmp
? g_markup_escape_text(tmp
, -1) : NULL
;
5299 const char *n2
= publication_note_200
? publication_note_200
->note
: NULL
;
5300 char *res
, *tmp1
, *tmp2
, *tmp3
;
5301 char *start_time_attr
;
5302 char *end_time_attr
;
5306 g_free(key_note_200
);
5307 g_free(key_note_300
);
5308 g_free(key_note_400
);
5310 /* we even need to republish empty note */
5311 if (sipe_strequal(n1
, n2
))
5313 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_note: note has NOT changed. Exiting.");
5315 return NULL
; /* nothing to update */
5318 start_time_attr
= note_start
? g_strdup_printf(" startTime=\"%s\"", (tmp
= sipe_utils_time_to_str(note_start
))) : NULL
;
5321 end_time_attr
= note_end
? g_strdup_printf(" endTime=\"%s\"", (tmp
= sipe_utils_time_to_str(note_end
))) : NULL
;
5325 tmp1
= g_strdup_printf(SIPE_PUB_XML_NOTE
,
5328 publication_note_200
? publication_note_200
->version
: 0,
5330 start_time_attr
? start_time_attr
: "",
5331 end_time_attr
? end_time_attr
: "",
5334 tmp2
= g_strdup_printf(SIPE_PUB_XML_NOTE
,
5337 publication_note_300
? publication_note_300
->version
: 0,
5339 start_time_attr
? start_time_attr
: "",
5340 end_time_attr
? end_time_attr
: "",
5343 tmp3
= g_strdup_printf(SIPE_PUB_XML_NOTE
,
5346 publication_note_400
? publication_note_400
->version
: 0,
5348 start_time_attr
? start_time_attr
: "",
5349 end_time_attr
? end_time_attr
: "",
5352 tmp1
= g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR
,
5356 publication_note_200
? publication_note_200
->version
: 0,
5358 tmp2
= g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR
,
5362 publication_note_200
? publication_note_200
->version
: 0,
5364 tmp3
= g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR
,
5368 publication_note_200
? publication_note_200
->version
: 0,
5371 res
= g_strconcat(tmp1
, tmp2
, tmp3
, NULL
);
5373 g_free(start_time_attr
);
5374 g_free(end_time_attr
);
5384 * Returns 'calendarData' XML part with WorkingHours for publication.
5385 * Must be g_free'd after use.
5388 sipe_publish_get_category_cal_working_hours(struct sipe_core_private
*sipe_private
)
5390 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
5391 struct sipe_calendar
* cal
= sip
->cal
;
5393 /* key is <category><instance><container> */
5394 gchar
*key_cal_1
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 1);
5395 gchar
*key_cal_100
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 100);
5396 gchar
*key_cal_200
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 200);
5397 gchar
*key_cal_300
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 300);
5398 gchar
*key_cal_400
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 400);
5399 gchar
*key_cal_32000
= g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 32000);
5401 struct sipe_publication
*publication_cal_1
=
5402 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_1
);
5403 struct sipe_publication
*publication_cal_100
=
5404 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_100
);
5405 struct sipe_publication
*publication_cal_200
=
5406 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_200
);
5407 struct sipe_publication
*publication_cal_300
=
5408 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_300
);
5409 struct sipe_publication
*publication_cal_400
=
5410 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_400
);
5411 struct sipe_publication
*publication_cal_32000
=
5412 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_32000
);
5414 const char *n1
= cal
? cal
->working_hours_xml_str
: NULL
;
5415 const char *n2
= publication_cal_300
? publication_cal_300
->working_hours_xml_str
: NULL
;
5418 g_free(key_cal_100
);
5419 g_free(key_cal_200
);
5420 g_free(key_cal_300
);
5421 g_free(key_cal_400
);
5422 g_free(key_cal_32000
);
5424 if (!cal
|| is_empty(cal
->email
) || is_empty(cal
->working_hours_xml_str
)) {
5425 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_working_hours: no data to publish, exiting");
5429 if (sipe_strequal(n1
, n2
))
5431 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_working_hours: WorkingHours has NOT changed. Exiting.");
5432 return NULL
; /* nothing to update */
5435 return g_strdup_printf(SIPE_PUB_XML_WORKING_HOURS
,
5437 publication_cal_1
? publication_cal_1
->version
: 0,
5439 cal
->working_hours_xml_str
,
5441 publication_cal_100
? publication_cal_100
->version
: 0,
5443 publication_cal_200
? publication_cal_200
->version
: 0,
5445 cal
->working_hours_xml_str
,
5447 publication_cal_300
? publication_cal_300
->version
: 0,
5449 cal
->working_hours_xml_str
,
5450 /* 400 - Personal */
5451 publication_cal_400
? publication_cal_400
->version
: 0,
5453 cal
->working_hours_xml_str
,
5454 /* 32000 - Blocked */
5455 publication_cal_32000
? publication_cal_32000
->version
: 0
5460 * Returns 'calendarData' XML part with FreeBusy for publication.
5461 * Must be g_free'd after use.
5464 sipe_publish_get_category_cal_free_busy(struct sipe_core_private
*sipe_private
)
5466 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
5467 struct sipe_calendar
* cal
= sip
->cal
;
5468 guint cal_data_instance
= sipe_get_pub_instance(sipe_private
, SIPE_PUB_CALENDAR_DATA
);
5470 char *free_busy_base64
;
5475 /* key is <category><instance><container> */
5476 gchar
*key_cal_1
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 1);
5477 gchar
*key_cal_100
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 100);
5478 gchar
*key_cal_200
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 200);
5479 gchar
*key_cal_300
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 300);
5480 gchar
*key_cal_400
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 400);
5481 gchar
*key_cal_32000
= g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance
, 32000);
5483 struct sipe_publication
*publication_cal_1
=
5484 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_1
);
5485 struct sipe_publication
*publication_cal_100
=
5486 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_100
);
5487 struct sipe_publication
*publication_cal_200
=
5488 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_200
);
5489 struct sipe_publication
*publication_cal_300
=
5490 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_300
);
5491 struct sipe_publication
*publication_cal_400
=
5492 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_400
);
5493 struct sipe_publication
*publication_cal_32000
=
5494 g_hash_table_lookup(g_hash_table_lookup(sip
->our_publications
, "calendarData"), key_cal_32000
);
5497 g_free(key_cal_100
);
5498 g_free(key_cal_200
);
5499 g_free(key_cal_300
);
5500 g_free(key_cal_400
);
5501 g_free(key_cal_32000
);
5503 if (!cal
|| is_empty(cal
->email
) || !cal
->fb_start
|| is_empty(cal
->free_busy
)) {
5504 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_free_busy: no data to publish, exiting");
5508 fb_start_str
= sipe_utils_time_to_str(cal
->fb_start
);
5509 free_busy_base64
= sipe_cal_get_freebusy_base64(cal
->free_busy
);
5511 st
= publication_cal_300
? publication_cal_300
->fb_start_str
: NULL
;
5512 fb
= publication_cal_300
? publication_cal_300
->free_busy_base64
: NULL
;
5514 /* we will rebuplish the same data to refresh publication time,
5515 * so if data from multiple sources, most recent will be choosen
5517 //if (sipe_strequal(st, fb_start_str) && sipe_strequal(fb, free_busy_base64))
5519 // SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_free_busy: FreeBusy has NOT changed. Exiting.");
5520 // g_free(fb_start_str);
5521 // g_free(free_busy_base64);
5522 // return NULL; /* nothing to update */
5525 res
= g_strdup_printf(SIPE_PUB_XML_FREE_BUSY
,
5528 publication_cal_1
? publication_cal_1
->version
: 0,
5531 publication_cal_100
? publication_cal_100
->version
: 0,
5534 publication_cal_200
? publication_cal_200
->version
: 0,
5540 publication_cal_300
? publication_cal_300
->version
: 0,
5544 /* 400 - Personal */
5546 publication_cal_400
? publication_cal_400
->version
: 0,
5550 /* 32000 - Blocked */
5552 publication_cal_32000
? publication_cal_32000
->version
: 0
5555 g_free(fb_start_str
);
5556 g_free(free_busy_base64
);
5560 static void send_presence_publish(struct sipe_core_private
*sipe_private
,
5561 const char *publications
)
5568 uri
= sip_uri_self(sipe_private
);
5569 doc
= g_strdup_printf(SIPE_SEND_PRESENCE
,
5573 tmp
= get_contact(sipe_private
);
5574 hdr
= g_strdup_printf("Contact: %s\r\n"
5575 "Content-Type: application/msrtc-category-publish+xml\r\n", tmp
);
5577 sip_transport_service(sipe_private
,
5581 process_send_presence_category_publish_response
);
5590 send_publish_category_initial(struct sipe_core_private
*sipe_private
)
5592 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
5593 gchar
*pub_device
= sipe_publish_get_category_device(sipe_private
);
5595 gchar
*publications
;
5597 g_free(sip
->status
);
5598 sip
->status
= g_strdup(SIPE_STATUS_ID_AVAILABLE
); /* our initial state */
5600 pub_machine
= sipe_publish_get_category_state_machine(sipe_private
);
5601 publications
= g_strdup_printf("%s%s",
5603 pub_machine
? pub_machine
: "");
5605 g_free(pub_machine
);
5607 send_presence_publish(sipe_private
, publications
);
5608 g_free(publications
);
5612 send_presence_category_publish(struct sipe_core_private
*sipe_private
)
5614 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
5615 gchar
*pub_state
= sipe_is_user_state(sipe_private
) ?
5616 sipe_publish_get_category_state_user(sipe_private
) :
5617 sipe_publish_get_category_state_machine(sipe_private
);
5618 gchar
*pub_note
= sipe_publish_get_category_note(sipe_private
,
5620 sip
->is_oof_note
? "OOF" : "personal",
5623 gchar
*publications
;
5625 if (!pub_state
&& !pub_note
) {
5626 SIPE_DEBUG_INFO_NOFORMAT("send_presence_category_publish: nothing has changed. Exiting.");
5630 publications
= g_strdup_printf("%s%s",
5631 pub_state
? pub_state
: "",
5632 pub_note
? pub_note
: "");
5637 send_presence_publish(sipe_private
, publications
);
5638 g_free(publications
);
5642 * Publishes self status
5643 * based on own calendar information.
5648 publish_calendar_status_self(struct sipe_core_private
*sipe_private
,
5649 SIPE_UNUSED_PARAMETER
void *unused
)
5651 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
5652 struct sipe_cal_event
* event
= NULL
;
5653 gchar
*pub_cal_working_hours
= NULL
;
5654 gchar
*pub_cal_free_busy
= NULL
;
5655 gchar
*pub_calendar
= NULL
;
5656 gchar
*pub_calendar2
= NULL
;
5657 gchar
*pub_oof_note
= NULL
;
5658 const gchar
*oof_note
;
5659 time_t oof_start
= 0;
5663 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self() no calendar data.");
5667 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self() started.");
5668 if (sip
->cal
->cal_events
) {
5669 event
= sipe_cal_get_event(sip
->cal
->cal_events
, time(NULL
));
5673 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self: current event is NULL");
5675 char *desc
= sipe_cal_event_describe(event
);
5676 SIPE_DEBUG_INFO("publish_calendar_status_self: current event is:\n%s", desc
? desc
: "");
5682 OOF publish, Busy clean
5684 OOF clean, Busy publish
5686 OOF clean, Busy clean
5688 if (event
&& event
->cal_status
== SIPE_CAL_OOF
) {
5689 pub_calendar
= sipe_publish_get_category_state_calendar(sipe_private
, event
, sip
->cal
->email
, SIPE_CAL_OOF
);
5690 pub_calendar2
= sipe_publish_get_category_state_calendar(sipe_private
, NULL
, sip
->cal
->email
, SIPE_CAL_BUSY
);
5691 } else if (event
&& event
->cal_status
== SIPE_CAL_BUSY
) {
5692 pub_calendar
= sipe_publish_get_category_state_calendar(sipe_private
, NULL
, sip
->cal
->email
, SIPE_CAL_OOF
);
5693 pub_calendar2
= sipe_publish_get_category_state_calendar(sipe_private
, event
, sip
->cal
->email
, SIPE_CAL_BUSY
);
5695 pub_calendar
= sipe_publish_get_category_state_calendar(sipe_private
, NULL
, sip
->cal
->email
, SIPE_CAL_OOF
);
5696 pub_calendar2
= sipe_publish_get_category_state_calendar(sipe_private
, NULL
, sip
->cal
->email
, SIPE_CAL_BUSY
);
5699 oof_note
= sipe_ews_get_oof_note(sip
->cal
);
5700 if (sipe_strequal("Scheduled", sip
->cal
->oof_state
)) {
5701 oof_start
= sip
->cal
->oof_start
;
5702 oof_end
= sip
->cal
->oof_end
;
5704 pub_oof_note
= sipe_publish_get_category_note(sipe_private
, oof_note
, "OOF", oof_start
, oof_end
);
5706 pub_cal_working_hours
= sipe_publish_get_category_cal_working_hours(sipe_private
);
5707 pub_cal_free_busy
= sipe_publish_get_category_cal_free_busy(sipe_private
);
5709 if (!pub_cal_working_hours
&& !pub_cal_free_busy
&& !pub_calendar
&& !pub_calendar2
&& !pub_oof_note
) {
5710 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self: nothing has changed.");
5712 gchar
*publications
= g_strdup_printf("%s%s%s%s%s",
5713 pub_cal_working_hours
? pub_cal_working_hours
: "",
5714 pub_cal_free_busy
? pub_cal_free_busy
: "",
5715 pub_calendar
? pub_calendar
: "",
5716 pub_calendar2
? pub_calendar2
: "",
5717 pub_oof_note
? pub_oof_note
: "");
5719 send_presence_publish(sipe_private
, publications
);
5720 g_free(publications
);
5723 g_free(pub_cal_working_hours
);
5724 g_free(pub_cal_free_busy
);
5725 g_free(pub_calendar
);
5726 g_free(pub_calendar2
);
5727 g_free(pub_oof_note
);
5729 /* repeat scheduling */
5730 sipe_sched_calendar_status_self_publish(sipe_private
, time(NULL
));
5733 static void send_presence_status(struct sipe_core_private
*sipe_private
,
5734 SIPE_UNUSED_PARAMETER
void *unused
)
5736 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
5737 PurpleStatus
* status
= purple_account_get_active_status(sip
->account
);
5739 if (!status
) return;
5741 SIPE_DEBUG_INFO("send_presence_status: status: %s (%s)",
5742 purple_status_get_id(status
) ? purple_status_get_id(status
) : "",
5743 sipe_is_user_state(sipe_private
) ? "USER" : "MACHINE");
5745 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
5746 send_presence_category_publish(sipe_private
);
5748 send_presence_soap(sipe_private
, FALSE
);
5752 static guint
sipe_ht_hash_nick(const char *nick
)
5754 char *lc
= g_utf8_strdown(nick
, -1);
5755 guint bucket
= g_str_hash(lc
);
5761 static gboolean
sipe_ht_equals_nick(const char *nick1
, const char *nick2
)
5763 char *nick1_norm
= NULL
;
5764 char *nick2_norm
= NULL
;
5767 if (nick1
== NULL
&& nick2
== NULL
) return TRUE
;
5768 if (nick1
== NULL
|| nick2
== NULL
||
5769 !g_utf8_validate(nick1
, -1, NULL
) ||
5770 !g_utf8_validate(nick2
, -1, NULL
)) return FALSE
;
5772 nick1_norm
= g_utf8_casefold(nick1
, -1);
5773 nick2_norm
= g_utf8_casefold(nick2
, -1);
5774 equal
= g_utf8_collate(nick1_norm
, nick2_norm
) == 0;
5781 /* temporary function */
5782 void sipe_purple_setup(struct sipe_core_public
*sipe_public
,
5783 PurpleConnection
*gc
)
5785 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA
;
5787 sip
->account
= purple_connection_get_account(gc
);
5790 struct sipe_core_public
*sipe_core_allocate(const gchar
*signin_name
,
5791 const gchar
*login_domain
,
5792 const gchar
*login_account
,
5793 const gchar
*password
,
5795 const gchar
*email_url
,
5796 const gchar
**errmsg
)
5798 struct sipe_core_private
*sipe_private
;
5799 struct sipe_account_data
*sip
;
5800 gchar
**user_domain
;
5802 SIPE_DEBUG_INFO("sipe_core_allocate: signin_name '%s'", signin_name
);
5804 /* ensure that sign-in name doesn't contain invalid characters */
5805 if (strpbrk(signin_name
, "\t\v\r\n") != NULL
) {
5806 *errmsg
= _("SIP Exchange user name contains invalid characters");
5810 /* ensure that sign-in name format is name@domain */
5811 if (!strchr(signin_name
, '@') ||
5812 g_str_has_prefix(signin_name
, "@") ||
5813 g_str_has_suffix(signin_name
, "@")) {
5814 *errmsg
= _("User name should be a valid SIP URI\nExample: user@company.com");
5818 /* ensure that email format is name@domain (if provided) */
5819 if (!is_empty(email
) &&
5820 (!strchr(email
, '@') ||
5821 g_str_has_prefix(email
, "@") ||
5822 g_str_has_suffix(email
, "@")))
5824 *errmsg
= _("Email address should be valid if provided\nExample: user@company.com");
5828 /* ensure that user name doesn't contain spaces */
5829 user_domain
= g_strsplit(signin_name
, "@", 2);
5830 SIPE_DEBUG_INFO("sipe_core_allocate: user '%s' domain '%s'", user_domain
[0], user_domain
[1]);
5831 if (strchr(user_domain
[0], ' ') != NULL
) {
5832 g_strfreev(user_domain
);
5833 *errmsg
= _("SIP Exchange user name contains whitespace");
5837 /* ensure that email_url is in proper format if enabled (if provided).
5838 * Example (Exchange): https://server.company.com/EWS/Exchange.asmx
5839 * Example (Domino) : https://[domino_server]/[mail_database_name].nsf
5841 if (!is_empty(email_url
)) {
5842 char *tmp
= g_ascii_strdown(email_url
, -1);
5843 if (!g_str_has_prefix(tmp
, "https://"))
5846 g_strfreev(user_domain
);
5847 *errmsg
= _("Email services URL should be valid if provided\n"
5848 "Example: https://exchange.corp.com/EWS/Exchange.asmx\n"
5849 "Example: https://domino.corp.com/maildatabase.nsf");
5855 sipe_private
= g_new0(struct sipe_core_private
, 1);
5856 sipe_private
->temporary
= sip
= g_new0(struct sipe_account_data
, 1);
5857 sip
->subscribed_buddies
= FALSE
;
5858 sip
->initial_state_published
= FALSE
;
5859 sipe_private
->username
= g_strdup(signin_name
);
5860 sip
->email
= is_empty(email
) ? g_strdup(signin_name
) : g_strdup(email
);
5861 sip
->authdomain
= is_empty(login_domain
) ? NULL
: g_strdup(login_domain
);
5862 sip
->authuser
= is_empty(login_account
) ? NULL
: g_strdup(login_account
);
5863 sip
->password
= g_strdup(password
);
5864 sipe_private
->public.sip_name
= g_strdup(user_domain
[0]);
5865 sipe_private
->public.sip_domain
= g_strdup(user_domain
[1]);
5866 g_strfreev(user_domain
);
5868 sipe_private
->buddies
= g_hash_table_new((GHashFunc
)sipe_ht_hash_nick
, (GEqualFunc
)sipe_ht_equals_nick
);
5869 sip
->our_publications
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
5870 g_free
, (GDestroyNotify
)g_hash_table_destroy
);
5871 sipe_subscriptions_init(sipe_private
);
5872 sip
->status
= g_strdup(SIPE_STATUS_ID_UNKNOWN
);
5874 return((struct sipe_core_public
*)sipe_private
);
5878 sipe_blist_menu_free_containers(struct sipe_core_private
*sipe_private
);
5880 void sipe_connection_cleanup(struct sipe_core_private
*sipe_private
)
5882 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
5884 g_free(sipe_private
->epid
);
5885 sipe_private
->epid
= NULL
;
5887 sip_transport_disconnect(sipe_private
);
5889 sipe_schedule_cancel_all(sipe_private
);
5891 if (sip
->allow_events
) {
5892 GSList
*entry
= sip
->allow_events
;
5894 g_free(entry
->data
);
5895 entry
= entry
->next
;
5898 g_slist_free(sip
->allow_events
);
5900 if (sip
->containers
) {
5901 GSList
*entry
= sip
->containers
;
5903 free_container((struct sipe_container
*)entry
->data
);
5904 entry
= entry
->next
;
5907 g_slist_free(sip
->containers
);
5909 /* libpurple memory leak workaround */
5910 sipe_blist_menu_free_containers(sipe_private
);
5912 if (sipe_private
->contact
)
5913 g_free(sipe_private
->contact
);
5914 sipe_private
->contact
= NULL
;
5916 g_free(sip
->regcallid
);
5917 sip
->regcallid
= NULL
;
5919 if (sipe_private
->focus_factory_uri
)
5920 g_free(sipe_private
->focus_factory_uri
);
5921 sipe_private
->focus_factory_uri
= NULL
;
5924 sipe_cal_calendar_free(sip
->cal
);
5928 sipe_groupchat_free(sipe_private
);
5932 * A callback for g_hash_table_foreach_remove
5934 static gboolean
sipe_buddy_remove(SIPE_UNUSED_PARAMETER gpointer key
, gpointer buddy
,
5935 SIPE_UNUSED_PARAMETER gpointer user_data
)
5937 sipe_free_buddy((struct sipe_buddy
*) buddy
);
5939 /* We must return TRUE as the key/value have already been deleted */
5943 void sipe_buddy_free_all(struct sipe_core_private
*sipe_private
)
5945 g_hash_table_foreach_steal(sipe_private
->buddies
, sipe_buddy_remove
, NULL
);
5948 static void sipe_searchresults_im_buddy(PurpleConnection
*gc
, GList
*row
,
5949 SIPE_UNUSED_PARAMETER
void *user_data
)
5951 PurpleAccount
*acct
= purple_connection_get_account(gc
);
5952 char *id
= sip_uri_from_name((gchar
*)g_list_nth_data(row
, 0));
5953 PurpleConversation
*conv
= purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM
, id
, acct
);
5955 conv
= purple_conversation_new(PURPLE_CONV_TYPE_IM
, acct
, id
);
5956 purple_conversation_present(conv
);
5960 static void sipe_searchresults_add_buddy(PurpleConnection
*gc
, GList
*row
,
5961 SIPE_UNUSED_PARAMETER
void *user_data
)
5964 purple_blist_request_add_buddy(purple_connection_get_account(gc
),
5965 g_list_nth_data(row
, 0), _("Other Contacts"), g_list_nth_data(row
, 1));
5968 static gboolean
process_search_contact_response(struct sipe_core_private
*sipe_private
,
5970 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
5972 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
5973 PurpleNotifySearchResults
*results
;
5974 PurpleNotifySearchColumn
*column
;
5975 sipe_xml
*searchResults
;
5976 const sipe_xml
*mrow
;
5977 int match_count
= 0;
5978 gboolean more
= FALSE
;
5981 SIPE_DEBUG_INFO("process_search_contact_response: body:\n%s", msg
->body
? msg
->body
: "");
5983 searchResults
= sipe_xml_parse(msg
->body
, msg
->bodylen
);
5984 if (!searchResults
) {
5985 SIPE_DEBUG_INFO_NOFORMAT("process_search_contact_response: no parseable searchResults");
5989 results
= purple_notify_searchresults_new();
5991 if (results
== NULL
) {
5992 SIPE_DEBUG_ERROR_NOFORMAT("purple_parse_searchreply: Unable to display the search results.");
5993 purple_notify_error(sip
->gc
, NULL
, _("Unable to display the search results"), NULL
);
5995 sipe_xml_free(searchResults
);
5999 column
= purple_notify_searchresults_column_new(_("User name"));
6000 purple_notify_searchresults_column_add(results
, column
);
6002 column
= purple_notify_searchresults_column_new(_("Name"));
6003 purple_notify_searchresults_column_add(results
, column
);
6005 column
= purple_notify_searchresults_column_new(_("Company"));
6006 purple_notify_searchresults_column_add(results
, column
);
6008 column
= purple_notify_searchresults_column_new(_("Country"));
6009 purple_notify_searchresults_column_add(results
, column
);
6011 column
= purple_notify_searchresults_column_new(_("Email"));
6012 purple_notify_searchresults_column_add(results
, column
);
6014 for (mrow
= sipe_xml_child(searchResults
, "Body/Array/row"); mrow
; mrow
= sipe_xml_twin(mrow
)) {
6017 gchar
**uri_parts
= g_strsplit(sipe_xml_attribute(mrow
, "uri"), ":", 2);
6018 row
= g_list_append(row
, g_strdup(uri_parts
[1]));
6019 g_strfreev(uri_parts
);
6021 row
= g_list_append(row
, g_strdup(sipe_xml_attribute(mrow
, "displayName")));
6022 row
= g_list_append(row
, g_strdup(sipe_xml_attribute(mrow
, "company")));
6023 row
= g_list_append(row
, g_strdup(sipe_xml_attribute(mrow
, "country")));
6024 row
= g_list_append(row
, g_strdup(sipe_xml_attribute(mrow
, "email")));
6026 purple_notify_searchresults_row_add(results
, row
);
6030 if ((mrow
= sipe_xml_child(searchResults
, "Body/directorySearch/moreAvailable")) != NULL
) {
6031 char *data
= sipe_xml_data(mrow
);
6032 more
= (g_strcasecmp(data
, "true") == 0);
6036 secondary
= g_strdup_printf(
6037 dngettext(PACKAGE_NAME
,
6038 "Found %d contact%s:",
6039 "Found %d contacts%s:", match_count
),
6040 match_count
, more
? _(" (more matched your query)") : "");
6042 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_IM
, sipe_searchresults_im_buddy
);
6043 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_ADD
, sipe_searchresults_add_buddy
);
6044 purple_notify_searchresults(sip
->gc
, NULL
, NULL
, secondary
, results
, NULL
, NULL
);
6047 sipe_xml_free(searchResults
);
6051 void sipe_search_contact_with_cb(PurpleConnection
*gc
, PurpleRequestFields
*fields
)
6053 GList
*entries
= purple_request_field_group_get_fields(purple_request_fields_get_groups(fields
)->data
);
6054 gchar
**attrs
= g_new(gchar
*, g_list_length(entries
) + 1);
6060 PurpleRequestField
*field
= entries
->data
;
6061 const char *id
= purple_request_field_get_id(field
);
6062 const char *value
= purple_request_field_string_get_value(field
);
6064 SIPE_DEBUG_INFO("sipe_search_contact_with_cb: %s = '%s'", id
, value
? value
: "");
6066 if (value
!= NULL
) attrs
[i
++] = g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW
, id
, value
);
6067 } while ((entries
= g_list_next(entries
)) != NULL
);
6071 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
6072 gchar
*domain_uri
= sip_uri_from_name(sipe_private
->public.sip_domain
);
6073 gchar
*query
= g_strjoinv(NULL
, attrs
);
6074 gchar
*body
= g_strdup_printf(SIPE_SOAP_SEARCH_CONTACT
, 100, query
);
6075 SIPE_DEBUG_INFO("sipe_search_contact_with_cb: body:\n%s", body
? body
: "");
6076 send_soap_request_with_cb(sipe_private
, domain_uri
, body
,
6077 process_search_contact_response
, NULL
);
6086 static void sipe_publish_get_cat_state_user_to_clear(SIPE_UNUSED_PARAMETER
const char *name
,
6090 struct sipe_publication
*publication
= value
;
6092 g_string_append_printf( str
,
6093 SIPE_PUB_XML_PUBLICATION_CLEAR
,
6094 publication
->category
,
6095 publication
->instance
,
6096 publication
->container
,
6097 publication
->version
,
6101 void sipe_core_reset_status(struct sipe_core_public
*sipe_public
)
6103 struct sipe_core_private
*sipe_private
= SIPE_CORE_PRIVATE
;
6104 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA
;
6105 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) /* 2007+ */
6107 GString
* str
= g_string_new(NULL
);
6108 gchar
*publications
;
6110 if (!sip
->user_state_publications
|| g_hash_table_size(sip
->user_state_publications
) == 0) {
6111 SIPE_DEBUG_INFO_NOFORMAT("sipe_reset_status: no userState publications, exiting.");
6115 g_hash_table_foreach(sip
->user_state_publications
, (GHFunc
)sipe_publish_get_cat_state_user_to_clear
, str
);
6116 publications
= g_string_free(str
, FALSE
);
6118 send_presence_publish(sipe_private
, publications
);
6119 g_free(publications
);
6123 send_presence_soap0(sipe_private
, FALSE
, TRUE
);
6127 /** for Access levels menu */
6128 #define INDENT_FMT " %s"
6130 /** Member is directly placed to access level container.
6131 * For example SIP URI of user is in the container.
6133 #define INDENT_MARKED_FMT "* %s"
6135 /** Member is indirectly belong to access level container.
6136 * For example 'sameEnterprise' is in the container and user
6137 * belongs to that same enterprise.
6139 #define INDENT_MARKED_INHERITED_FMT "= %s"
6141 GSList
*sipe_core_buddy_info(struct sipe_core_public
*sipe_public
,
6143 const gchar
*status_name
,
6146 struct sipe_core_private
*sipe_private
= SIPE_CORE_PRIVATE
;
6148 gboolean is_oof_note
= FALSE
;
6149 const gchar
*activity
= NULL
;
6150 gchar
*calendar
= NULL
;
6151 const gchar
*meeting_subject
= NULL
;
6152 const gchar
*meeting_location
= NULL
;
6153 gchar
*access_text
= NULL
;
6154 GSList
*info
= NULL
;
6156 #define SIPE_ADD_BUDDY_INFO_COMMON(l, t) \
6158 struct sipe_buddy_info *sbi = g_malloc(sizeof(struct sipe_buddy_info)); \
6161 info = g_slist_append(info, sbi); \
6163 #define SIPE_ADD_BUDDY_INFO(l, t) SIPE_ADD_BUDDY_INFO_COMMON((l), g_markup_escape_text((t), -1))
6164 #define SIPE_ADD_BUDDY_INFO_NOESCAPE(l, t) SIPE_ADD_BUDDY_INFO_COMMON((l), (t))
6166 if (sipe_public
) { //happens on pidgin exit
6167 struct sipe_buddy
*sbuddy
= g_hash_table_lookup(sipe_private
->buddies
, name
);
6169 note
= sbuddy
->note
;
6170 is_oof_note
= sbuddy
->is_oof_note
;
6171 activity
= sbuddy
->activity
;
6172 calendar
= sipe_cal_get_description(sbuddy
);
6173 meeting_subject
= sbuddy
->meeting_subject
;
6174 meeting_location
= sbuddy
->meeting_location
;
6176 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
6177 gboolean is_group_access
= FALSE
;
6178 const int container_id
= sipe_find_access_level(sipe_private
, "user", sipe_get_no_sip_uri(name
), &is_group_access
);
6179 const char *access_level
= sipe_get_access_level_name(container_id
);
6180 access_text
= is_group_access
?
6181 g_strdup(access_level
) :
6182 g_strdup_printf(INDENT_MARKED_FMT
, access_level
);
6189 const gchar
*status_str
= activity
? activity
: status_name
;
6191 SIPE_ADD_BUDDY_INFO(_("Status"), status_str
);
6193 if (is_online
&& !is_empty(calendar
))
6195 SIPE_ADD_BUDDY_INFO(_("Calendar"), calendar
);
6198 if (!is_empty(meeting_location
))
6200 SIPE_DEBUG_INFO("sipe_tooltip_text: %s meeting location: '%s'", name
, meeting_location
);
6201 SIPE_ADD_BUDDY_INFO(_("Meeting in"), meeting_location
);
6203 if (!is_empty(meeting_subject
))
6205 SIPE_DEBUG_INFO("sipe_tooltip_text: %s meeting subject: '%s'", name
, meeting_subject
);
6206 SIPE_ADD_BUDDY_INFO(_("Meeting about"), meeting_subject
);
6210 SIPE_DEBUG_INFO("sipe_tooltip_text: %s note: '%s'", name
, note
);
6211 SIPE_ADD_BUDDY_INFO_NOESCAPE(is_oof_note
? _("Out of office note") : _("Note"),
6212 g_strdup_printf("<i>%s</i>", note
));
6215 SIPE_ADD_BUDDY_INFO(_("Access level"), access_text
);
6216 g_free(access_text
);
6222 static PurpleBuddy
*
6223 purple_blist_add_buddy_clone(PurpleGroup
* group
, PurpleBuddy
* buddy
)
6226 const gchar
*server_alias
, *email
;
6227 const PurpleStatus
*status
= purple_presence_get_active_status(purple_buddy_get_presence(buddy
));
6229 clone
= purple_buddy_new(buddy
->account
, buddy
->name
, buddy
->alias
);
6231 purple_blist_add_buddy(clone
, NULL
, group
, NULL
);
6233 server_alias
= purple_buddy_get_server_alias(buddy
);
6235 purple_blist_server_alias_buddy(clone
, server_alias
);
6238 email
= purple_blist_node_get_string(&buddy
->node
, EMAIL_PROP
);
6240 purple_blist_node_set_string(&clone
->node
, EMAIL_PROP
, email
);
6243 purple_presence_set_status_active(purple_buddy_get_presence(clone
), purple_status_get_id(status
), TRUE
);
6245 purple_prpl_got_user_status(clone
->account
, clone
->name
, purple_status_get_id(status
), NULL
);
6250 sipe_buddy_menu_copy_to_cb(PurpleBlistNode
*node
, const char *group_name
)
6252 PurpleBuddy
*buddy
, *b
;
6253 PurpleConnection
*gc
;
6254 PurpleGroup
* group
= purple_find_group(group_name
);
6256 g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node
));
6258 buddy
= (PurpleBuddy
*)node
;
6260 SIPE_DEBUG_INFO("sipe_buddy_menu_copy_to_cb: copying %s to %s", buddy
->name
, group_name
);
6261 gc
= purple_account_get_connection(buddy
->account
);
6263 b
= purple_find_buddy_in_group(buddy
->account
, buddy
->name
, group
);
6265 b
= purple_blist_add_buddy_clone(group
, buddy
);
6268 sipe_add_buddy(gc
, b
, group
);
6272 sipe_buddy_menu_chat_new_cb(PurpleBuddy
*buddy
)
6274 struct sipe_core_private
*sipe_private
= PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE
;
6276 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_new_cb: buddy->name=%s", buddy
->name
);
6278 /* 2007+ conference */
6279 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
))
6281 sipe_conf_add(sipe_private
, buddy
->name
);
6283 else /* 2005- multiparty chat */
6285 gchar
*self
= sip_uri_self(sipe_private
);
6286 struct sip_session
*session
;
6288 session
= sipe_session_add_chat(sipe_private
,
6292 session
->chat_session
->backend
= sipe_backend_chat_create(SIPE_CORE_PUBLIC
,
6293 session
->chat_session
,
6294 session
->chat_session
->title
,
6298 sipe_invite(sipe_private
, session
, buddy
->name
, NULL
, NULL
, NULL
, FALSE
);
6303 * For 2007+ conference only.
6306 sipe_buddy_menu_chat_make_leader_cb(PurpleBuddy
*buddy
,
6307 struct sipe_chat_session
*chat_session
)
6309 struct sipe_core_private
*sipe_private
= PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE
;
6310 struct sip_session
*session
;
6312 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_make_leader_cb: buddy->name=%s", buddy
->name
);
6313 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_make_leader_cb: chat_title=%s", chat_session
->title
);
6315 session
= sipe_session_find_chat(sipe_private
, chat_session
);
6317 sipe_conf_modify_user_role(sipe_private
, session
, buddy
->name
);
6321 * For 2007+ conference only.
6324 sipe_buddy_menu_chat_remove_cb(PurpleBuddy
*buddy
,
6325 struct sipe_chat_session
*chat_session
)
6327 struct sipe_core_private
*sipe_private
= PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE
;
6328 struct sip_session
*session
;
6330 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_remove_cb: buddy->name=%s", buddy
->name
);
6331 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_remove_cb: chat_title=%s", chat_session
->title
);
6333 session
= sipe_session_find_chat(sipe_private
, chat_session
);
6335 sipe_conf_delete_user(sipe_private
, session
, buddy
->name
);
6339 sipe_buddy_menu_chat_invite_cb(PurpleBuddy
*buddy
,
6340 struct sipe_chat_session
*chat_session
)
6342 struct sipe_core_private
*sipe_private
= PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE
;
6344 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_invite_cb: buddy->name=%s", buddy
->name
);
6345 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_invite_cb: chat_title=%s", chat_session
->title
);
6347 sipe_core_chat_invite(SIPE_CORE_PUBLIC
, chat_session
, buddy
->name
);
6351 sipe_buddy_menu_make_call_cb(PurpleBuddy
*buddy
, const char *phone
)
6353 struct sipe_core_private
*sipe_private
= PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE
;
6355 SIPE_DEBUG_INFO("sipe_buddy_menu_make_call_cb: buddy->name=%s", buddy
->name
);
6357 char *tel_uri
= sip_to_tel_uri(phone
);
6359 SIPE_DEBUG_INFO("sipe_buddy_menu_make_call_cb: going to call number: %s", tel_uri
? tel_uri
: "");
6360 sip_csta_make_call(sipe_private
, tel_uri
);
6367 sipe_buddy_menu_access_level_help_cb(PurpleBuddy
*buddy
)
6369 /** Translators: replace with URL to localized page
6370 * If it doesn't exist copy the original URL */
6371 purple_notify_uri(buddy
->account
->gc
, _("https://sourceforge.net/apps/mediawiki/sipe/index.php?title=Access_Levels"));
6375 sipe_buddy_menu_send_email_cb(PurpleBuddy
*buddy
)
6378 SIPE_DEBUG_INFO("sipe_buddy_menu_send_email_cb: buddy->name=%s", buddy
->name
);
6380 email
= purple_blist_node_get_string(&buddy
->node
, EMAIL_PROP
);
6383 char *command_line
= g_strdup_printf(
6389 " mailto:%s", email
);
6390 SIPE_DEBUG_INFO("sipe_buddy_menu_send_email_cb: going to call email client: %s", command_line
);
6392 g_spawn_command_line_async(command_line
, NULL
);
6393 g_free(command_line
);
6397 SIPE_DEBUG_INFO("sipe_buddy_menu_send_email_cb: no email address stored for buddy=%s", buddy
->name
);
6402 sipe_buddy_menu_access_level_cb(PurpleBuddy
*buddy
,
6403 struct sipe_container
*container
)
6405 struct sipe_core_private
*sipe_private
= PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE
;
6406 struct sipe_container_member
*member
;
6408 if (!container
|| !container
->members
) return;
6410 member
= ((struct sipe_container_member
*)container
->members
->data
);
6412 if (!member
->type
) return;
6414 SIPE_DEBUG_INFO("sipe_buddy_menu_access_level_cb: container->id=%d, member->type=%s, member->value=%s",
6415 container
->id
, member
->type
, member
->value
? member
->value
: "");
6417 sipe_change_access_level(sipe_private
, container
->id
, member
->type
, member
->value
);
6421 sipe_get_access_control_menu(struct sipe_core_private
*sipe_private
,
6425 * A menu which appear when right-clicking on buddy in contact list.
6428 sipe_buddy_menu(PurpleBuddy
*buddy
)
6430 PurpleBlistNode
*g_node
;
6431 PurpleGroup
*gr_parent
;
6432 PurpleMenuAction
*act
;
6434 GList
*menu_groups
= NULL
;
6435 struct sipe_core_private
*sipe_private
= PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE
;
6436 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
6438 gchar
*self
= sip_uri_self(sipe_private
);
6440 SIPE_SESSION_FOREACH
{
6441 if (!sipe_strcase_equal(self
, buddy
->name
) && session
->chat_session
)
6443 struct sipe_chat_session
*chat_session
= session
->chat_session
;
6444 gboolean is_conf
= (chat_session
->type
== SIPE_CHAT_TYPE_CONFERENCE
);
6446 if (sipe_backend_chat_find(chat_session
->backend
, buddy
->name
))
6448 gboolean conf_op
= sipe_backend_chat_is_operator(chat_session
->backend
, self
);
6451 && !sipe_backend_chat_is_operator(chat_session
->backend
, buddy
->name
) /* Not conf OP */
6452 && conf_op
) /* We are a conf OP */
6454 gchar
*label
= g_strdup_printf(_("Make leader of '%s'"),
6455 chat_session
->title
);
6456 act
= purple_menu_action_new(label
,
6457 PURPLE_CALLBACK(sipe_buddy_menu_chat_make_leader_cb
),
6458 chat_session
, NULL
);
6460 menu
= g_list_prepend(menu
, act
);
6464 && conf_op
) /* We are a conf OP */
6466 gchar
*label
= g_strdup_printf(_("Remove from '%s'"),
6467 chat_session
->title
);
6468 act
= purple_menu_action_new(label
,
6469 PURPLE_CALLBACK(sipe_buddy_menu_chat_remove_cb
),
6470 chat_session
, NULL
);
6472 menu
= g_list_prepend(menu
, act
);
6478 || (is_conf
&& !session
->locked
))
6480 gchar
*label
= g_strdup_printf(_("Invite to '%s'"),
6481 chat_session
->title
);
6482 act
= purple_menu_action_new(label
,
6483 PURPLE_CALLBACK(sipe_buddy_menu_chat_invite_cb
),
6484 chat_session
, NULL
);
6486 menu
= g_list_prepend(menu
, act
);
6490 } SIPE_SESSION_FOREACH_END
;
6492 act
= purple_menu_action_new(_("New chat"),
6493 PURPLE_CALLBACK(sipe_buddy_menu_chat_new_cb
),
6495 menu
= g_list_prepend(menu
, act
);
6497 if (sip
->csta
&& !sip
->csta
->line_status
) {
6499 const char *phone_disp_str
;
6502 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_PROP
);
6503 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_DISPLAY_PROP
);
6505 gchar
*label
= g_strdup_printf(_("Work %s"),
6506 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
6507 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
6511 menu
= g_list_prepend(menu
, act
);
6515 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_MOBILE_PROP
);
6516 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_MOBILE_DISPLAY_PROP
);
6518 gchar
*label
= g_strdup_printf(_("Mobile %s"),
6519 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
6520 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
6524 menu
= g_list_prepend(menu
, act
);
6528 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_HOME_PROP
);
6529 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_HOME_DISPLAY_PROP
);
6531 gchar
*label
= g_strdup_printf(_("Home %s"),
6532 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
6533 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
6537 menu
= g_list_prepend(menu
, act
);
6541 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_OTHER_PROP
);
6542 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_OTHER_DISPLAY_PROP
);
6544 gchar
*label
= g_strdup_printf(_("Other %s"),
6545 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
6546 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
6550 menu
= g_list_prepend(menu
, act
);
6554 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_CUSTOM1_PROP
);
6555 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_CUSTOM1_DISPLAY_PROP
);
6557 gchar
*label
= g_strdup_printf(_("Custom1 %s"),
6558 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
6559 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
6563 menu
= g_list_prepend(menu
, act
);
6567 email
= purple_blist_node_get_string(&buddy
->node
, EMAIL_PROP
);
6569 act
= purple_menu_action_new(_("Send email..."),
6570 PURPLE_CALLBACK(sipe_buddy_menu_send_email_cb
),
6572 menu
= g_list_prepend(menu
, act
);
6576 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
6577 GList
*menu_access_levels
= sipe_get_access_control_menu(sipe_private
, buddy
->name
);
6579 act
= purple_menu_action_new(_("Access level"),
6581 NULL
, menu_access_levels
);
6582 menu
= g_list_prepend(menu
, act
);
6586 gr_parent
= purple_buddy_get_group(buddy
);
6587 for (g_node
= purple_blist_get_root(); g_node
; g_node
= g_node
->next
) {
6590 if (g_node
->type
!= PURPLE_BLIST_GROUP_NODE
)
6593 group
= (PurpleGroup
*)g_node
;
6594 if (group
== gr_parent
)
6597 if (purple_find_buddy_in_group(buddy
->account
, buddy
->name
, group
))
6600 act
= purple_menu_action_new(purple_group_get_name(group
),
6601 PURPLE_CALLBACK(sipe_buddy_menu_copy_to_cb
),
6603 menu_groups
= g_list_prepend(menu_groups
, act
);
6605 menu_groups
= g_list_reverse(menu_groups
);
6607 act
= purple_menu_action_new(_("Copy to"),
6610 menu
= g_list_prepend(menu
, act
);
6612 menu
= g_list_reverse(menu
);
6619 sipe_ask_access_domain_cb(PurpleConnection
*gc
, PurpleRequestFields
*fields
)
6621 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
6622 const char *domain
= purple_request_fields_get_string(fields
, "access_domain");
6623 int index
= purple_request_fields_get_choice(fields
, "container_id");
6624 /* move Blocked first */
6625 int i
= (index
== 4) ? 0 : index
+ 1;
6626 int container_id
= containers
[i
];
6628 SIPE_DEBUG_INFO("sipe_ask_access_domain_cb: domain=%s, container_id=(%d)%d", domain
? domain
: "", index
, container_id
);
6630 sipe_change_access_level(sipe_private
, container_id
, "domain", domain
);
6634 sipe_ask_access_domain(struct sipe_core_private
*sipe_private
)
6636 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
6637 PurpleAccount
*account
= sip
->account
;
6638 PurpleConnection
*gc
= sip
->gc
;
6639 PurpleRequestFields
*fields
;
6640 PurpleRequestFieldGroup
*g
;
6641 PurpleRequestField
*f
;
6643 fields
= purple_request_fields_new();
6645 g
= purple_request_field_group_new(NULL
);
6646 f
= purple_request_field_string_new("access_domain", _("Domain"), "partner-company.com", FALSE
);
6647 purple_request_field_set_required(f
, TRUE
);
6648 purple_request_field_group_add_field(g
, f
);
6650 f
= purple_request_field_choice_new("container_id", _("Access level"), 0);
6651 purple_request_field_choice_add(f
, _("Personal")); /* index 0 */
6652 purple_request_field_choice_add(f
, _("Team"));
6653 purple_request_field_choice_add(f
, _("Company"));
6654 purple_request_field_choice_add(f
, _("Public"));
6655 purple_request_field_choice_add(f
, _("Blocked")); /* index 4 */
6656 purple_request_field_choice_set_default_value(f
, 3); /* index */
6657 purple_request_field_set_required(f
, TRUE
);
6658 purple_request_field_group_add_field(g
, f
);
6660 purple_request_fields_add_group(fields
, g
);
6662 purple_request_fields(gc
, _("Add new domain"),
6663 _("Add new domain"), NULL
, fields
,
6664 _("Add"), G_CALLBACK(sipe_ask_access_domain_cb
),
6666 account
, NULL
, NULL
, gc
);
6670 sipe_buddy_menu_access_level_add_domain_cb(PurpleBuddy
*buddy
)
6672 sipe_ask_access_domain(PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE
);
6676 * Workaround for missing libpurple API to release resources allocated
6677 * during blist_node_menu() callback. See also:
6679 * <http://developer.pidgin.im/ticket/12597>
6681 * We remember all memory blocks in a list and deallocate them when
6683 * - the next time we enter the callback, or
6684 * - the account is disconnected
6686 * That means that after the buddy menu has been closed we have unused
6687 * resources but at least we don't leak them anymore...
6690 sipe_blist_menu_free_containers(struct sipe_core_private
*sipe_private
)
6692 GSList
*entry
= sipe_private
->blist_menu_containers
;
6694 free_container(entry
->data
);
6695 entry
= entry
->next
;
6697 g_slist_free(sipe_private
->blist_menu_containers
);
6698 sipe_private
->blist_menu_containers
= NULL
;
6702 sipe_blist_menu_remember_container(struct sipe_core_private
*sipe_private
,
6703 struct sipe_container
*container
)
6705 sipe_private
->blist_menu_containers
= g_slist_prepend(sipe_private
->blist_menu_containers
,
6710 sipe_get_access_levels_menu(struct sipe_core_private
*sipe_private
,
6711 const char* member_type
,
6712 const char* member_value
,
6713 const gboolean extra_menu
)
6715 GList
*menu_access_levels
= NULL
;
6718 PurpleMenuAction
*act
;
6719 struct sipe_container
*container
;
6720 struct sipe_container_member
*member
;
6721 gboolean is_group_access
= FALSE
;
6722 int container_id
= sipe_find_access_level(sipe_private
, member_type
, member_value
, &is_group_access
);
6724 for (i
= 1; i
<= CONTAINERS_LEN
; i
++) {
6725 /* to put Blocked level last in menu list.
6726 * Blocked should remaim in the first place in the containers[] array.
6728 unsigned int j
= (i
== CONTAINERS_LEN
) ? 0 : i
;
6729 const char *acc_level_name
= sipe_get_access_level_name(containers
[j
]);
6731 container
= g_new0(struct sipe_container
, 1);
6732 member
= g_new0(struct sipe_container_member
, 1);
6733 container
->id
= containers
[j
];
6734 container
->members
= g_slist_append(container
->members
, member
);
6735 member
->type
= g_strdup(member_type
);
6736 member
->value
= g_strdup(member_value
);
6738 /* libpurple memory leak workaround */
6739 sipe_blist_menu_remember_container(sipe_private
, container
);
6741 /* current container/access level */
6742 if (((int)containers
[j
]) == container_id
) {
6743 menu_name
= is_group_access
?
6744 g_strdup_printf(INDENT_MARKED_INHERITED_FMT
, acc_level_name
) :
6745 g_strdup_printf(INDENT_MARKED_FMT
, acc_level_name
);
6747 menu_name
= g_strdup_printf(INDENT_FMT
, acc_level_name
);
6750 act
= purple_menu_action_new(menu_name
,
6751 PURPLE_CALLBACK(sipe_buddy_menu_access_level_cb
),
6754 menu_access_levels
= g_list_prepend(menu_access_levels
, act
);
6757 if (extra_menu
&& (container_id
>= 0)) {
6759 act
= purple_menu_action_new(" --------------", NULL
, NULL
, NULL
);
6760 menu_access_levels
= g_list_prepend(menu_access_levels
, act
);
6762 if (!is_group_access
) {
6763 container
= g_new0(struct sipe_container
, 1);
6764 member
= g_new0(struct sipe_container_member
, 1);
6766 container
->members
= g_slist_append(container
->members
, member
);
6767 member
->type
= g_strdup(member_type
);
6768 member
->value
= g_strdup(member_value
);
6770 /* libpurple memory leak workaround */
6771 sipe_blist_menu_remember_container(sipe_private
, container
);
6773 /* Translators: remove (clear) previously assigned access level */
6774 menu_name
= g_strdup_printf(INDENT_FMT
, _("Unspecify"));
6775 act
= purple_menu_action_new(menu_name
,
6776 PURPLE_CALLBACK(sipe_buddy_menu_access_level_cb
),
6779 menu_access_levels
= g_list_prepend(menu_access_levels
, act
);
6783 menu_access_levels
= g_list_reverse(menu_access_levels
);
6784 return menu_access_levels
;
6788 sipe_get_access_groups_menu(struct sipe_core_private
*sipe_private
)
6790 GList
*menu_access_groups
= NULL
;
6791 PurpleMenuAction
*act
;
6792 GSList
*access_domains
= NULL
;
6797 act
= purple_menu_action_new(_("People in my company"),
6799 NULL
, sipe_get_access_levels_menu(sipe_private
, "sameEnterprise", NULL
, FALSE
));
6800 menu_access_groups
= g_list_prepend(menu_access_groups
, act
);
6802 /* this is original name, don't edit */
6803 act
= purple_menu_action_new(_("People in domains connected with my company"),
6805 NULL
, sipe_get_access_levels_menu(sipe_private
, "federated", NULL
, FALSE
));
6806 menu_access_groups
= g_list_prepend(menu_access_groups
, act
);
6808 act
= purple_menu_action_new(_("People in public domains"),
6810 NULL
, sipe_get_access_levels_menu(sipe_private
, "publicCloud", NULL
, TRUE
));
6811 menu_access_groups
= g_list_prepend(menu_access_groups
, act
);
6813 access_domains
= sipe_get_access_domains(sipe_private
);
6814 entry
= access_domains
;
6816 domain
= entry
->data
;
6818 menu_name
= g_strdup_printf(_("People at %s"), domain
);
6819 act
= purple_menu_action_new(menu_name
,
6821 NULL
, sipe_get_access_levels_menu(sipe_private
, "domain", g_strdup(domain
), TRUE
));
6822 menu_access_groups
= g_list_prepend(menu_access_groups
, act
);
6825 entry
= entry
->next
;
6829 /* People in domains connected with my company */
6830 act
= purple_menu_action_new("-------------------------------------------", NULL
, NULL
, NULL
);
6831 menu_access_groups
= g_list_prepend(menu_access_groups
, act
);
6833 act
= purple_menu_action_new(_("Add new domain..."),
6834 PURPLE_CALLBACK(sipe_buddy_menu_access_level_add_domain_cb
),
6836 menu_access_groups
= g_list_prepend(menu_access_groups
, act
);
6838 menu_access_groups
= g_list_reverse(menu_access_groups
);
6840 return menu_access_groups
;
6844 sipe_get_access_control_menu(struct sipe_core_private
*sipe_private
,
6847 GList
*menu_access_levels
= NULL
;
6848 GList
*menu_access_groups
= NULL
;
6850 PurpleMenuAction
*act
;
6852 /* libpurple memory leak workaround */
6853 sipe_blist_menu_free_containers(sipe_private
);
6855 menu_access_levels
= sipe_get_access_levels_menu(sipe_private
, "user", sipe_get_no_sip_uri(uri
), TRUE
);
6857 menu_access_groups
= sipe_get_access_groups_menu(sipe_private
);
6859 menu_name
= g_strdup_printf(INDENT_FMT
, _("Access groups"));
6860 act
= purple_menu_action_new(menu_name
,
6862 NULL
, menu_access_groups
);
6864 menu_access_levels
= g_list_append(menu_access_levels
, act
);
6866 menu_name
= g_strdup_printf(INDENT_FMT
, _("Online help..."));
6867 act
= purple_menu_action_new(menu_name
,
6868 PURPLE_CALLBACK(sipe_buddy_menu_access_level_help_cb
),
6871 menu_access_levels
= g_list_append(menu_access_levels
, act
);
6873 return menu_access_levels
;
6877 process_get_info_response(struct sipe_core_private
*sipe_private
,
6878 struct sipmsg
*msg
, struct transaction
*trans
)
6880 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
6881 char *uri
= trans
->payload
->data
;
6883 PurpleNotifyUserInfo
*info
;
6884 PurpleBuddy
*pbuddy
= NULL
;
6885 struct sipe_buddy
*sbuddy
;
6886 const char *alias
= NULL
;
6887 char *device_name
= NULL
;
6888 char *server_alias
= NULL
;
6889 char *phone_number
= NULL
;
6892 char *first_name
= NULL
;
6893 char *last_name
= NULL
;
6895 if (!sip
) return FALSE
;
6897 SIPE_DEBUG_INFO("Fetching %s's user info for %s", uri
, sipe_private
->username
);
6899 pbuddy
= purple_find_buddy((PurpleAccount
*)sip
->account
, uri
);
6900 alias
= purple_buddy_get_local_alias(pbuddy
);
6902 //will query buddy UA's capabilities and send answer to log
6903 sipe_options_request(sipe_private
, uri
);
6905 sbuddy
= g_hash_table_lookup(sipe_private
->buddies
, uri
);
6907 device_name
= sbuddy
->device_name
? g_strdup(sbuddy
->device_name
) : NULL
;
6910 info
= purple_notify_user_info_new();
6912 if (msg
->response
!= 200) {
6913 SIPE_DEBUG_INFO("process_get_info_response: SERVICE response is %d", msg
->response
);
6915 sipe_xml
*searchResults
;
6916 const sipe_xml
*mrow
;
6918 SIPE_DEBUG_INFO("process_get_info_response: body:\n%s", msg
->body
? msg
->body
: "");
6919 searchResults
= sipe_xml_parse(msg
->body
, msg
->bodylen
);
6920 if (!searchResults
) {
6921 SIPE_DEBUG_INFO_NOFORMAT("process_get_info_response: no parseable searchResults");
6922 } else if ((mrow
= sipe_xml_child(searchResults
, "Body/Array/row"))) {
6924 server_alias
= g_strdup(sipe_xml_attribute(mrow
, "displayName"));
6925 email
= g_strdup(sipe_xml_attribute(mrow
, "email"));
6926 phone_number
= g_strdup(sipe_xml_attribute(mrow
, "phone"));
6928 /* For 2007 system we will take this from ContactCard -
6929 * it has cleaner tel: URIs at least
6931 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
6932 char *tel_uri
= sip_to_tel_uri(phone_number
);
6933 /* trims its parameters, so call first */
6934 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_DISPLAY_NAME
, server_alias
);
6935 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_EMAIL
, email
);
6936 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_WORK_PHONE
, tel_uri
);
6937 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY
, phone_number
);
6941 if (server_alias
&& strlen(server_alias
) > 0) {
6942 purple_notify_user_info_add_pair(info
, _("Display name"), server_alias
);
6944 if ((value
= sipe_xml_attribute(mrow
, "title")) && strlen(value
) > 0) {
6945 purple_notify_user_info_add_pair(info
, _("Job title"), value
);
6947 if ((value
= sipe_xml_attribute(mrow
, "office")) && strlen(value
) > 0) {
6948 purple_notify_user_info_add_pair(info
, _("Office"), value
);
6950 if (phone_number
&& strlen(phone_number
) > 0) {
6951 purple_notify_user_info_add_pair(info
, _("Business phone"), phone_number
);
6953 if ((value
= sipe_xml_attribute(mrow
, "company")) && strlen(value
) > 0) {
6954 purple_notify_user_info_add_pair(info
, _("Company"), value
);
6956 if ((value
= sipe_xml_attribute(mrow
, "city")) && strlen(value
) > 0) {
6957 purple_notify_user_info_add_pair(info
, _("City"), value
);
6959 if ((value
= sipe_xml_attribute(mrow
, "state")) && strlen(value
) > 0) {
6960 purple_notify_user_info_add_pair(info
, _("State"), value
);
6962 if ((value
= sipe_xml_attribute(mrow
, "country")) && strlen(value
) > 0) {
6963 purple_notify_user_info_add_pair(info
, _("Country"), value
);
6965 if (email
&& strlen(email
) > 0) {
6966 purple_notify_user_info_add_pair(info
, _("Email address"), email
);
6970 sipe_xml_free(searchResults
);
6973 purple_notify_user_info_add_section_break(info
);
6975 if (is_empty(server_alias
)) {
6976 g_free(server_alias
);
6977 server_alias
= g_strdup(purple_buddy_get_server_alias(pbuddy
));
6979 purple_notify_user_info_add_pair(info
, _("Display name"), server_alias
);
6983 /* present alias if it differs from server alias */
6984 if (alias
&& !sipe_strequal(alias
, server_alias
))
6986 purple_notify_user_info_add_pair(info
, _("Alias"), alias
);
6989 if (is_empty(email
)) {
6991 email
= g_strdup(purple_blist_node_get_string(&pbuddy
->node
, EMAIL_PROP
));
6993 purple_notify_user_info_add_pair(info
, _("Email address"), email
);
6997 site
= purple_blist_node_get_string(&pbuddy
->node
, SITE_PROP
);
6999 purple_notify_user_info_add_pair(info
, _("Site"), site
);
7002 sipe_get_first_last_names(sipe_private
, uri
, &first_name
, &last_name
);
7003 if (first_name
&& last_name
) {
7004 char *link
= g_strconcat("http://www.linkedin.com/pub/dir/", first_name
, "/", last_name
, NULL
);
7006 purple_notify_user_info_add_pair(info
, _("Find on LinkedIn"), link
);
7013 purple_notify_user_info_add_pair(info
, _("Device"), device_name
);
7016 /* show a buddy's user info in a nice dialog box */
7017 purple_notify_userinfo(sip
->gc
, /* connection the buddy info came through */
7018 uri
, /* buddy's URI */
7020 NULL
, /* callback called when dialog closed */
7021 NULL
); /* userdata for callback */
7023 g_free(phone_number
);
7024 g_free(server_alias
);
7026 g_free(device_name
);
7032 * AD search first, LDAP based
7034 void sipe_get_info(PurpleConnection
*gc
, const char *username
)
7036 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
7037 gchar
*domain_uri
= sip_uri_from_name(sipe_private
->public.sip_domain
);
7038 char *row
= g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW
, "msRTCSIP-PrimaryUserAddress", username
);
7039 gchar
*body
= g_strdup_printf(SIPE_SOAP_SEARCH_CONTACT
, 1, row
);
7040 struct transaction_payload
*payload
= g_new0(struct transaction_payload
, 1);
7042 payload
->destroy
= g_free
;
7043 payload
->data
= g_strdup(username
);
7045 SIPE_DEBUG_INFO("sipe_get_contact_data: body:\n%s", body
? body
: "");
7046 send_soap_request_with_cb(sipe_private
, domain_uri
, body
,
7047 process_get_info_response
, payload
);