core cleanup: move out presence subscription functions
[siplcs.git] / src / core / sipe.c
blob114c0093e8e1b934db148b9e062abac1ad83ceea
1 /**
2 * @file sipe.c
4 *****************************************************************************
5 *** !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ***
6 *** ***
7 *** THIS MODULE IS DEPECRATED ***
8 *** ***
9 *** DO NOT ADD ANY NEW CODE TO THIS MODULE ***
10 *** ***
11 *** !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ***
12 *****************************************************************************
14 * pidgin-sipe
16 * Copyright (C) 2010-11 SIPE Project <http://sipe.sourceforge.net/>
17 * Copyright (C) 2010 pier11 <pier11@operamail.com>
18 * Copyright (C) 2009 Anibal Avelar <debianmx@gmail.com>
19 * Copyright (C) 2009 pier11 <pier11@operamail.com>
20 * Copyright (C) 2008 Novell, Inc., Anibal Avelar <debianmx@gmail.com>
21 * Copyright (C) 2007 Anibal Avelar <debianmx@gmail.com>
22 * Copyright (C) 2005 Thomas Butter <butter@uni-mannheim.de>
24 * ***
25 * Thanks to Google's Summer of Code Program and the helpful mentors
26 * ***
28 * Session-based SIP MESSAGE documentation:
29 * http://tools.ietf.org/html/draft-ietf-simple-im-session-00
31 * This program is free software; you can redistribute it and/or modify
32 * it under the terms of the GNU General Public License as published by
33 * the Free Software Foundation; either version 2 of the License, or
34 * (at your option) any later version.
36 * This program is distributed in the hope that it will be useful,
37 * but WITHOUT ANY WARRANTY; without even the implied warranty of
38 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
39 * GNU General Public License for more details.
41 * You should have received a copy of the GNU General Public License
42 * along with this program; if not, write to the Free Software
43 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
46 #ifdef HAVE_CONFIG_H
47 #include "config.h"
48 #endif
50 #include <time.h>
51 #include <stdlib.h>
52 #include <stdio.h>
53 #include <errno.h>
54 #include <string.h>
55 #include <unistd.h>
57 #include <glib.h>
59 #include <libintl.h>
61 #include "sipe-common.h"
63 #include "account.h"
64 #include "blist.h"
65 #include "connection.h"
66 #include "conversation.h"
67 #include "ft.h"
68 #include "notify.h"
69 #include "plugin.h"
70 #include "privacy.h"
71 #include "request.h"
72 #include "savedstatuses.h"
73 #include "version.h"
75 #include "core-depurple.h" /* Temporary for the core de-purple transition */
77 #include "http-conn.h"
78 #include "sipmsg.h"
79 #include "sip-csta.h"
80 #include "sip-soap.h"
81 #include "sip-transport.h"
82 #include "sipe-backend.h"
83 #include "sipe-buddy.h"
84 #include "sipe-cal.h"
85 #include "sipe-chat.h"
86 #include "sipe-conf.h"
87 #include "sipe-core.h"
88 #include "sipe-core-private.h"
89 #include "sipe-group.h"
90 #include "sipe-dialog.h"
91 #include "sipe-ews.h"
92 #ifdef _WIN32
93 #include "sipe-domino.h"
94 #endif
95 #include "sipe-groupchat.h"
96 #include "sipe-im.h"
97 #include "sipe-mime.h"
98 #include "sipe-nls.h"
99 #include "sipe-schedule.h"
100 #include "sipe-session.h"
101 #include "sipe-subscriptions.h"
102 #ifdef HAVE_VV
103 #include "sipe-media.h"
104 #endif
105 #include "sipe-utils.h"
106 #include "sipe-xml.h"
107 #include "uuid.h"
108 #include "sipe.h"
110 #define SIPE_IDLE_SET_DELAY 1 /* 1 sec */
112 #define UPDATE_CALENDAR_DELAY 1*60 /* 1 min */
113 #define UPDATE_CALENDAR_INTERVAL 30*60 /* 30 min */
115 /* Status identifiers (see also: sipe_status_types()) */
116 #define SIPE_STATUS_ID_UNKNOWN purple_primitive_get_id_from_type(PURPLE_STATUS_UNSET) /* Unset (primitive) */
117 #define SIPE_STATUS_ID_OFFLINE purple_primitive_get_id_from_type(PURPLE_STATUS_OFFLINE) /* Offline (primitive) */
118 #define SIPE_STATUS_ID_AVAILABLE purple_primitive_get_id_from_type(PURPLE_STATUS_AVAILABLE) /* Online */
119 /* PURPLE_STATUS_UNAVAILABLE: */
120 #define SIPE_STATUS_ID_BUSY "busy" /* Busy */
121 #define SIPE_STATUS_ID_BUSYIDLE "busyidle" /* BusyIdle */
122 #define SIPE_STATUS_ID_DND "do-not-disturb" /* Do Not Disturb */
123 #define SIPE_STATUS_ID_IN_MEETING "in-a-meeting" /* In a meeting */
124 #define SIPE_STATUS_ID_IN_CONF "in-a-conference" /* In a conference */
125 #define SIPE_STATUS_ID_ON_PHONE "on-the-phone" /* On the phone */
126 #define SIPE_STATUS_ID_INVISIBLE purple_primitive_get_id_from_type(PURPLE_STATUS_INVISIBLE) /* Appear Offline */
127 /* PURPLE_STATUS_AWAY: */
128 #define SIPE_STATUS_ID_IDLE "idle" /* Idle/Inactive */
129 #define SIPE_STATUS_ID_BRB "be-right-back" /* Be Right Back */
130 #define SIPE_STATUS_ID_AWAY purple_primitive_get_id_from_type(PURPLE_STATUS_AWAY) /* Away (primitive) */
131 /** Reuters status (user settable) */
132 #define SIPE_STATUS_ID_LUNCH "out-to-lunch" /* Out To Lunch */
133 /* ??? PURPLE_STATUS_EXTENDED_AWAY */
134 /* ??? PURPLE_STATUS_MOBILE */
135 /* ??? PURPLE_STATUS_TUNE */
137 /* Status attributes (see also sipe_status_types() */
138 #define SIPE_STATUS_ATTR_ID_MESSAGE "message"
140 static struct sipe_activity_map_struct
142 sipe_activity type;
143 const char *token;
144 const char *desc;
145 const char *status_id;
147 } const sipe_activity_map[] =
149 /* This has nothing to do with Availability numbers, like 3500 (online).
150 * Just a mapping of Communicator Activities to Purple statuses to be able display them in Pidgin.
152 { SIPE_ACTIVITY_UNSET, "unset", NULL , NULL },
153 { SIPE_ACTIVITY_ONLINE, "online", NULL , NULL },
154 { SIPE_ACTIVITY_INACTIVE, SIPE_STATUS_ID_IDLE, N_("Inactive") , NULL },
155 { SIPE_ACTIVITY_BUSY, SIPE_STATUS_ID_BUSY, N_("Busy") , SIPE_STATUS_ID_BUSY },
156 { SIPE_ACTIVITY_BUSYIDLE, SIPE_STATUS_ID_BUSYIDLE, N_("Busy-Idle") , NULL },
157 { SIPE_ACTIVITY_DND, SIPE_STATUS_ID_DND, NULL , SIPE_STATUS_ID_DND },
158 { SIPE_ACTIVITY_BRB, SIPE_STATUS_ID_BRB, N_("Be right back") , SIPE_STATUS_ID_BRB },
159 { SIPE_ACTIVITY_AWAY, "away", NULL , NULL },
160 { SIPE_ACTIVITY_LUNCH, SIPE_STATUS_ID_LUNCH, N_("Out to lunch") , NULL },
161 { SIPE_ACTIVITY_OFFLINE, "offline", NULL , NULL },
162 { SIPE_ACTIVITY_ON_PHONE, SIPE_STATUS_ID_ON_PHONE, N_("In a call") , NULL },
163 { SIPE_ACTIVITY_IN_CONF, SIPE_STATUS_ID_IN_CONF, N_("In a conference") , NULL },
164 { SIPE_ACTIVITY_IN_MEETING, SIPE_STATUS_ID_IN_MEETING, N_("In a meeting") , NULL },
165 { SIPE_ACTIVITY_OOF, "out-of-office", N_("Out of office") , NULL },
166 { SIPE_ACTIVITY_URGENT_ONLY, "urgent-interruptions-only", N_("Urgent interruptions only") , NULL }
168 /** @param x is sipe_activity */
169 #define SIPE_ACTIVITY_I18N(x) gettext(sipe_activity_map[x].desc)
171 static sipe_activity
172 sipe_get_activity_by_token(const char *token)
174 int i;
176 for (i = 0; i < SIPE_ACTIVITY_NUM_TYPES; i++)
178 if (sipe_strequal(token, sipe_activity_map[i].token))
179 return sipe_activity_map[i].type;
182 return sipe_activity_map[0].type;
185 static const char *
186 sipe_get_activity_desc_by_token(const char *token)
188 if (!token) return NULL;
190 return SIPE_ACTIVITY_I18N(sipe_get_activity_by_token(token));
193 static void send_presence_status(struct sipe_core_private *sipe_private,
194 void *unused);
197 * Returns pointer to URI without sip: prefix if any
199 * @param sip_uri SIP URI possibly with sip: prefix. Example: sip:first.last@hq.company.com
200 * @return pointer to URL without sip: prefix. Coresponding example: first.last@hq.company.com
202 * Doesn't allocate memory
204 static const char *
205 sipe_get_no_sip_uri(const char *sip_uri)
207 const char *prefix = "sip:";
208 if (!sip_uri) return NULL;
210 if (g_str_has_prefix(sip_uri, prefix)) {
211 return (sip_uri+strlen(prefix));
212 } else {
213 return sip_uri;
217 static void
218 sipe_change_access_level(struct sipe_core_private *sipe_private,
219 const int container_id,
220 const gchar *type,
221 const gchar *value);
223 void
224 sipe_core_contact_allow_deny (struct sipe_core_public *sipe_public,
225 const gchar * who, gboolean allow)
227 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
229 if (allow) {
230 SIPE_DEBUG_INFO("Authorizing contact %s", who);
231 } else {
232 SIPE_DEBUG_INFO("Blocking contact %s", who);
235 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
236 sipe_change_access_level(sipe_private, (allow ? -1 : 32000), "user", sipe_get_no_sip_uri(who));
237 } else {
238 sip_soap_ocs2005_setacl(sipe_private, who, allow);
242 static int
243 sipe_get_availability_by_status(const char* sipe_status_id, char** activity_token);
245 static const char*
246 sipe_get_status_by_availability(int avail,
247 char** activity);
249 static void
250 sipe_set_purple_account_status_and_note(const PurpleAccount *account,
251 const char *status_id,
252 const char *message,
253 time_t do_not_publish[]);
255 static void
256 sipe_apply_calendar_status(struct sipe_core_private *sipe_private,
257 struct sipe_buddy *sbuddy,
258 const char *status_id)
260 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
261 time_t cal_avail_since;
262 int cal_status = sipe_cal_get_status(sbuddy, time(NULL), &cal_avail_since);
263 int avail;
264 gchar *self_uri;
266 if (!sbuddy) return;
268 if (cal_status < SIPE_CAL_NO_DATA) {
269 SIPE_DEBUG_INFO("sipe_apply_calendar_status: cal_status : %d for %s", cal_status, sbuddy->name);
270 SIPE_DEBUG_INFO("sipe_apply_calendar_status: cal_avail_since : %s", asctime(localtime(&cal_avail_since)));
273 /* scheduled Cal update call */
274 if (!status_id) {
275 status_id = sbuddy->last_non_cal_status_id;
276 g_free(sbuddy->activity);
277 sbuddy->activity = g_strdup(sbuddy->last_non_cal_activity);
280 if (!status_id) {
281 SIPE_DEBUG_INFO("sipe_apply_calendar_status: status_id is NULL for %s, exiting.",
282 sbuddy->name ? sbuddy->name : "" );
283 return;
286 /* adjust to calendar status */
287 if (cal_status != SIPE_CAL_NO_DATA) {
288 SIPE_DEBUG_INFO("sipe_apply_calendar_status: user_avail_since: %s", asctime(localtime(&sbuddy->user_avail_since)));
290 if (cal_status == SIPE_CAL_BUSY
291 && cal_avail_since > sbuddy->user_avail_since
292 && 6500 >= sipe_get_availability_by_status(status_id, NULL))
294 status_id = SIPE_STATUS_ID_BUSY;
295 g_free(sbuddy->activity);
296 sbuddy->activity = g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_IN_MEETING));
298 avail = sipe_get_availability_by_status(status_id, NULL);
300 SIPE_DEBUG_INFO("sipe_apply_calendar_status: activity_since : %s", asctime(localtime(&sbuddy->activity_since)));
301 if (cal_avail_since > sbuddy->activity_since) {
302 if (cal_status == SIPE_CAL_OOF
303 && avail >= 15000) /* 12000 in 2007 */
305 g_free(sbuddy->activity);
306 sbuddy->activity = g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_OOF));
311 /* then set status_id actually */
312 SIPE_DEBUG_INFO("sipe_apply_calendar_status: to %s for %s", status_id, sbuddy->name ? sbuddy->name : "" );
313 sipe_backend_buddy_set_status(SIPE_CORE_PUBLIC, sbuddy->name, status_id);
315 /* set our account state to the one in roaming (including calendar info) */
316 self_uri = sip_uri_self(sipe_private);
317 if (sip->initial_state_published && sipe_strcase_equal(sbuddy->name, self_uri)) {
318 if (sipe_strequal(status_id, SIPE_STATUS_ID_OFFLINE)) {
319 status_id = g_strdup(SIPE_STATUS_ID_INVISIBLE); /* not not let offline status switch us off */
322 SIPE_DEBUG_INFO("sipe_apply_calendar_status: switch to '%s' for the account", sip->status);
323 sipe_set_purple_account_status_and_note(sip->account, status_id, sip->note, sip->do_not_publish);
325 g_free(self_uri);
328 void
329 sipe_core_buddy_got_status(struct sipe_core_public *sipe_public,
330 const gchar* uri,
331 const gchar *status_id)
333 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
334 struct sipe_buddy *sbuddy = g_hash_table_lookup(sipe_private->buddies, uri);
336 if (!sbuddy) return;
338 /* Check if on 2005 system contact's calendar,
339 * then set/preserve it.
341 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
342 sipe_apply_calendar_status(sipe_private, sbuddy, status_id);
343 } else {
344 sipe_backend_buddy_set_status(sipe_public, uri, status_id);
348 static void
349 update_calendar_status_cb(SIPE_UNUSED_PARAMETER char *name,
350 struct sipe_buddy *sbuddy,
351 struct sipe_core_private *sipe_private)
353 sipe_apply_calendar_status(sipe_private, sbuddy, NULL);
357 * Updates contact's status
358 * based on their calendar information.
360 * Applicability: 2005 systems
362 static void
363 update_calendar_status(struct sipe_core_private *sipe_private,
364 SIPE_UNUSED_PARAMETER void *unused)
366 SIPE_DEBUG_INFO_NOFORMAT("update_calendar_status() started.");
367 g_hash_table_foreach(sipe_private->buddies, (GHFunc)update_calendar_status_cb, sipe_private);
369 /* repeat scheduling */
370 sipe_sched_calendar_status_update(sipe_private, time(NULL) + 3*60 /* 3 min */);
374 * Schedules process of contacts' status update
375 * based on their calendar information.
376 * Should be scheduled to the beginning of every
377 * 15 min interval, like:
378 * 13:00, 13:15, 13:30, 13:45, etc.
380 * Applicability: 2005 systems
382 void
383 sipe_sched_calendar_status_update(struct sipe_core_private *sipe_private,
384 time_t calculate_from)
386 int interval = 15*60;
387 /** start of the beginning of closest 15 min interval. */
388 time_t next_start = ((time_t)((int)((int)calculate_from)/interval + 1)*interval);
390 SIPE_DEBUG_INFO("sipe_sched_calendar_status_update: calculate_from time: %s",
391 asctime(localtime(&calculate_from)));
392 SIPE_DEBUG_INFO("sipe_sched_calendar_status_update: next start time : %s",
393 asctime(localtime(&next_start)));
395 sipe_schedule_seconds(sipe_private,
396 "<+2005-cal-status>",
397 NULL,
398 next_start - time(NULL),
399 update_calendar_status,
400 NULL);
404 * Schedules process of self status publish
405 * based on own calendar information.
406 * Should be scheduled to the beginning of every
407 * 15 min interval, like:
408 * 13:00, 13:15, 13:30, 13:45, etc.
410 * Applicability: 2007+ systems
412 static void
413 sipe_sched_calendar_status_self_publish(struct sipe_core_private *sipe_private,
414 time_t calculate_from)
416 int interval = 5*60;
417 /** start of the beginning of closest 5 min interval. */
418 time_t next_start = ((time_t)((int)((int)calculate_from)/interval + 1)*interval);
420 SIPE_DEBUG_INFO("sipe_sched_calendar_status_self_publish: calculate_from time: %s",
421 asctime(localtime(&calculate_from)));
422 SIPE_DEBUG_INFO("sipe_sched_calendar_status_self_publish: next start time : %s",
423 asctime(localtime(&next_start)));
425 sipe_schedule_seconds(sipe_private,
426 "<+2007-cal-status>",
427 NULL,
428 next_start - time(NULL),
429 publish_calendar_status_self,
430 NULL);
433 void sipe_set_status(PurpleAccount *account, PurpleStatus *status)
435 SIPE_DEBUG_INFO("sipe_set_status: status=%s", purple_status_get_id(status));
437 if (!purple_status_is_active(status))
438 return;
440 if (account->gc) {
441 struct sipe_core_private *sipe_private = PURPLE_ACCOUNT_TO_SIPE_CORE_PRIVATE;
442 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
444 if (sip) {
445 gchar *action_name;
446 gchar *tmp;
447 time_t now = time(NULL);
448 const char *status_id = purple_status_get_id(status);
449 const char *note = purple_status_get_attr_string(status, SIPE_STATUS_ATTR_ID_MESSAGE);
450 sipe_activity activity = sipe_get_activity_by_token(status_id);
451 gboolean do_not_publish = ((now - sip->do_not_publish[activity]) <= 2);
453 /* when other point of presence clears note, but we are keeping
454 * state if OOF note.
456 if (do_not_publish && !note && sip->cal && sip->cal->oof_note) {
457 SIPE_DEBUG_INFO_NOFORMAT("sipe_set_status: enabling publication as OOF note keepers.");
458 do_not_publish = FALSE;
461 SIPE_DEBUG_INFO("sipe_set_status: was: sip->do_not_publish[%s]=%d [?] now(time)=%d",
462 status_id, (int)sip->do_not_publish[activity], (int)now);
464 sip->do_not_publish[activity] = 0;
465 SIPE_DEBUG_INFO("sipe_set_status: set: sip->do_not_publish[%s]=%d [0]",
466 status_id, (int)sip->do_not_publish[activity]);
468 if (do_not_publish)
470 SIPE_DEBUG_INFO_NOFORMAT("sipe_set_status: publication was switched off, exiting.");
471 return;
474 g_free(sip->status);
475 sip->status = g_strdup(status_id);
477 /* hack to escape apostrof before comparison */
478 tmp = note ? sipe_utils_str_replace(note, "'", "&apos;") : NULL;
480 /* this will preserve OOF flag as well */
481 if (!sipe_strequal(tmp, sip->note)) {
482 sip->is_oof_note = FALSE;
483 g_free(sip->note);
484 sip->note = g_strdup(note);
485 sip->note_since = time(NULL);
487 g_free(tmp);
489 /* schedule 2 sec to capture idle flag */
490 action_name = g_strdup_printf("<%s>", "+set-status");
491 sipe_schedule_seconds(sipe_private,
492 action_name,
493 NULL,
494 SIPE_IDLE_SET_DELAY,
495 send_presence_status,
496 NULL);
497 g_free(action_name);
502 void
503 sipe_set_idle(PurpleConnection * gc,
504 int interval)
506 SIPE_DEBUG_INFO("sipe_set_idle: interval=%d", interval);
508 if (gc) {
509 struct sipe_core_private *sipe_private = PURPLE_GC_TO_SIPE_CORE_PRIVATE;
510 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
512 if (sip) {
513 sip->idle_switch = time(NULL);
514 SIPE_DEBUG_INFO("sipe_set_idle: sip->idle_switch : %s", asctime(localtime(&(sip->idle_switch))));
519 void sipe_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
521 SIPE_DEBUG_INFO("sipe_add_buddy[CB]: buddy:%s group:%s", buddy ? buddy->name : "", group ? group->name : "");
523 /* libpurple can call us with undefined buddy or group */
524 if (buddy && group) {
525 struct sipe_core_private *sipe_private = PURPLE_GC_TO_SIPE_CORE_PRIVATE;
527 /* Buddy name must be lower case as we use purple_normalize_nocase() to compare */
528 gchar *buddy_name = g_ascii_strdown(buddy->name, -1);
529 purple_blist_rename_buddy(buddy, buddy_name);
530 g_free(buddy_name);
532 /* Prepend sip: if needed */
533 if (!g_str_has_prefix(buddy->name, "sip:")) {
534 gchar *buf = sip_uri_from_name(buddy->name);
535 purple_blist_rename_buddy(buddy, buf);
536 g_free(buf);
539 if (!g_hash_table_lookup(sipe_private->buddies, buddy->name)) {
540 struct sipe_buddy *b = g_new0(struct sipe_buddy, 1);
541 SIPE_DEBUG_INFO("sipe_add_buddy: adding %s", buddy->name);
542 b->name = g_strdup(buddy->name);
543 b->just_added = TRUE;
544 g_hash_table_insert(sipe_private->buddies, b->name, b);
545 /* @TODO should go to callback */
546 sipe_subscribe_presence_single(sipe_private,
547 b->name);
548 } else {
549 SIPE_DEBUG_INFO("sipe_add_buddy: buddy %s already in internal list", buddy->name);
552 sipe_core_buddy_group(PURPLE_GC_TO_SIPE_CORE_PUBLIC, buddy->name, NULL, group->name);
556 static void sipe_free_buddy(struct sipe_buddy *buddy)
558 #ifndef _WIN32
560 * We are calling g_hash_table_foreach_steal(). That means that no
561 * key/value deallocation functions are called. Therefore the glib
562 * hash code does not touch the key (buddy->name) or value (buddy)
563 * of the to-be-deleted hash node at all. It follows that we
565 * - MUST free the memory for the key ourselves and
566 * - ARE allowed to do it in this function
568 * Conclusion: glib must be broken on the Windows platform if sipe
569 * crashes with SIGTRAP when closing. You'll have to live
570 * with the memory leak until this is fixed.
572 g_free(buddy->name);
573 #endif
574 g_free(buddy->activity);
575 g_free(buddy->meeting_subject);
576 g_free(buddy->meeting_location);
577 g_free(buddy->note);
579 g_free(buddy->cal_start_time);
580 g_free(buddy->cal_free_busy_base64);
581 g_free(buddy->cal_free_busy);
582 g_free(buddy->last_non_cal_activity);
584 sipe_cal_free_working_hours(buddy->cal_working_hours);
586 g_free(buddy->device_name);
587 g_slist_free(buddy->groups);
588 g_free(buddy);
592 * Unassociates buddy from group first.
593 * Then see if no groups left, removes buddy completely.
594 * Otherwise updates buddy groups on server.
596 void sipe_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
598 struct sipe_core_private *sipe_private = PURPLE_GC_TO_SIPE_CORE_PRIVATE;
599 struct sipe_buddy *b;
600 struct sipe_group *g = NULL;
602 SIPE_DEBUG_INFO("sipe_remove_buddy[CB]: buddy:%s group:%s", buddy ? buddy->name : "", group ? group->name : "");
603 if (!buddy) return;
605 b = g_hash_table_lookup(sipe_private->buddies, buddy->name);
606 if (!b) return;
608 if (group) {
609 g = sipe_group_find_by_name(sipe_private, group->name);
612 if (g) {
613 b->groups = g_slist_remove(b->groups, g);
614 SIPE_DEBUG_INFO("buddy %s removed from group %s", buddy->name, g->name);
617 if (g_slist_length(b->groups) < 1) {
618 gchar *action_name = sipe_utils_presence_key(buddy->name);
619 sipe_schedule_cancel(sipe_private, action_name);
620 g_free(action_name);
622 g_hash_table_remove(sipe_private->buddies, buddy->name);
624 if (b->name) {
625 gchar *request = g_strdup_printf("<m:URI>%s</m:URI>",
626 b->name);
627 sip_soap_request(sipe_private,
628 "deleteContact",
629 request);
630 g_free(request);
633 sipe_free_buddy(b);
634 } else {
635 //updates groups on server
636 sipe_core_group_set_user(SIPE_CORE_PUBLIC, b->name);
641 static int
642 sipe_find_access_level(struct sipe_core_private *sipe_private,
643 const gchar *type,
644 const gchar *value,
645 gboolean *is_group_access);
647 static void
648 sipe_refresh_blocked_status_cb(char *buddy_name,
649 SIPE_UNUSED_PARAMETER struct sipe_buddy *buddy,
650 struct sipe_core_private *sipe_private)
652 int container_id = sipe_find_access_level(sipe_private, "user", buddy_name, NULL);
653 gboolean blocked = (container_id == 32000);
654 gboolean blocked_in_blist = sipe_backend_buddy_is_blocked(SIPE_CORE_PUBLIC, buddy_name);
656 /* SIPE_DEBUG_INFO("sipe_refresh_blocked_status_cb: buddy_name=%s, blocked=%s, blocked_in_blist=%s",
657 buddy_name, blocked ? "T" : "F", blocked_in_blist ? "T" : "F"); */
659 if (blocked != blocked_in_blist) {
660 sipe_backend_buddy_set_blocked_status(SIPE_CORE_PUBLIC, buddy_name, blocked);
664 static void
665 sipe_refresh_blocked_status(struct sipe_core_private *sipe_private)
667 g_hash_table_foreach(sipe_private->buddies,
668 (GHFunc) sipe_refresh_blocked_status_cb,
669 sipe_private);
672 /** MS-PRES container */
673 struct sipe_container {
674 guint id;
675 guint version;
676 GSList *members;
678 /** MS-PRES container member */
679 struct sipe_container_member {
680 /** user, domain, sameEnterprise, federated, publicCloud; everyone */
681 gchar *type;
682 gchar *value;
685 static void
686 free_container_member(struct sipe_container_member *member)
688 if (!member) return;
690 g_free(member->type);
691 g_free(member->value);
692 g_free(member);
695 static void
696 free_container(struct sipe_container *container)
698 GSList *entry;
700 if (!container) return;
702 entry = container->members;
703 while (entry) {
704 void *data = entry->data;
705 entry = g_slist_remove(entry, data);
706 free_container_member((struct sipe_container_member *)data);
708 g_free(container);
711 static void
712 sipe_send_container_members_prepare(const guint container_id,
713 const guint container_version,
714 const gchar *action,
715 const gchar *type,
716 const gchar *value,
717 char **container_xmls)
719 gchar *value_str = value ? g_strdup_printf(" value=\"%s\"", value) : g_strdup("");
720 gchar *body;
722 if (!container_xmls) return;
724 body = g_strdup_printf(
725 "<container id=\"%d\" version=\"%d\"><member action=\"%s\" type=\"%s\"%s/></container>",
726 container_id,
727 container_version,
728 action,
729 type,
730 value_str);
731 g_free(value_str);
733 if ((*container_xmls) == NULL) {
734 *container_xmls = body;
735 } else {
736 char *tmp = *container_xmls;
738 *container_xmls = g_strconcat(*container_xmls, body, NULL);
739 g_free(tmp);
740 g_free(body);
744 static void
745 sipe_send_set_container_members(struct sipe_core_private *sipe_private,
746 char *container_xmls)
748 gchar *self;
749 gchar *contact;
750 gchar *hdr;
751 gchar *body;
753 if (!container_xmls) return;
755 self = sip_uri_self(sipe_private);
756 body = g_strdup_printf(
757 "<setContainerMembers xmlns=\"http://schemas.microsoft.com/2006/09/sip/container-management\">"
758 "%s"
759 "</setContainerMembers>",
760 container_xmls);
762 contact = get_contact(sipe_private);
763 hdr = g_strdup_printf("Contact: %s\r\n"
764 "Content-Type: application/msrtc-setcontainermembers+xml\r\n", contact);
765 g_free(contact);
767 sip_transport_service(sipe_private,
768 self,
769 hdr,
770 body,
771 NULL);
773 g_free(hdr);
774 g_free(body);
775 g_free(self);
779 * Finds locally stored MS-PRES container member
781 static struct sipe_container_member *
782 sipe_find_container_member(struct sipe_container *container,
783 const gchar *type,
784 const gchar *value)
786 struct sipe_container_member *member;
787 GSList *entry;
789 if (container == NULL || type == NULL) {
790 return NULL;
793 entry = container->members;
794 while (entry) {
795 member = entry->data;
796 if (sipe_strcase_equal(member->type, type) &&
797 sipe_strcase_equal(member->value, value))
799 return member;
801 entry = entry->next;
803 return NULL;
807 * Finds locally stored MS-PRES container by id
809 static struct sipe_container *
810 sipe_find_container(struct sipe_core_private *sipe_private,
811 guint id)
813 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
814 struct sipe_container *container;
815 GSList *entry;
817 if (sip == NULL) {
818 return NULL;
821 entry = sip->containers;
822 while (entry) {
823 container = entry->data;
824 if (id == container->id) {
825 return container;
827 entry = entry->next;
829 return NULL;
832 static GSList *
833 sipe_get_access_domains(struct sipe_core_private *sipe_private)
835 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
836 struct sipe_container *container;
837 struct sipe_container_member *member;
838 GSList *entry;
839 GSList *entry2;
840 GSList *res = NULL;
842 if (!sip) return NULL;
844 entry = sip->containers;
845 while (entry) {
846 container = entry->data;
848 entry2 = container->members;
849 while (entry2) {
850 member = entry2->data;
851 if (sipe_strcase_equal(member->type, "domain"))
853 res = slist_insert_unique_sorted(res, g_strdup(member->value), (GCompareFunc)g_ascii_strcasecmp);
855 entry2 = entry2->next;
857 entry = entry->next;
859 return res;
863 * Returns pointer to domain part in provided Email URL
865 * @param email an email URL. Example: first.last@hq.company.com
866 * @return pointer to domain part of email URL. Coresponding example: hq.company.com
868 * Doesn't allocate memory
870 static const char *
871 sipe_get_domain(const char *email)
873 char *tmp;
875 if (!email) return NULL;
877 tmp = strstr(email, "@");
879 if (tmp && ((tmp+1) < (email + strlen(email)))) {
880 return tmp+1;
881 } else {
882 return NULL;
887 /* @TODO: replace with binary search for faster access? */
888 /** source: http://support.microsoft.com/kb/897567 */
889 static const char * const public_domains [] = {
890 "aol.com", "icq.com", "love.com", "mac.com", "br.live.com",
891 "hotmail.co.il", "hotmail.co.jp", "hotmail.co.th", "hotmail.co.uk",
892 "hotmail.com", "hotmail.com.ar", "hotmail.com.tr", "hotmail.es",
893 "hotmail.de", "hotmail.fr", "hotmail.it", "live.at", "live.be",
894 "live.ca", "live.cl", "live.cn", "live.co.in", "live.co.kr",
895 "live.co.uk", "live.co.za", "live.com", "live.com.ar", "live.com.au",
896 "live.com.co", "live.com.mx", "live.com.my", "live.com.pe",
897 "live.com.ph", "live.com.pk", "live.com.pt", "live.com.sg",
898 "live.com.ve", "live.de", "live.dk", "live.fr", "live.hk", "live.ie",
899 "live.in", "live.it", "live.jp", "live.nl", "live.no", "live.ph",
900 "live.ru", "live.se", "livemail.com.br", "livemail.tw",
901 "messengeruser.com", "msn.com", "passport.com", "sympatico.ca",
902 "tw.live.com", "webtv.net", "windowslive.com", "windowslive.es",
903 "yahoo.com",
904 NULL};
906 static gboolean
907 sipe_is_public_domain(const char *domain)
909 int i = 0;
910 while (public_domains[i]) {
911 if (sipe_strcase_equal(public_domains[i], domain)) {
912 return TRUE;
914 i++;
916 return FALSE;
920 * Access Levels
921 * 32000 - Blocked
922 * 400 - Personal
923 * 300 - Team
924 * 200 - Company
925 * 100 - Public
927 static const char *
928 sipe_get_access_level_name(int container_id)
930 switch(container_id) {
931 case 32000: return _("Blocked");
932 case 400: return _("Personal");
933 case 300: return _("Team");
934 case 200: return _("Company");
935 case 100: return _("Public");
937 return _("Unknown");
940 static const guint containers[] = {32000, 400, 300, 200, 100};
941 #define CONTAINERS_LEN (sizeof(containers) / sizeof(guint))
944 static int
945 sipe_find_member_access_level(struct sipe_core_private *sipe_private,
946 const gchar *type,
947 const gchar *value)
949 unsigned int i = 0;
950 const gchar *value_mod = value;
952 if (!type) return -1;
954 if (sipe_strequal("user", type)) {
955 value_mod = sipe_get_no_sip_uri(value);
958 for (i = 0; i < CONTAINERS_LEN; i++) {
959 struct sipe_container_member *member;
960 struct sipe_container *container = sipe_find_container(sipe_private, containers[i]);
961 if (!container) continue;
963 member = sipe_find_container_member(container, type, value_mod);
964 if (member) return containers[i];
967 return -1;
970 /** Member type: user, domain, sameEnterprise, federated, publicCloud; everyone */
971 static int
972 sipe_find_access_level(struct sipe_core_private *sipe_private,
973 const gchar *type,
974 const gchar *value,
975 gboolean *is_group_access)
977 int container_id = -1;
979 if (sipe_strequal("user", type)) {
980 const char *domain;
981 const char *no_sip_uri = sipe_get_no_sip_uri(value);
983 container_id = sipe_find_member_access_level(sipe_private, "user", no_sip_uri);
984 if (container_id >= 0) {
985 if (is_group_access) *is_group_access = FALSE;
986 return container_id;
989 domain = sipe_get_domain(no_sip_uri);
990 container_id = sipe_find_member_access_level(sipe_private, "domain", domain);
991 if (container_id >= 0) {
992 if (is_group_access) *is_group_access = TRUE;
993 return container_id;
996 container_id = sipe_find_member_access_level(sipe_private, "sameEnterprise", NULL);
997 if ((container_id >= 0) && sipe_strcase_equal(sipe_private->public.sip_domain, domain)) {
998 if (is_group_access) *is_group_access = TRUE;
999 return container_id;
1002 container_id = sipe_find_member_access_level(sipe_private, "publicCloud", NULL);
1003 if ((container_id >= 0) && sipe_is_public_domain(domain)) {
1004 if (is_group_access) *is_group_access = TRUE;
1005 return container_id;
1008 container_id = sipe_find_member_access_level(sipe_private, "everyone", NULL);
1009 if ((container_id >= 0)) {
1010 if (is_group_access) *is_group_access = TRUE;
1011 return container_id;
1013 } else {
1014 container_id = sipe_find_member_access_level(sipe_private, type, value);
1015 if (is_group_access) *is_group_access = FALSE;
1018 return container_id;
1022 * @param container_id a new access level. If -1 then current access level
1023 * is just removed (I.e. the member is removed from all containers).
1024 * @param type a type of member. E.g. "user", "sameEnterprise", etc.
1025 * @param value a value for member. E.g. SIP URI for "user" member type.
1027 static void
1028 sipe_change_access_level(struct sipe_core_private *sipe_private,
1029 const int container_id,
1030 const gchar *type,
1031 const gchar *value)
1033 unsigned int i;
1034 int current_container_id = -1;
1035 char *container_xmls = NULL;
1037 /* for each container: find/delete */
1038 for (i = 0; i < CONTAINERS_LEN; i++) {
1039 struct sipe_container_member *member;
1040 struct sipe_container *container = sipe_find_container(sipe_private, containers[i]);
1042 if (!container) continue;
1044 member = sipe_find_container_member(container, type, value);
1045 if (member) {
1046 current_container_id = containers[i];
1047 /* delete/publish current access level */
1048 if (container_id < 0 || container_id != current_container_id) {
1049 sipe_send_container_members_prepare(current_container_id, container->version, "remove", type, value, &container_xmls);
1050 /* remove member from our cache, to be able to recalculate AL below */
1051 container->members = g_slist_remove(container->members, member);
1052 current_container_id = -1;
1057 /* recalculate AL below */
1058 current_container_id = sipe_find_access_level(sipe_private, type, value, NULL);
1060 /* assign/publish new access level */
1061 if (container_id != current_container_id && container_id >= 0) {
1062 struct sipe_container *container = sipe_find_container(sipe_private, container_id);
1063 guint version = container ? container->version : 0;
1065 sipe_send_container_members_prepare(container_id, version, "add", type, value, &container_xmls);
1068 if (container_xmls) {
1069 sipe_send_set_container_members(sipe_private, container_xmls);
1071 g_free(container_xmls);
1074 static void
1075 free_publication(struct sipe_publication *publication)
1077 g_free(publication->category);
1078 g_free(publication->cal_event_hash);
1079 g_free(publication->note);
1081 g_free(publication->working_hours_xml_str);
1082 g_free(publication->fb_start_str);
1083 g_free(publication->free_busy_base64);
1085 g_free(publication);
1088 /* key is <category><instance><container> */
1089 static gboolean
1090 sipe_is_our_publication(struct sipe_core_private *sipe_private,
1091 const gchar *key)
1093 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
1094 GSList *entry;
1096 /* filling keys for our publications if not yet cached */
1097 if (!sip->our_publication_keys) {
1098 guint device_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_DEVICE);
1099 guint machine_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_MACHINE);
1100 guint user_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_USER);
1101 guint calendar_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_CALENDAR);
1102 guint cal_oof_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_CALENDAR_OOF);
1103 guint cal_data_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_CALENDAR_DATA);
1104 guint note_oof_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_NOTE_OOF);
1106 SIPE_DEBUG_INFO_NOFORMAT("* Our Publication Instances *");
1107 SIPE_DEBUG_INFO("\tDevice : %u\t0x%08X", device_instance, device_instance);
1108 SIPE_DEBUG_INFO("\tMachine State : %u\t0x%08X", machine_instance, machine_instance);
1109 SIPE_DEBUG_INFO("\tUser Stare : %u\t0x%08X", user_instance, user_instance);
1110 SIPE_DEBUG_INFO("\tCalendar State : %u\t0x%08X", calendar_instance, calendar_instance);
1111 SIPE_DEBUG_INFO("\tCalendar OOF State : %u\t0x%08X", cal_oof_instance, cal_oof_instance);
1112 SIPE_DEBUG_INFO("\tCalendar FreeBusy : %u\t0x%08X", cal_data_instance, cal_data_instance);
1113 SIPE_DEBUG_INFO("\tOOF Note : %u\t0x%08X", note_oof_instance, note_oof_instance);
1114 SIPE_DEBUG_INFO("\tNote : %u", 0);
1115 SIPE_DEBUG_INFO("\tCalendar WorkingHours: %u", 0);
1117 /* device */
1118 sip->our_publication_keys = g_slist_append(sip->our_publication_keys,
1119 g_strdup_printf("<%s><%u><%u>", "device", device_instance, 2));
1121 /* state:machineState */
1122 sip->our_publication_keys = g_slist_append(sip->our_publication_keys,
1123 g_strdup_printf("<%s><%u><%u>", "state", machine_instance, 2));
1124 sip->our_publication_keys = g_slist_append(sip->our_publication_keys,
1125 g_strdup_printf("<%s><%u><%u>", "state", machine_instance, 3));
1127 /* state:userState */
1128 sip->our_publication_keys = g_slist_append(sip->our_publication_keys,
1129 g_strdup_printf("<%s><%u><%u>", "state", user_instance, 2));
1130 sip->our_publication_keys = g_slist_append(sip->our_publication_keys,
1131 g_strdup_printf("<%s><%u><%u>", "state", user_instance, 3));
1133 /* state:calendarState */
1134 sip->our_publication_keys = g_slist_append(sip->our_publication_keys,
1135 g_strdup_printf("<%s><%u><%u>", "state", calendar_instance, 2));
1136 sip->our_publication_keys = g_slist_append(sip->our_publication_keys,
1137 g_strdup_printf("<%s><%u><%u>", "state", calendar_instance, 3));
1139 /* state:calendarState OOF */
1140 sip->our_publication_keys = g_slist_append(sip->our_publication_keys,
1141 g_strdup_printf("<%s><%u><%u>", "state", cal_oof_instance, 2));
1142 sip->our_publication_keys = g_slist_append(sip->our_publication_keys,
1143 g_strdup_printf("<%s><%u><%u>", "state", cal_oof_instance, 3));
1145 /* note */
1146 sip->our_publication_keys = g_slist_append(sip->our_publication_keys,
1147 g_strdup_printf("<%s><%u><%u>", "note", 0, 200));
1148 sip->our_publication_keys = g_slist_append(sip->our_publication_keys,
1149 g_strdup_printf("<%s><%u><%u>", "note", 0, 300));
1150 sip->our_publication_keys = g_slist_append(sip->our_publication_keys,
1151 g_strdup_printf("<%s><%u><%u>", "note", 0, 400));
1153 /* note OOF */
1154 sip->our_publication_keys = g_slist_append(sip->our_publication_keys,
1155 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance, 200));
1156 sip->our_publication_keys = g_slist_append(sip->our_publication_keys,
1157 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance, 300));
1158 sip->our_publication_keys = g_slist_append(sip->our_publication_keys,
1159 g_strdup_printf("<%s><%u><%u>", "note", note_oof_instance, 400));
1161 /* calendarData:WorkingHours */
1162 sip->our_publication_keys = g_slist_append(sip->our_publication_keys,
1163 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 1));
1164 sip->our_publication_keys = g_slist_append(sip->our_publication_keys,
1165 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 100));
1166 sip->our_publication_keys = g_slist_append(sip->our_publication_keys,
1167 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 200));
1168 sip->our_publication_keys = g_slist_append(sip->our_publication_keys,
1169 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 300));
1170 sip->our_publication_keys = g_slist_append(sip->our_publication_keys,
1171 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 400));
1172 sip->our_publication_keys = g_slist_append(sip->our_publication_keys,
1173 g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 32000));
1175 /* calendarData:FreeBusy */
1176 sip->our_publication_keys = g_slist_append(sip->our_publication_keys,
1177 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 1));
1178 sip->our_publication_keys = g_slist_append(sip->our_publication_keys,
1179 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 100));
1180 sip->our_publication_keys = g_slist_append(sip->our_publication_keys,
1181 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 200));
1182 sip->our_publication_keys = g_slist_append(sip->our_publication_keys,
1183 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 300));
1184 sip->our_publication_keys = g_slist_append(sip->our_publication_keys,
1185 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 400));
1186 sip->our_publication_keys = g_slist_append(sip->our_publication_keys,
1187 g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 32000));
1189 //SIPE_DEBUG_INFO("sipe_is_our_publication: sip->our_publication_keys length=%d",
1190 // sip->our_publication_keys ? (int) g_slist_length(sip->our_publication_keys) : -1);
1193 //SIPE_DEBUG_INFO("sipe_is_our_publication: key=%s", key);
1195 entry = sip->our_publication_keys;
1196 while (entry) {
1197 //SIPE_DEBUG_INFO(" sipe_is_our_publication: entry->data=%s", entry->data);
1198 if (sipe_strequal(entry->data, key)) {
1199 return TRUE;
1201 entry = entry->next;
1203 return FALSE;
1206 /** Property names to store in blist.xml */
1207 #define ALIAS_PROP "alias"
1208 #define EMAIL_PROP "email"
1209 #define PHONE_PROP "phone"
1210 #define PHONE_DISPLAY_PROP "phone-display"
1211 #define PHONE_MOBILE_PROP "phone-mobile"
1212 #define PHONE_MOBILE_DISPLAY_PROP "phone-mobile-display"
1213 #define PHONE_HOME_PROP "phone-home"
1214 #define PHONE_HOME_DISPLAY_PROP "phone-home-display"
1215 #define PHONE_OTHER_PROP "phone-other"
1216 #define PHONE_OTHER_DISPLAY_PROP "phone-other-display"
1217 #define PHONE_CUSTOM1_PROP "phone-custom1"
1218 #define PHONE_CUSTOM1_DISPLAY_PROP "phone-custom1-display"
1219 #define SITE_PROP "site"
1220 #define COMPANY_PROP "company"
1221 #define DEPARTMENT_PROP "department"
1222 #define TITLE_PROP "title"
1223 #define OFFICE_PROP "office"
1224 /** implies work address */
1225 #define ADDRESS_STREET_PROP "address-street"
1226 #define ADDRESS_CITY_PROP "address-city"
1227 #define ADDRESS_STATE_PROP "address-state"
1228 #define ADDRESS_ZIPCODE_PROP "address-zipcode"
1229 #define ADDRESS_COUNTRYCODE_PROP "address-country-code"
1232 * Tries to figure out user first and last name
1233 * based on Display Name and email properties.
1235 * Allocates memory - must be g_free()'d
1237 * Examples to parse:
1238 * First Last
1239 * First Last - Company Name
1240 * Last, First
1241 * Last, First M.
1242 * Last, First (C)(STP) (Company)
1243 * first.last@company.com (preprocessed as "first last")
1244 * first.last.company.com@reuters.net (preprocessed as "first last company com")
1246 * Unusable examples:
1247 * user@company.com (preprocessed as "user")
1248 * first.m.last@company.com (preprocessed as "first m last")
1249 * user.company.com@reuters.net (preprocessed as "user company com")
1251 static void
1252 sipe_get_first_last_names(struct sipe_core_private *sipe_private,
1253 const char *uri,
1254 char **first_name,
1255 char **last_name)
1257 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
1258 sipe_backend_buddy p_buddy;
1259 char *display_name;
1260 gchar *email;
1261 const char *first, *last;
1262 char *tmp;
1263 char **parts;
1264 gboolean has_comma = FALSE;
1266 if (!sip || !uri) return;
1268 p_buddy = sipe_backend_buddy_find(SIPE_CORE_PUBLIC, uri, NULL);
1270 if (!p_buddy) return;
1272 display_name = sipe_backend_buddy_get_alias(SIPE_CORE_PUBLIC, p_buddy);
1273 email = sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC, p_buddy, SIPE_BUDDY_INFO_EMAIL);
1275 if (!display_name && !email) return;
1277 /* if no display name, make "first last anything_else" out of email */
1278 if (email && !display_name) {
1279 display_name = g_strndup(email, strstr(email, "@") - email);
1280 display_name = sipe_utils_str_replace((tmp = display_name), ".", " ");
1281 g_free(tmp);
1284 if (display_name) {
1285 has_comma = (strstr(display_name, ",") != NULL);
1286 display_name = sipe_utils_str_replace((tmp = display_name), ", ", " ");
1287 g_free(tmp);
1288 display_name = sipe_utils_str_replace((tmp = display_name), ",", " ");
1289 g_free(tmp);
1292 parts = g_strsplit(display_name, " ", 0);
1294 if (!parts[0] || !parts[1]) {
1295 g_free(email);
1296 g_free(display_name);
1297 g_strfreev(parts);
1298 return;
1301 if (has_comma) {
1302 last = parts[0];
1303 first = parts[1];
1304 } else {
1305 first = parts[0];
1306 last = parts[1];
1309 if (first_name) {
1310 *first_name = g_strstrip(g_strdup(first));
1313 if (last_name) {
1314 *last_name = g_strstrip(g_strdup(last));
1317 g_free(email);
1318 g_free(display_name);
1319 g_strfreev(parts);
1323 * Update user information
1325 * @param uri buddy SIP URI with 'sip:' prefix whose info we want to change.
1326 * @param property_name
1327 * @param property_value may be modified to strip white space
1329 static void
1330 sipe_update_user_info(struct sipe_core_private *sipe_private,
1331 const char *uri,
1332 sipe_buddy_info_fields propkey,
1333 char *property_value)
1335 GSList *buddies, *entry;
1337 if (property_value)
1338 property_value = g_strstrip(property_value);
1340 entry = buddies = sipe_backend_buddy_find_all(SIPE_CORE_PUBLIC, uri, NULL); /* all buddies in different groups */
1341 while (entry) {
1342 gchar *prop_str;
1343 gchar *server_alias;
1344 gchar *alias;
1345 sipe_backend_buddy p_buddy = entry->data;
1347 /* for Display Name */
1348 if (propkey == SIPE_BUDDY_INFO_DISPLAY_NAME) {
1349 alias = sipe_backend_buddy_get_alias(SIPE_CORE_PUBLIC, p_buddy);
1350 if (property_value && sipe_is_bad_alias(uri, alias)) {
1351 SIPE_DEBUG_INFO("Replacing alias for %s with %s", uri, property_value);
1352 sipe_backend_buddy_set_alias(SIPE_CORE_PUBLIC, p_buddy, property_value);
1354 g_free(alias);
1356 server_alias = sipe_backend_buddy_get_server_alias(SIPE_CORE_PUBLIC, p_buddy);
1357 if (!is_empty(property_value) &&
1358 (!sipe_strequal(property_value, server_alias) || is_empty(server_alias)) )
1360 SIPE_DEBUG_INFO("Replacing service alias for %s with %s", uri, property_value);
1361 sipe_backend_buddy_set_server_alias(SIPE_CORE_PUBLIC, p_buddy, property_value);
1363 g_free(server_alias);
1365 /* for other properties */
1366 else {
1367 if (!is_empty(property_value)) {
1368 prop_str = sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC, p_buddy, propkey);
1369 if (!prop_str || !sipe_strcase_equal(prop_str, property_value)) {
1370 sipe_backend_buddy_set_string(SIPE_CORE_PUBLIC, p_buddy, propkey, property_value);
1372 g_free(prop_str);
1376 entry = entry->next;
1378 g_slist_free(buddies);
1382 * Update user phone
1383 * Suitable for both 2005 and 2007 systems.
1385 * @param uri buddy SIP URI with 'sip:' prefix whose info we want to change.
1386 * @param phone_type
1387 * @param phone may be modified to strip white space
1388 * @param phone_display_string may be modified to strip white space
1390 static void
1391 sipe_update_user_phone(struct sipe_core_private *sipe_private,
1392 const gchar *uri,
1393 const gchar *phone_type,
1394 gchar *phone,
1395 gchar *phone_display_string)
1397 sipe_buddy_info_fields phone_node = SIPE_BUDDY_INFO_WORK_PHONE; /* work phone by default */
1398 sipe_buddy_info_fields phone_display_node = SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY; /* work phone by default */
1400 if(!phone || strlen(phone) == 0) return;
1402 if ((sipe_strequal(phone_type, "mobile") || sipe_strequal(phone_type, "cell"))) {
1403 phone_node = SIPE_BUDDY_INFO_MOBILE_PHONE;
1404 phone_display_node = SIPE_BUDDY_INFO_MOBILE_PHONE_DISPLAY;
1405 } else if (sipe_strequal(phone_type, "home")) {
1406 phone_node = SIPE_BUDDY_INFO_HOME_PHONE;
1407 phone_display_node = SIPE_BUDDY_INFO_HOME_PHONE_DISPLAY;
1408 } else if (sipe_strequal(phone_type, "other")) {
1409 phone_node = SIPE_BUDDY_INFO_OTHER_PHONE;
1410 phone_display_node = SIPE_BUDDY_INFO_OTHER_PHONE_DISPLAY;
1411 } else if (sipe_strequal(phone_type, "custom1")) {
1412 phone_node = SIPE_BUDDY_INFO_CUSTOM1_PHONE;
1413 phone_display_node = SIPE_BUDDY_INFO_CUSTOM1_PHONE_DISPLAY;
1416 sipe_update_user_info(sipe_private, uri, phone_node, phone);
1417 if (phone_display_string) {
1418 sipe_update_user_info(sipe_private, uri, phone_display_node, phone_display_string);
1422 void
1423 sipe_core_update_calendar(struct sipe_core_public *sipe_public)
1425 SIPE_DEBUG_INFO_NOFORMAT("sipe_core_update_calendar: started.");
1427 /* Do in parallel.
1428 * If failed, the branch will be disabled for subsequent calls.
1429 * Can't rely that user turned the functionality on in account settings.
1431 sipe_ews_update_calendar(SIPE_CORE_PRIVATE);
1432 #ifdef _WIN32
1433 /* @TODO: UNIX integration missing */
1434 sipe_domino_update_calendar(SIPE_CORE_PRIVATE);
1435 #endif
1437 /* schedule repeat */
1438 sipe_schedule_seconds(SIPE_CORE_PRIVATE,
1439 "<+update-calendar>",
1440 NULL,
1441 UPDATE_CALENDAR_INTERVAL,
1442 (sipe_schedule_action)sipe_core_update_calendar,
1443 NULL);
1445 SIPE_DEBUG_INFO_NOFORMAT("sipe_core_update_calendar: finished.");
1449 * This method motivates Purple's Host (e.g. Pidgin) to update its UI
1450 * by using standard Purple's means of signals and saved statuses.
1452 * Thus all UI elements get updated: Status Button with Note, docklet.
1453 * This is ablolutely important as both our status and note can come
1454 * inbound (roaming) or be updated programmatically (e.g. based on our
1455 * calendar data).
1457 static void
1458 sipe_set_purple_account_status_and_note(const PurpleAccount *account,
1459 const char *status_id,
1460 const char *message,
1461 time_t do_not_publish[])
1463 PurpleStatus *status = purple_account_get_active_status(account);
1464 gboolean changed = TRUE;
1466 if (g_str_equal(status_id, purple_status_get_id(status)) &&
1467 sipe_strequal(message, purple_status_get_attr_string(status, SIPE_STATUS_ATTR_ID_MESSAGE)))
1469 changed = FALSE;
1472 if (purple_savedstatus_is_idleaway()) {
1473 changed = FALSE;
1476 if (changed) {
1477 PurpleSavedStatus *saved_status;
1478 const PurpleStatusType *acct_status_type =
1479 purple_status_type_find_with_id(account->status_types, status_id);
1480 PurpleStatusPrimitive primitive = purple_status_type_get_primitive(acct_status_type);
1481 sipe_activity activity = sipe_get_activity_by_token(status_id);
1483 saved_status = purple_savedstatus_find_transient_by_type_and_message(primitive, message);
1484 if (saved_status) {
1485 purple_savedstatus_set_substatus(saved_status, account, acct_status_type, message);
1488 /* If this type+message is unique then create a new transient saved status
1489 * Ref: gtkstatusbox.c
1491 if (!saved_status) {
1492 GList *tmp;
1493 GList *active_accts = purple_accounts_get_all_active();
1495 saved_status = purple_savedstatus_new(NULL, primitive);
1496 purple_savedstatus_set_message(saved_status, message);
1498 for (tmp = active_accts; tmp != NULL; tmp = tmp->next) {
1499 purple_savedstatus_set_substatus(saved_status,
1500 (PurpleAccount *)tmp->data, acct_status_type, message);
1502 g_list_free(active_accts);
1505 do_not_publish[activity] = time(NULL);
1506 SIPE_DEBUG_INFO("sipe_set_purple_account_status_and_note: do_not_publish[%s]=%d [now]",
1507 status_id, (int)do_not_publish[activity]);
1509 /* Set the status for each account */
1510 purple_savedstatus_activate(saved_status);
1514 struct hash_table_delete_payload {
1515 GHashTable *hash_table;
1516 guint container;
1519 static void
1520 sipe_remove_category_container_publications_cb(const char *name,
1521 struct sipe_publication *publication,
1522 struct hash_table_delete_payload *payload)
1524 if (publication->container == payload->container) {
1525 g_hash_table_remove(payload->hash_table, name);
1528 static void
1529 sipe_remove_category_container_publications(GHashTable *our_publications,
1530 const char *category,
1531 guint container)
1533 struct hash_table_delete_payload payload;
1534 payload.hash_table = g_hash_table_lookup(our_publications, category);
1536 if (!payload.hash_table) return;
1538 payload.container = container;
1539 g_hash_table_foreach(payload.hash_table, (GHFunc)sipe_remove_category_container_publications_cb, &payload);
1542 static void
1543 send_publish_category_initial(struct sipe_core_private *sipe_private);
1546 * When we receive some self (BE) NOTIFY with a new subscriber
1547 * we sends a setSubscribers request to him [SIP-PRES] 4.8
1550 void sipe_process_roaming_self(struct sipe_core_private *sipe_private,
1551 struct sipmsg *msg)
1553 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
1554 gchar *contact;
1555 gchar *to;
1556 sipe_xml *xml;
1557 const sipe_xml *node;
1558 const sipe_xml *node2;
1559 char *display_name = NULL;
1560 char *uri;
1561 GSList *category_names = NULL;
1562 int aggreg_avail = 0;
1563 gboolean do_update_status = FALSE;
1564 gboolean has_note_cleaned = FALSE;
1565 GHashTable *devices;
1567 SIPE_DEBUG_INFO_NOFORMAT("sipe_process_roaming_self");
1569 xml = sipe_xml_parse(msg->body, msg->bodylen);
1570 if (!xml) return;
1572 contact = get_contact(sipe_private);
1573 to = sip_uri_self(sipe_private);
1575 /* categories */
1576 /* set list of categories participating in this XML */
1577 for (node = sipe_xml_child(xml, "categories/category"); node; node = sipe_xml_twin(node)) {
1578 const gchar *name = sipe_xml_attribute(node, "name");
1579 category_names = slist_insert_unique_sorted(category_names, (gchar *)name, (GCompareFunc)strcmp);
1581 SIPE_DEBUG_INFO("sipe_process_roaming_self: category_names length=%d",
1582 category_names ? (int) g_slist_length(category_names) : -1);
1583 /* drop category information */
1584 if (category_names) {
1585 GSList *entry = category_names;
1586 while (entry) {
1587 GHashTable *cat_publications;
1588 const gchar *category = entry->data;
1589 entry = entry->next;
1590 SIPE_DEBUG_INFO("sipe_process_roaming_self: dropping category: %s", category);
1591 cat_publications = g_hash_table_lookup(sip->our_publications, category);
1592 if (cat_publications) {
1593 g_hash_table_remove(sip->our_publications, category);
1594 SIPE_DEBUG_INFO("sipe_process_roaming_self: dropped category: %s", category);
1598 g_slist_free(category_names);
1600 /* filling our categories reflected in roaming data */
1601 devices = g_hash_table_new_full(g_str_hash, g_str_equal,
1602 g_free, NULL);
1603 for (node = sipe_xml_child(xml, "categories/category"); node; node = sipe_xml_twin(node)) {
1604 const char *tmp;
1605 const gchar *name = sipe_xml_attribute(node, "name");
1606 guint container = sipe_xml_int_attribute(node, "container", -1);
1607 guint instance = sipe_xml_int_attribute(node, "instance", -1);
1608 guint version = sipe_xml_int_attribute(node, "version", 0);
1609 time_t publish_time = (tmp = sipe_xml_attribute(node, "publishTime")) ?
1610 sipe_utils_str_to_time(tmp) : 0;
1611 gchar *key;
1612 GHashTable *cat_publications = g_hash_table_lookup(sip->our_publications, name);
1614 /* Ex. clear note: <category name="note"/> */
1615 if (container == (guint)-1) {
1616 g_free(sip->note);
1617 sip->note = NULL;
1618 do_update_status = TRUE;
1619 continue;
1622 /* Ex. clear note: <category name="note" container="200"/> */
1623 if (instance == (guint)-1) {
1624 if (container == 200) {
1625 g_free(sip->note);
1626 sip->note = NULL;
1627 do_update_status = TRUE;
1629 SIPE_DEBUG_INFO("sipe_process_roaming_self: removing publications for: %s/%u", name, container);
1630 sipe_remove_category_container_publications(
1631 sip->our_publications, name, container);
1632 continue;
1635 /* key is <category><instance><container> */
1636 key = g_strdup_printf("<%s><%u><%u>", name, instance, container);
1637 SIPE_DEBUG_INFO("sipe_process_roaming_self: key=%s version=%d", key, version);
1639 /* capture all userState publication for later clean up if required */
1640 if (sipe_strequal(name, "state") && (container == 2 || container == 3)) {
1641 const sipe_xml *xn_state = sipe_xml_child(node, "state");
1643 if (xn_state && sipe_strequal(sipe_xml_attribute(xn_state, "type"), "userState")) {
1644 struct sipe_publication *publication = g_new0(struct sipe_publication, 1);
1645 publication->category = g_strdup(name);
1646 publication->instance = instance;
1647 publication->container = container;
1648 publication->version = version;
1650 if (!sip->user_state_publications) {
1651 sip->user_state_publications = g_hash_table_new_full(
1652 g_str_hash, g_str_equal,
1653 g_free, (GDestroyNotify)free_publication);
1655 g_hash_table_insert(sip->user_state_publications, g_strdup(key), publication);
1656 SIPE_DEBUG_INFO("sipe_process_roaming_self: added to user_state_publications key=%s version=%d",
1657 key, version);
1661 /* count each client instance only once */
1662 if (sipe_strequal(name, "device"))
1663 g_hash_table_replace(devices, g_strdup_printf("%u", instance), NULL);
1665 if (sipe_is_our_publication(sipe_private, key)) {
1666 struct sipe_publication *publication = g_new0(struct sipe_publication, 1);
1668 publication->category = g_strdup(name);
1669 publication->instance = instance;
1670 publication->container = container;
1671 publication->version = version;
1673 /* filling publication->availability */
1674 if (sipe_strequal(name, "state")) {
1675 const sipe_xml *xn_state = sipe_xml_child(node, "state");
1676 const sipe_xml *xn_avail = sipe_xml_child(xn_state, "availability");
1678 if (xn_avail) {
1679 gchar *avail_str = sipe_xml_data(xn_avail);
1680 if (avail_str) {
1681 publication->availability = atoi(avail_str);
1683 g_free(avail_str);
1685 /* for calendarState */
1686 if (xn_state && sipe_strequal(sipe_xml_attribute(xn_state, "type"), "calendarState")) {
1687 const sipe_xml *xn_activity = sipe_xml_child(xn_state, "activity");
1688 struct sipe_cal_event *event = g_new0(struct sipe_cal_event, 1);
1690 event->start_time = sipe_utils_str_to_time(sipe_xml_attribute(xn_state, "startTime"));
1691 if (xn_activity) {
1692 if (sipe_strequal(sipe_xml_attribute(xn_activity, "token"),
1693 sipe_activity_map[SIPE_ACTIVITY_IN_MEETING].token))
1695 event->is_meeting = TRUE;
1698 event->subject = sipe_xml_data(sipe_xml_child(xn_state, "meetingSubject"));
1699 event->location = sipe_xml_data(sipe_xml_child(xn_state, "meetingLocation"));
1701 publication->cal_event_hash = sipe_cal_event_hash(event);
1702 SIPE_DEBUG_INFO("sipe_process_roaming_self: hash=%s",
1703 publication->cal_event_hash);
1704 sipe_cal_event_free(event);
1707 /* filling publication->note */
1708 if (sipe_strequal(name, "note")) {
1709 const sipe_xml *xn_body = sipe_xml_child(node, "note/body");
1711 if (!has_note_cleaned) {
1712 has_note_cleaned = TRUE;
1714 g_free(sip->note);
1715 sip->note = NULL;
1716 sip->note_since = publish_time;
1718 do_update_status = TRUE;
1721 g_free(publication->note);
1722 publication->note = NULL;
1723 if (xn_body) {
1724 char *tmp;
1726 publication->note = g_markup_escape_text((tmp = sipe_xml_data(xn_body)), -1);
1727 g_free(tmp);
1728 if (publish_time >= sip->note_since) {
1729 g_free(sip->note);
1730 sip->note = g_strdup(publication->note);
1731 sip->note_since = publish_time;
1732 sip->is_oof_note = sipe_strequal(sipe_xml_attribute(xn_body, "type"), "OOF");
1734 do_update_status = TRUE;
1739 /* filling publication->fb_start_str, free_busy_base64, working_hours_xml_str */
1740 if (sipe_strequal(name, "calendarData") && (publication->container == 300)) {
1741 const sipe_xml *xn_free_busy = sipe_xml_child(node, "calendarData/freeBusy");
1742 const sipe_xml *xn_working_hours = sipe_xml_child(node, "calendarData/WorkingHours");
1743 if (xn_free_busy) {
1744 publication->fb_start_str = g_strdup(sipe_xml_attribute(xn_free_busy, "startTime"));
1745 publication->free_busy_base64 = sipe_xml_data(xn_free_busy);
1747 if (xn_working_hours) {
1748 publication->working_hours_xml_str = sipe_xml_stringify(xn_working_hours);
1752 if (!cat_publications) {
1753 cat_publications = g_hash_table_new_full(
1754 g_str_hash, g_str_equal,
1755 g_free, (GDestroyNotify)free_publication);
1756 g_hash_table_insert(sip->our_publications, g_strdup(name), cat_publications);
1757 SIPE_DEBUG_INFO("sipe_process_roaming_self: added GHashTable cat=%s", name);
1759 g_hash_table_insert(cat_publications, g_strdup(key), publication);
1760 SIPE_DEBUG_INFO("sipe_process_roaming_self: added key=%s version=%d", key, version);
1762 g_free(key);
1764 /* aggregateState (not an our publication) from 2-nd container */
1765 if (sipe_strequal(name, "state") && container == 2) {
1766 const sipe_xml *xn_state = sipe_xml_child(node, "state");
1768 if (xn_state && sipe_strequal(sipe_xml_attribute(xn_state, "type"), "aggregateState")) {
1769 const sipe_xml *xn_avail = sipe_xml_child(xn_state, "availability");
1771 if (xn_avail) {
1772 gchar *avail_str = sipe_xml_data(xn_avail);
1773 if (avail_str) {
1774 aggreg_avail = atoi(avail_str);
1776 g_free(avail_str);
1779 do_update_status = TRUE;
1783 /* userProperties published by server from AD */
1784 if (!sip->csta && sipe_strequal(name, "userProperties")) {
1785 const sipe_xml *line;
1786 /* line, for Remote Call Control (RCC) */
1787 for (line = sipe_xml_child(node, "userProperties/lines/line"); line; line = sipe_xml_twin(line)) {
1788 const gchar *line_server = sipe_xml_attribute(line, "lineServer");
1789 const gchar *line_type = sipe_xml_attribute(line, "lineType");
1790 gchar *line_uri;
1792 if (!line_server || !(sipe_strequal(line_type, "Rcc") || sipe_strequal(line_type, "Dual"))) continue;
1794 line_uri = sipe_xml_data(line);
1795 if (line_uri) {
1796 SIPE_DEBUG_INFO("sipe_process_roaming_self: line_uri=%s server=%s", line_uri, line_server);
1797 sip_csta_open(sipe_private, line_uri, line_server);
1799 g_free(line_uri);
1801 break;
1805 SIPE_DEBUG_INFO("sipe_process_roaming_self: sip->our_publications size=%d",
1806 sip->our_publications ? (int) g_hash_table_size(sip->our_publications) : -1);
1808 /* active clients for user account */
1809 if (g_hash_table_size(devices) > 1) {
1810 SIPE_CORE_PRIVATE_FLAG_SET(MPOP);
1811 SIPE_DEBUG_INFO("sipe_process_roaming_self: multiple clients detected (%d)",
1812 g_hash_table_size(devices));
1813 } else {
1814 SIPE_CORE_PRIVATE_FLAG_UNSET(MPOP);
1815 SIPE_DEBUG_INFO_NOFORMAT("sipe_process_roaming_self: single client detected");
1817 g_hash_table_destroy(devices);
1819 /* containers */
1820 for (node = sipe_xml_child(xml, "containers/container"); node; node = sipe_xml_twin(node)) {
1821 guint id = sipe_xml_int_attribute(node, "id", 0);
1822 struct sipe_container *container = sipe_find_container(sipe_private, id);
1824 if (container) {
1825 sip->containers = g_slist_remove(sip->containers, container);
1826 SIPE_DEBUG_INFO("sipe_process_roaming_self: removed existing container id=%d v%d", container->id, container->version);
1827 free_container(container);
1829 container = g_new0(struct sipe_container, 1);
1830 container->id = id;
1831 container->version = sipe_xml_int_attribute(node, "version", 0);
1832 sip->containers = g_slist_append(sip->containers, container);
1833 SIPE_DEBUG_INFO("sipe_process_roaming_self: added container id=%d v%d", container->id, container->version);
1835 for (node2 = sipe_xml_child(node, "member"); node2; node2 = sipe_xml_twin(node2)) {
1836 struct sipe_container_member *member = g_new0(struct sipe_container_member, 1);
1837 member->type = g_strdup(sipe_xml_attribute(node2, "type"));
1838 member->value = g_strdup(sipe_xml_attribute(node2, "value"));
1839 container->members = g_slist_append(container->members, member);
1840 SIPE_DEBUG_INFO("sipe_process_roaming_self: added container member type=%s value=%s",
1841 member->type, member->value ? member->value : "");
1845 SIPE_DEBUG_INFO("sipe_process_roaming_self: sip->access_level_set=%s", sip->access_level_set ? "TRUE" : "FALSE");
1846 if (!sip->access_level_set && sipe_xml_child(xml, "containers")) {
1847 char *container_xmls = NULL;
1848 int sameEnterpriseAL = sipe_find_access_level(sipe_private, "sameEnterprise", NULL, NULL);
1849 int federatedAL = sipe_find_access_level(sipe_private, "federated", NULL, NULL);
1851 SIPE_DEBUG_INFO("sipe_process_roaming_self: sameEnterpriseAL=%d", sameEnterpriseAL);
1852 SIPE_DEBUG_INFO("sipe_process_roaming_self: federatedAL=%d", federatedAL);
1853 /* initial set-up to let counterparties see your status */
1854 if (sameEnterpriseAL < 0) {
1855 struct sipe_container *container = sipe_find_container(sipe_private, 200);
1856 guint version = container ? container->version : 0;
1857 sipe_send_container_members_prepare(200, version, "add", "sameEnterprise", NULL, &container_xmls);
1859 if (federatedAL < 0) {
1860 struct sipe_container *container = sipe_find_container(sipe_private, 100);
1861 guint version = container ? container->version : 0;
1862 sipe_send_container_members_prepare(100, version, "add", "federated", NULL, &container_xmls);
1864 sip->access_level_set = TRUE;
1866 if (container_xmls) {
1867 sipe_send_set_container_members(sipe_private, container_xmls);
1869 g_free(container_xmls);
1872 /* Refresh contacts' blocked status */
1873 sipe_refresh_blocked_status(sipe_private);
1875 /* subscribers */
1876 for (node = sipe_xml_child(xml, "subscribers/subscriber"); node; node = sipe_xml_twin(node)) {
1877 const char *user;
1878 const char *acknowledged;
1879 gchar *hdr;
1880 gchar *body;
1882 user = sipe_xml_attribute(node, "user"); /* without 'sip:' prefix */
1883 if (!user) continue;
1884 SIPE_DEBUG_INFO("sipe_process_roaming_self: user %s", user);
1885 display_name = g_strdup(sipe_xml_attribute(node, "displayName"));
1886 uri = sip_uri_from_name(user);
1888 sipe_update_user_info(sipe_private, uri, SIPE_BUDDY_INFO_DISPLAY_NAME, display_name);
1890 acknowledged= sipe_xml_attribute(node, "acknowledged");
1891 if(sipe_strcase_equal(acknowledged,"false")){
1892 SIPE_DEBUG_INFO("sipe_process_roaming_self: user added you %s", user);
1893 if (!sipe_backend_buddy_find(SIPE_CORE_PUBLIC, uri, NULL)) {
1894 sipe_backend_buddy_request_add(SIPE_CORE_PUBLIC, uri, display_name);
1897 hdr = g_strdup_printf(
1898 "Contact: %s\r\n"
1899 "Content-Type: application/msrtc-presence-setsubscriber+xml\r\n", contact);
1901 body = g_strdup_printf(
1902 "<setSubscribers xmlns=\"http://schemas.microsoft.com/2006/09/sip/presence-subscribers\">"
1903 "<subscriber user=\"%s\" acknowledged=\"true\"/>"
1904 "</setSubscribers>", user);
1906 sip_transport_service(sipe_private,
1908 hdr,
1909 body,
1910 NULL);
1911 g_free(body);
1912 g_free(hdr);
1914 g_free(display_name);
1915 g_free(uri);
1918 g_free(contact);
1919 sipe_xml_free(xml);
1921 /* Publish initial state if not yet.
1922 * Assuming this happens on initial responce to subscription to roaming-self
1923 * so we've already updated our roaming data in full.
1924 * Only for 2007+
1926 if (!sip->initial_state_published) {
1927 send_publish_category_initial(sipe_private);
1928 sipe_groupchat_init(sipe_private);
1929 sip->initial_state_published = TRUE;
1930 /* dalayed run */
1931 sipe_schedule_seconds(sipe_private,
1932 "<+update-calendar>",
1933 NULL,
1934 UPDATE_CALENDAR_DELAY,
1935 (sipe_schedule_action)sipe_core_update_calendar,
1936 NULL);
1937 do_update_status = FALSE;
1938 } else if (aggreg_avail) {
1940 g_free(sip->status);
1941 if (aggreg_avail && aggreg_avail < 18000) { /* not offline */
1942 sip->status = g_strdup(sipe_get_status_by_availability(aggreg_avail, NULL));
1943 } else {
1944 sip->status = g_strdup(SIPE_STATUS_ID_INVISIBLE); /* not not let offline status switch us off */
1948 if (do_update_status) {
1949 SIPE_DEBUG_INFO("sipe_process_roaming_self: switch to '%s' for the account", sip->status);
1950 sipe_set_purple_account_status_and_note(sip->account, sip->status, sip->note, sip->do_not_publish);
1953 g_free(to);
1956 /* IM Session (INVITE and MESSAGE methods) */
1958 static gboolean
1959 process_options_response(SIPE_UNUSED_PARAMETER struct sipe_core_private *sipe_private,
1960 struct sipmsg *msg,
1961 SIPE_UNUSED_PARAMETER struct transaction *trans)
1963 gboolean ret = TRUE;
1965 if (msg->response != 200) {
1966 SIPE_DEBUG_INFO("process_options_response: OPTIONS response is %d", msg->response);
1967 return FALSE;
1970 SIPE_DEBUG_INFO("process_options_response: body:\n%s", msg->body ? msg->body : "");
1972 return ret;
1976 * Asks UA/proxy about its capabilities.
1978 static void sipe_options_request(struct sipe_core_private *sipe_private,
1979 const char *who)
1981 gchar *to = sip_uri(who);
1982 gchar *contact = get_contact(sipe_private);
1983 gchar *request = g_strdup_printf(
1984 "Accept: application/sdp\r\n"
1985 "Contact: %s\r\n", contact);
1986 g_free(contact);
1988 sip_transport_request(sipe_private,
1989 "OPTIONS",
1992 request,
1993 NULL,
1994 NULL,
1995 process_options_response);
1997 g_free(to);
1998 g_free(request);
2001 void
2002 sipe_convo_closed(PurpleConnection * gc, const char *who)
2004 struct sipe_core_private *sipe_private = PURPLE_GC_TO_SIPE_CORE_PRIVATE;
2006 SIPE_DEBUG_INFO("conversation with %s closed", who);
2007 sipe_session_close(sipe_private,
2008 sipe_session_find_im(sipe_private, who));
2012 * Returns 2005-style activity and Availability.
2014 * @param status Sipe statis id.
2016 static void
2017 sipe_get_act_avail_by_status_2005(const char *status,
2018 int *activity,
2019 int *availability)
2021 int avail = 300; /* online */
2022 int act = 400; /* Available */
2024 if (sipe_strequal(status, SIPE_STATUS_ID_AWAY)) {
2025 act = 100;
2026 //} else if (sipe_strequal(status, SIPE_STATUS_ID_LUNCH)) {
2027 // act = 150;
2028 } else if (sipe_strequal(status, SIPE_STATUS_ID_BRB)) {
2029 act = 300;
2030 } else if (sipe_strequal(status, SIPE_STATUS_ID_AVAILABLE)) {
2031 act = 400;
2032 //} else if (sipe_strequal(status, SIPE_STATUS_ID_ON_PHONE)) {
2033 // act = 500;
2034 } else if (sipe_strequal(status, SIPE_STATUS_ID_BUSY) ||
2035 sipe_strequal(status, SIPE_STATUS_ID_DND)) {
2036 act = 600;
2037 } else if (sipe_strequal(status, SIPE_STATUS_ID_INVISIBLE) ||
2038 sipe_strequal(status, SIPE_STATUS_ID_OFFLINE)) {
2039 avail = 0; /* offline */
2040 act = 100;
2041 } else {
2042 act = 400; /* Available */
2045 if (activity) *activity = act;
2046 if (availability) *availability = avail;
2050 * [MS-SIP] 2.2.1
2052 * @param activity 2005 aggregated activity. Ex.: 600
2053 * @param availablity 2005 aggregated availablity. Ex.: 300
2055 static const char *
2056 sipe_get_status_by_act_avail_2005(const int activity,
2057 const int availablity,
2058 char **activity_desc)
2060 const char *status_id = NULL;
2061 const char *act = NULL;
2063 if (activity < 150) {
2064 status_id = SIPE_STATUS_ID_AWAY;
2065 } else if (activity < 200) {
2066 //status_id = SIPE_STATUS_ID_LUNCH;
2067 status_id = SIPE_STATUS_ID_AWAY;
2068 act = SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_LUNCH);
2069 } else if (activity < 300) {
2070 //status_id = SIPE_STATUS_ID_IDLE;
2071 status_id = SIPE_STATUS_ID_AWAY;
2072 act = SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_INACTIVE);
2073 } else if (activity < 400) {
2074 status_id = SIPE_STATUS_ID_BRB;
2075 } else if (activity < 500) {
2076 status_id = SIPE_STATUS_ID_AVAILABLE;
2077 } else if (activity < 600) {
2078 //status_id = SIPE_STATUS_ID_ON_PHONE;
2079 status_id = SIPE_STATUS_ID_BUSY;
2080 act = SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_ON_PHONE);
2081 } else if (activity < 700) {
2082 status_id = SIPE_STATUS_ID_BUSY;
2083 } else if (activity < 800) {
2084 status_id = SIPE_STATUS_ID_AWAY;
2085 } else {
2086 status_id = SIPE_STATUS_ID_AVAILABLE;
2089 if (availablity < 100)
2090 status_id = SIPE_STATUS_ID_OFFLINE;
2092 if (activity_desc && act) {
2093 g_free(*activity_desc);
2094 *activity_desc = g_strdup(act);
2097 return status_id;
2101 * [MS-PRES] Table 3: Conversion of legacyInterop elements and attributes to MSRTC elements and attributes.
2103 static const char*
2104 sipe_get_status_by_availability(int avail,
2105 char** activity_desc)
2107 const char *status;
2108 const char *act = NULL;
2110 if (avail < 3000) {
2111 status = SIPE_STATUS_ID_OFFLINE;
2112 } else if (avail < 4500) {
2113 status = SIPE_STATUS_ID_AVAILABLE;
2114 } else if (avail < 6000) {
2115 //status = SIPE_STATUS_ID_IDLE;
2116 status = SIPE_STATUS_ID_AVAILABLE;
2117 act = SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_INACTIVE);
2118 } else if (avail < 7500) {
2119 status = SIPE_STATUS_ID_BUSY;
2120 } else if (avail < 9000) {
2121 //status = SIPE_STATUS_ID_BUSYIDLE;
2122 status = SIPE_STATUS_ID_BUSY;
2123 act = SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_BUSYIDLE);
2124 } else if (avail < 12000) {
2125 status = SIPE_STATUS_ID_DND;
2126 } else if (avail < 15000) {
2127 status = SIPE_STATUS_ID_BRB;
2128 } else if (avail < 18000) {
2129 status = SIPE_STATUS_ID_AWAY;
2130 } else {
2131 status = SIPE_STATUS_ID_OFFLINE;
2134 if (activity_desc && act) {
2135 g_free(*activity_desc);
2136 *activity_desc = g_strdup(act);
2139 return status;
2143 * Returns 2007-style availability value
2145 * @param sipe_status_id (in)
2146 * @param activity_token (out) Must be g_free()'d after use if consumed.
2148 static int
2149 sipe_get_availability_by_status(const char* sipe_status_id, char** activity_token)
2151 int availability;
2152 sipe_activity activity = SIPE_ACTIVITY_UNSET;
2154 if (sipe_strequal(sipe_status_id, SIPE_STATUS_ID_AWAY)) {
2155 availability = 15500;
2156 if (!activity_token || !(*activity_token)) {
2157 activity = SIPE_ACTIVITY_AWAY;
2159 } else if (sipe_strequal(sipe_status_id, SIPE_STATUS_ID_BRB)) {
2160 availability = 12500;
2161 activity = SIPE_ACTIVITY_BRB;
2162 } else if (sipe_strequal(sipe_status_id, SIPE_STATUS_ID_DND)) {
2163 availability = 9500;
2164 activity = SIPE_ACTIVITY_DND;
2165 } else if (sipe_strequal(sipe_status_id, SIPE_STATUS_ID_BUSY)) {
2166 availability = 6500;
2167 if (!activity_token || !(*activity_token)) {
2168 activity = SIPE_ACTIVITY_BUSY;
2170 } else if (sipe_strequal(sipe_status_id, SIPE_STATUS_ID_AVAILABLE)) {
2171 availability = 3500;
2172 activity = SIPE_ACTIVITY_ONLINE;
2173 } else if (sipe_strequal(sipe_status_id, SIPE_STATUS_ID_UNKNOWN)) {
2174 availability = 0;
2175 } else {
2176 // Offline or invisible
2177 availability = 18500;
2178 activity = SIPE_ACTIVITY_OFFLINE;
2181 if (activity_token) {
2182 *activity_token = g_strdup(sipe_activity_map[activity].token);
2184 return availability;
2187 void process_incoming_notify_rlmi(struct sipe_core_private *sipe_private,
2188 const gchar *data,
2189 unsigned len)
2191 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
2192 const char *uri;
2193 sipe_xml *xn_categories;
2194 const sipe_xml *xn_category;
2195 const char *status = NULL;
2196 gboolean do_update_status = FALSE;
2197 gboolean has_note_cleaned = FALSE;
2198 gboolean has_free_busy_cleaned = FALSE;
2200 xn_categories = sipe_xml_parse(data, len);
2201 uri = sipe_xml_attribute(xn_categories, "uri"); /* with 'sip:' prefix */
2203 for (xn_category = sipe_xml_child(xn_categories, "category");
2204 xn_category ;
2205 xn_category = sipe_xml_twin(xn_category) )
2207 const sipe_xml *xn_node;
2208 const char *tmp;
2209 const char *attrVar = sipe_xml_attribute(xn_category, "name");
2210 time_t publish_time = (tmp = sipe_xml_attribute(xn_category, "publishTime")) ?
2211 sipe_utils_str_to_time(tmp) : 0;
2213 /* contactCard */
2214 if (sipe_strequal(attrVar, "contactCard"))
2216 const sipe_xml *card = sipe_xml_child(xn_category, "contactCard");
2218 if (card) {
2219 const sipe_xml *node;
2220 /* identity - Display Name and email */
2221 node = sipe_xml_child(card, "identity");
2222 if (node) {
2223 char* display_name = sipe_xml_data(
2224 sipe_xml_child(node, "name/displayName"));
2225 char* email = sipe_xml_data(
2226 sipe_xml_child(node, "email"));
2228 sipe_update_user_info(sipe_private, uri, SIPE_BUDDY_INFO_DISPLAY_NAME, display_name);
2229 sipe_update_user_info(sipe_private, uri, SIPE_BUDDY_INFO_EMAIL, email);
2231 g_free(display_name);
2232 g_free(email);
2234 /* company */
2235 node = sipe_xml_child(card, "company");
2236 if (node) {
2237 char* company = sipe_xml_data(node);
2238 sipe_update_user_info(sipe_private, uri, SIPE_BUDDY_INFO_COMPANY, company);
2239 g_free(company);
2241 /* department */
2242 node = sipe_xml_child(card, "department");
2243 if (node) {
2244 char* department = sipe_xml_data(node);
2245 sipe_update_user_info(sipe_private, uri, SIPE_BUDDY_INFO_DEPARTMENT, department);
2246 g_free(department);
2248 /* title */
2249 node = sipe_xml_child(card, "title");
2250 if (node) {
2251 char* title = sipe_xml_data(node);
2252 sipe_update_user_info(sipe_private, uri, SIPE_BUDDY_INFO_JOB_TITLE, title);
2253 g_free(title);
2255 /* office */
2256 node = sipe_xml_child(card, "office");
2257 if (node) {
2258 char* office = sipe_xml_data(node);
2259 sipe_update_user_info(sipe_private, uri, SIPE_BUDDY_INFO_OFFICE, office);
2260 g_free(office);
2262 /* site (url) */
2263 node = sipe_xml_child(card, "url");
2264 if (node) {
2265 char* site = sipe_xml_data(node);
2266 sipe_update_user_info(sipe_private, uri, SIPE_BUDDY_INFO_SITE, site);
2267 g_free(site);
2269 /* phone */
2270 for (node = sipe_xml_child(card, "phone");
2271 node;
2272 node = sipe_xml_twin(node))
2274 const char *phone_type = sipe_xml_attribute(node, "type");
2275 char* phone = sipe_xml_data(sipe_xml_child(node, "uri"));
2276 char* phone_display_string = sipe_xml_data(sipe_xml_child(node, "displayString"));
2278 sipe_update_user_phone(sipe_private, uri, phone_type, phone, phone_display_string);
2280 g_free(phone);
2281 g_free(phone_display_string);
2283 /* address */
2284 for (node = sipe_xml_child(card, "address");
2285 node;
2286 node = sipe_xml_twin(node))
2288 if (sipe_strequal(sipe_xml_attribute(node, "type"), "work")) {
2289 char* street = sipe_xml_data(sipe_xml_child(node, "street"));
2290 char* city = sipe_xml_data(sipe_xml_child(node, "city"));
2291 char* state = sipe_xml_data(sipe_xml_child(node, "state"));
2292 char* zipcode = sipe_xml_data(sipe_xml_child(node, "zipcode"));
2293 char* country_code = sipe_xml_data(sipe_xml_child(node, "countryCode"));
2295 sipe_update_user_info(sipe_private, uri, SIPE_BUDDY_INFO_STREET, street);
2296 sipe_update_user_info(sipe_private, uri, SIPE_BUDDY_INFO_CITY, city);
2297 sipe_update_user_info(sipe_private, uri, SIPE_BUDDY_INFO_STATE, state);
2298 sipe_update_user_info(sipe_private, uri, SIPE_BUDDY_INFO_ZIPCODE, zipcode);
2299 sipe_update_user_info(sipe_private, uri, SIPE_BUDDY_INFO_COUNTRY, country_code);
2301 g_free(street);
2302 g_free(city);
2303 g_free(state);
2304 g_free(zipcode);
2305 g_free(country_code);
2307 break;
2312 /* note */
2313 else if (sipe_strequal(attrVar, "note"))
2315 if (uri) {
2316 struct sipe_buddy *sbuddy = g_hash_table_lookup(sipe_private->buddies, uri);
2318 if (!has_note_cleaned) {
2319 has_note_cleaned = TRUE;
2321 g_free(sbuddy->note);
2322 sbuddy->note = NULL;
2323 sbuddy->is_oof_note = FALSE;
2324 sbuddy->note_since = publish_time;
2326 do_update_status = TRUE;
2328 if (sbuddy && (publish_time >= sbuddy->note_since)) {
2329 /* clean up in case no 'note' element is supplied
2330 * which indicate note removal in client
2332 g_free(sbuddy->note);
2333 sbuddy->note = NULL;
2334 sbuddy->is_oof_note = FALSE;
2335 sbuddy->note_since = publish_time;
2337 xn_node = sipe_xml_child(xn_category, "note/body");
2338 if (xn_node) {
2339 char *tmp;
2340 sbuddy->note = g_markup_escape_text((tmp = sipe_xml_data(xn_node)), -1);
2341 g_free(tmp);
2342 sbuddy->is_oof_note = sipe_strequal(sipe_xml_attribute(xn_node, "type"), "OOF");
2343 sbuddy->note_since = publish_time;
2345 SIPE_DEBUG_INFO("process_incoming_notify_rlmi: uri(%s), note(%s)",
2346 uri, sbuddy->note ? sbuddy->note : "");
2348 /* to trigger UI refresh in case no status info is supplied in this update */
2349 do_update_status = TRUE;
2353 /* state */
2354 else if(sipe_strequal(attrVar, "state"))
2356 char *tmp;
2357 int availability;
2358 const sipe_xml *xn_availability;
2359 const sipe_xml *xn_activity;
2360 const sipe_xml *xn_meeting_subject;
2361 const sipe_xml *xn_meeting_location;
2362 struct sipe_buddy *sbuddy = uri ? g_hash_table_lookup(sipe_private->buddies, uri) : NULL;
2364 xn_node = sipe_xml_child(xn_category, "state");
2365 if (!xn_node) continue;
2366 xn_availability = sipe_xml_child(xn_node, "availability");
2367 if (!xn_availability) continue;
2368 xn_activity = sipe_xml_child(xn_node, "activity");
2369 xn_meeting_subject = sipe_xml_child(xn_node, "meetingSubject");
2370 xn_meeting_location = sipe_xml_child(xn_node, "meetingLocation");
2372 tmp = sipe_xml_data(xn_availability);
2373 availability = atoi(tmp);
2374 g_free(tmp);
2376 /* activity, meeting_subject, meeting_location */
2377 if (sbuddy) {
2378 char *tmp = NULL;
2380 /* activity */
2381 g_free(sbuddy->activity);
2382 sbuddy->activity = NULL;
2383 if (xn_activity) {
2384 const char *token = sipe_xml_attribute(xn_activity, "token");
2385 const sipe_xml *xn_custom = sipe_xml_child(xn_activity, "custom");
2387 /* from token */
2388 if (!is_empty(token)) {
2389 sbuddy->activity = g_strdup(sipe_get_activity_desc_by_token(token));
2391 /* from custom element */
2392 if (xn_custom) {
2393 char *custom = sipe_xml_data(xn_custom);
2395 if (!is_empty(custom)) {
2396 sbuddy->activity = custom;
2397 custom = NULL;
2399 g_free(custom);
2402 /* meeting_subject */
2403 g_free(sbuddy->meeting_subject);
2404 sbuddy->meeting_subject = NULL;
2405 if (xn_meeting_subject) {
2406 char *meeting_subject = sipe_xml_data(xn_meeting_subject);
2408 if (!is_empty(meeting_subject)) {
2409 sbuddy->meeting_subject = meeting_subject;
2410 meeting_subject = NULL;
2412 g_free(meeting_subject);
2414 /* meeting_location */
2415 g_free(sbuddy->meeting_location);
2416 sbuddy->meeting_location = NULL;
2417 if (xn_meeting_location) {
2418 char *meeting_location = sipe_xml_data(xn_meeting_location);
2420 if (!is_empty(meeting_location)) {
2421 sbuddy->meeting_location = meeting_location;
2422 meeting_location = NULL;
2424 g_free(meeting_location);
2427 status = sipe_get_status_by_availability(availability, &tmp);
2428 if (sbuddy->activity && tmp) {
2429 char *tmp2 = sbuddy->activity;
2431 sbuddy->activity = g_strdup_printf("%s, %s", sbuddy->activity, tmp);
2432 g_free(tmp);
2433 g_free(tmp2);
2434 } else if (tmp) {
2435 sbuddy->activity = tmp;
2439 do_update_status = TRUE;
2441 /* calendarData */
2442 else if(sipe_strequal(attrVar, "calendarData"))
2444 struct sipe_buddy *sbuddy = uri ? g_hash_table_lookup(sipe_private->buddies, uri) : NULL;
2445 const sipe_xml *xn_free_busy = sipe_xml_child(xn_category, "calendarData/freeBusy");
2446 const sipe_xml *xn_working_hours = sipe_xml_child(xn_category, "calendarData/WorkingHours");
2448 if (sbuddy && xn_free_busy) {
2449 if (!has_free_busy_cleaned) {
2450 has_free_busy_cleaned = TRUE;
2452 g_free(sbuddy->cal_start_time);
2453 sbuddy->cal_start_time = NULL;
2455 g_free(sbuddy->cal_free_busy_base64);
2456 sbuddy->cal_free_busy_base64 = NULL;
2458 g_free(sbuddy->cal_free_busy);
2459 sbuddy->cal_free_busy = NULL;
2461 sbuddy->cal_free_busy_published = publish_time;
2464 if (publish_time >= sbuddy->cal_free_busy_published) {
2465 g_free(sbuddy->cal_start_time);
2466 sbuddy->cal_start_time = g_strdup(sipe_xml_attribute(xn_free_busy, "startTime"));
2468 sbuddy->cal_granularity = sipe_strcase_equal(sipe_xml_attribute(xn_free_busy, "granularity"), "PT15M") ?
2469 15 : 0;
2471 g_free(sbuddy->cal_free_busy_base64);
2472 sbuddy->cal_free_busy_base64 = sipe_xml_data(xn_free_busy);
2474 g_free(sbuddy->cal_free_busy);
2475 sbuddy->cal_free_busy = NULL;
2477 sbuddy->cal_free_busy_published = publish_time;
2479 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);
2483 if (sbuddy && xn_working_hours) {
2484 sipe_cal_parse_working_hours(xn_working_hours, sbuddy);
2489 if (do_update_status) {
2490 if (!status) { /* no status category in this update, using contact's current status */
2491 PurpleBuddy *pbuddy = purple_find_buddy((PurpleAccount *)sip->account, uri);
2492 const PurplePresence *presence = purple_buddy_get_presence(pbuddy);
2493 const PurpleStatus *pstatus = purple_presence_get_active_status(presence);
2494 status = purple_status_get_id(pstatus);
2497 SIPE_DEBUG_INFO("process_incoming_notify_rlmi: %s", status);
2498 sipe_core_buddy_got_status(SIPE_CORE_PUBLIC, uri, status);
2501 sipe_xml_free(xn_categories);
2504 void process_incoming_notify_pidf(struct sipe_core_private *sipe_private,
2505 const gchar *data,
2506 unsigned len)
2508 gchar *uri;
2509 gchar *getbasic;
2510 gchar *activity = NULL;
2511 sipe_xml *pidf;
2512 const sipe_xml *basicstatus = NULL, *tuple, *status;
2513 gboolean isonline = FALSE;
2514 const sipe_xml *display_name_node;
2516 pidf = sipe_xml_parse(data, len);
2517 if (!pidf) {
2518 SIPE_DEBUG_INFO("process_incoming_notify_pidf: no parseable pidf:%s", data);
2519 return;
2522 if ((tuple = sipe_xml_child(pidf, "tuple")))
2524 if ((status = sipe_xml_child(tuple, "status"))) {
2525 basicstatus = sipe_xml_child(status, "basic");
2529 if (!basicstatus) {
2530 SIPE_DEBUG_INFO_NOFORMAT("process_incoming_notify_pidf: no basic found");
2531 sipe_xml_free(pidf);
2532 return;
2535 getbasic = sipe_xml_data(basicstatus);
2536 if (!getbasic) {
2537 SIPE_DEBUG_INFO_NOFORMAT("process_incoming_notify_pidf: no basic data found");
2538 sipe_xml_free(pidf);
2539 return;
2542 SIPE_DEBUG_INFO("process_incoming_notify_pidf: basic-status(%s)", getbasic);
2543 if (strstr(getbasic, "open")) {
2544 isonline = TRUE;
2546 g_free(getbasic);
2548 uri = sip_uri(sipe_xml_attribute(pidf, "entity")); /* with 'sip:' prefix */ /* AOL comes without the prefix */
2550 display_name_node = sipe_xml_child(pidf, "display-name");
2551 if (display_name_node) {
2552 char * display_name = sipe_xml_data(display_name_node);
2554 sipe_update_user_info(sipe_private, uri, SIPE_BUDDY_INFO_DISPLAY_NAME, display_name);
2555 g_free(display_name);
2558 if ((tuple = sipe_xml_child(pidf, "tuple"))) {
2559 if ((status = sipe_xml_child(tuple, "status"))) {
2560 if ((basicstatus = sipe_xml_child(status, "activities"))) {
2561 if ((basicstatus = sipe_xml_child(basicstatus, "activity"))) {
2562 activity = sipe_xml_data(basicstatus);
2563 SIPE_DEBUG_INFO("process_incoming_notify_pidf: activity(%s)", activity);
2569 if (isonline) {
2570 const gchar * status_id = NULL;
2571 if (activity) {
2572 if (sipe_strequal(activity, sipe_activity_map[SIPE_ACTIVITY_BUSY].token)) {
2573 status_id = SIPE_STATUS_ID_BUSY;
2574 } else if (sipe_strequal(activity, sipe_activity_map[SIPE_ACTIVITY_AWAY].token)) {
2575 status_id = SIPE_STATUS_ID_AWAY;
2579 if (!status_id) {
2580 status_id = SIPE_STATUS_ID_AVAILABLE;
2583 SIPE_DEBUG_INFO("process_incoming_notify_pidf: status_id(%s)", status_id);
2584 sipe_core_buddy_got_status(SIPE_CORE_PUBLIC, uri, status_id);
2585 } else {
2586 sipe_core_buddy_got_status(SIPE_CORE_PUBLIC, uri, SIPE_STATUS_ID_OFFLINE);
2589 g_free(activity);
2590 g_free(uri);
2591 sipe_xml_free(pidf);
2594 /** 2005 */
2595 static void
2596 sipe_user_info_has_updated(struct sipe_core_private *sipe_private,
2597 const sipe_xml *xn_userinfo)
2599 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
2600 const sipe_xml *xn_states;
2602 g_free(sip->user_states);
2603 sip->user_states = NULL;
2604 if ((xn_states = sipe_xml_child(xn_userinfo, "states")) != NULL) {
2605 gchar *orig = sip->user_states = sipe_xml_stringify(xn_states);
2607 /* this is a hack-around to remove added newline after inner element,
2608 * state in this case, where it shouldn't be.
2609 * After several use of sipe_xml_stringify, amount of added newlines
2610 * grows significantly.
2612 if (orig) {
2613 gchar c, *stripped = orig;
2614 while ((c = *orig++)) {
2615 if ((c != '\n') /* && (c != '\r') */) {
2616 *stripped++ = c;
2619 *stripped = '\0';
2623 /* Publish initial state if not yet.
2624 * Assuming this happens on initial responce to self subscription
2625 * so we've already updated our UserInfo.
2627 if (!sip->initial_state_published) {
2628 send_presence_soap(sipe_private, FALSE);
2629 /* dalayed run */
2630 sipe_schedule_seconds(sipe_private,
2631 "<+update-calendar>",
2632 NULL,
2633 UPDATE_CALENDAR_DELAY,
2634 (sipe_schedule_action) sipe_core_update_calendar,
2635 NULL);
2639 void process_incoming_notify_msrtc(struct sipe_core_private *sipe_private,
2640 const gchar *data,
2641 unsigned len)
2643 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
2644 char *activity = NULL;
2645 const char *epid;
2646 const char *status_id = NULL;
2647 const char *name;
2648 char *uri;
2649 char *self_uri = sip_uri_self(sipe_private);
2650 int avl;
2651 int act;
2652 const char *device_name = NULL;
2653 const char *cal_start_time = NULL;
2654 const char *cal_granularity = NULL;
2655 char *cal_free_busy_base64 = NULL;
2656 struct sipe_buddy *sbuddy;
2657 const sipe_xml *node;
2658 sipe_xml *xn_presentity;
2659 const sipe_xml *xn_availability;
2660 const sipe_xml *xn_activity;
2661 const sipe_xml *xn_display_name;
2662 const sipe_xml *xn_email;
2663 const sipe_xml *xn_phone_number;
2664 const sipe_xml *xn_userinfo;
2665 const sipe_xml *xn_note;
2666 const sipe_xml *xn_oof;
2667 const sipe_xml *xn_state;
2668 const sipe_xml *xn_contact;
2669 char *note;
2670 int user_avail;
2671 const char *user_avail_nil;
2672 int res_avail;
2673 time_t user_avail_since = 0;
2674 time_t activity_since = 0;
2676 /* fix for Reuters environment on Linux */
2677 if (data && strstr(data, "encoding=\"utf-16\"")) {
2678 char *tmp_data;
2679 tmp_data = replace(data, "encoding=\"utf-16\"", "encoding=\"utf-8\"");
2680 xn_presentity = sipe_xml_parse(tmp_data, strlen(tmp_data));
2681 g_free(tmp_data);
2682 } else {
2683 xn_presentity = sipe_xml_parse(data, len);
2686 xn_availability = sipe_xml_child(xn_presentity, "availability");
2687 xn_activity = sipe_xml_child(xn_presentity, "activity");
2688 xn_display_name = sipe_xml_child(xn_presentity, "displayName");
2689 xn_email = sipe_xml_child(xn_presentity, "email");
2690 xn_phone_number = sipe_xml_child(xn_presentity, "phoneNumber");
2691 xn_userinfo = sipe_xml_child(xn_presentity, "userInfo");
2692 xn_oof = xn_userinfo ? sipe_xml_child(xn_userinfo, "oof") : NULL;
2693 xn_state = xn_userinfo ? sipe_xml_child(xn_userinfo, "states/state"): NULL;
2694 user_avail = xn_state ? sipe_xml_int_attribute(xn_state, "avail", 0) : 0;
2695 user_avail_since = xn_state ? sipe_utils_str_to_time(sipe_xml_attribute(xn_state, "since")) : 0;
2696 user_avail_nil = xn_state ? sipe_xml_attribute(xn_state, "nil") : NULL;
2697 xn_contact = xn_userinfo ? sipe_xml_child(xn_userinfo, "contact") : NULL;
2698 xn_note = xn_userinfo ? sipe_xml_child(xn_userinfo, "note") : NULL;
2699 note = xn_note ? sipe_xml_data(xn_note) : NULL;
2701 if (sipe_strequal(user_avail_nil, "true")) { /* null-ed */
2702 user_avail = 0;
2703 user_avail_since = 0;
2706 name = sipe_xml_attribute(xn_presentity, "uri"); /* without 'sip:' prefix */
2707 uri = sip_uri_from_name(name);
2708 avl = sipe_xml_int_attribute(xn_availability, "aggregate", 0);
2709 epid = sipe_xml_attribute(xn_availability, "epid");
2710 act = sipe_xml_int_attribute(xn_activity, "aggregate", 0);
2712 status_id = sipe_get_status_by_act_avail_2005(act, avl, &activity);
2713 res_avail = sipe_get_availability_by_status(status_id, NULL);
2714 if (user_avail > res_avail) {
2715 res_avail = user_avail;
2716 status_id = sipe_get_status_by_availability(user_avail, NULL);
2719 if (xn_display_name) {
2720 char *display_name = g_strdup(sipe_xml_attribute(xn_display_name, "displayName"));
2721 char *email = xn_email ? g_strdup(sipe_xml_attribute(xn_email, "email")) : NULL;
2722 char *phone_label = xn_phone_number ? g_strdup(sipe_xml_attribute(xn_phone_number, "label")) : NULL;
2723 char *phone_number = xn_phone_number ? g_strdup(sipe_xml_attribute(xn_phone_number, "number")) : NULL;
2724 char *tel_uri = sip_to_tel_uri(phone_number);
2726 sipe_update_user_info(sipe_private, uri, SIPE_BUDDY_INFO_DISPLAY_NAME, display_name);
2727 sipe_update_user_info(sipe_private, uri, SIPE_BUDDY_INFO_EMAIL, email);
2728 sipe_update_user_info(sipe_private, uri, SIPE_BUDDY_INFO_WORK_PHONE, tel_uri);
2729 sipe_update_user_info(sipe_private, uri, SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY, !is_empty(phone_label) ? phone_label : phone_number);
2731 g_free(tel_uri);
2732 g_free(phone_label);
2733 g_free(phone_number);
2734 g_free(email);
2735 g_free(display_name);
2738 if (xn_contact) {
2739 /* tel */
2740 for (node = sipe_xml_child(xn_contact, "tel"); node; node = sipe_xml_twin(node))
2742 /* Ex.: <tel type="work">tel:+3222220000</tel> */
2743 const char *phone_type = sipe_xml_attribute(node, "type");
2744 char* phone = sipe_xml_data(node);
2746 sipe_update_user_phone(sipe_private, uri, phone_type, phone, NULL);
2748 g_free(phone);
2752 /* devicePresence */
2753 for (node = sipe_xml_child(xn_presentity, "devices/devicePresence"); node; node = sipe_xml_twin(node)) {
2754 const sipe_xml *xn_device_name;
2755 const sipe_xml *xn_calendar_info;
2756 const sipe_xml *xn_state;
2757 char *state;
2759 /* deviceName */
2760 if (sipe_strequal(sipe_xml_attribute(node, "epid"), epid)) {
2761 xn_device_name = sipe_xml_child(node, "deviceName");
2762 device_name = xn_device_name ? sipe_xml_attribute(xn_device_name, "name") : NULL;
2765 /* calendarInfo */
2766 xn_calendar_info = sipe_xml_child(node, "calendarInfo");
2767 if (xn_calendar_info) {
2768 const char *cal_start_time_tmp = sipe_xml_attribute(xn_calendar_info, "startTime");
2770 if (cal_start_time) {
2771 time_t cal_start_time_t = sipe_utils_str_to_time(cal_start_time);
2772 time_t cal_start_time_t_tmp = sipe_utils_str_to_time(cal_start_time_tmp);
2774 if (cal_start_time_t_tmp > cal_start_time_t) {
2775 cal_start_time = cal_start_time_tmp;
2776 cal_granularity = sipe_xml_attribute(xn_calendar_info, "granularity");
2777 g_free(cal_free_busy_base64);
2778 cal_free_busy_base64 = sipe_xml_data(xn_calendar_info);
2780 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);
2782 } else {
2783 cal_start_time = cal_start_time_tmp;
2784 cal_granularity = sipe_xml_attribute(xn_calendar_info, "granularity");
2785 g_free(cal_free_busy_base64);
2786 cal_free_busy_base64 = sipe_xml_data(xn_calendar_info);
2788 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);
2792 /* state */
2793 xn_state = sipe_xml_child(node, "states/state");
2794 if (xn_state) {
2795 int dev_avail = sipe_xml_int_attribute(xn_state, "avail", 0);
2796 time_t dev_avail_since = sipe_utils_str_to_time(sipe_xml_attribute(xn_state, "since"));
2798 state = sipe_xml_data(xn_state);
2799 if (dev_avail_since > user_avail_since &&
2800 dev_avail >= res_avail)
2802 res_avail = dev_avail;
2803 if (!is_empty(state))
2805 if (sipe_strequal(state, sipe_activity_map[SIPE_ACTIVITY_ON_PHONE].token)) {
2806 g_free(activity);
2807 activity = g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_ON_PHONE));
2808 } else if (sipe_strequal(state, "presenting")) {
2809 g_free(activity);
2810 activity = g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_IN_CONF));
2811 } else {
2812 activity = state;
2813 state = NULL;
2815 activity_since = dev_avail_since;
2817 status_id = sipe_get_status_by_availability(res_avail, &activity);
2819 g_free(state);
2823 /* oof */
2824 if (xn_oof && res_avail >= 15000) { /* 12000 in 2007 */
2825 g_free(activity);
2826 activity = g_strdup(SIPE_ACTIVITY_I18N(SIPE_ACTIVITY_OOF));
2827 activity_since = 0;
2830 sbuddy = g_hash_table_lookup(sipe_private->buddies, uri);
2831 if (sbuddy)
2833 g_free(sbuddy->activity);
2834 sbuddy->activity = activity;
2835 activity = NULL;
2837 sbuddy->activity_since = activity_since;
2839 sbuddy->user_avail = user_avail;
2840 sbuddy->user_avail_since = user_avail_since;
2842 g_free(sbuddy->note);
2843 sbuddy->note = NULL;
2844 if (!is_empty(note)) { sbuddy->note = g_markup_escape_text(note, -1); }
2846 sbuddy->is_oof_note = (xn_oof != NULL);
2848 g_free(sbuddy->device_name);
2849 sbuddy->device_name = NULL;
2850 if (!is_empty(device_name)) { sbuddy->device_name = g_strdup(device_name); }
2852 if (!is_empty(cal_free_busy_base64)) {
2853 g_free(sbuddy->cal_start_time);
2854 sbuddy->cal_start_time = g_strdup(cal_start_time);
2856 sbuddy->cal_granularity = sipe_strcase_equal(cal_granularity, "PT15M") ? 15 : 0;
2858 g_free(sbuddy->cal_free_busy_base64);
2859 sbuddy->cal_free_busy_base64 = cal_free_busy_base64;
2860 cal_free_busy_base64 = NULL;
2862 g_free(sbuddy->cal_free_busy);
2863 sbuddy->cal_free_busy = NULL;
2866 sbuddy->last_non_cal_status_id = status_id;
2867 g_free(sbuddy->last_non_cal_activity);
2868 sbuddy->last_non_cal_activity = g_strdup(sbuddy->activity);
2870 if (sipe_strcase_equal(sbuddy->name, self_uri)) {
2871 if (!sipe_strequal(sbuddy->note, sip->note)) /* not same */
2873 sip->is_oof_note = sbuddy->is_oof_note;
2875 g_free(sip->note);
2876 sip->note = g_strdup(sbuddy->note);
2878 sip->note_since = time(NULL);
2881 g_free(sip->status);
2882 sip->status = g_strdup(sbuddy->last_non_cal_status_id);
2885 g_free(cal_free_busy_base64);
2886 g_free(activity);
2888 SIPE_DEBUG_INFO("process_incoming_notify_msrtc: status(%s)", status_id);
2889 sipe_core_buddy_got_status(SIPE_CORE_PUBLIC, uri, status_id);
2891 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007) && sipe_strcase_equal(self_uri, uri)) {
2892 sipe_user_info_has_updated(sipe_private, xn_userinfo);
2895 g_free(note);
2896 sipe_xml_free(xn_presentity);
2897 g_free(uri);
2898 g_free(self_uri);
2902 * Whether user manually changed status or
2903 * it was changed automatically due to user
2904 * became inactive/active again
2906 static gboolean
2907 sipe_is_user_state(struct sipe_core_private *sipe_private)
2909 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
2910 gboolean res;
2911 time_t now = time(NULL);
2913 SIPE_DEBUG_INFO("sipe_is_user_state: sip->idle_switch : %s", asctime(localtime(&(sip->idle_switch))));
2914 SIPE_DEBUG_INFO("sipe_is_user_state: now : %s", asctime(localtime(&now)));
2916 res = ((now - SIPE_IDLE_SET_DELAY * 2) >= sip->idle_switch);
2918 SIPE_DEBUG_INFO("sipe_is_user_state: res = %s", res ? "USER" : "MACHINE");
2919 return res;
2923 * OCS2005 presence XML messages
2925 * Calendar publication entry
2927 * @param legacy_dn (%s) Ex.: /o=EXCHANGE/ou=BTUK02/cn=Recipients/cn=AHHBTT
2928 * @param fb_start_time_str (%s) Ex.: 2009-12-06T17:15:00Z
2929 * @param free_busy_base64 (%s) Ex.: AAAAAAAAAAAAAAAAA......
2931 #define SIPE_SOAP_SET_PRESENCE_CALENDAR \
2932 "<calendarInfo xmlns=\"http://schemas.microsoft.com/2002/09/sip/presence\" mailboxId=\"%s\" startTime=\"%s\" granularity=\"PT15M\">%s</calendarInfo>"
2935 * Note publication entry
2937 * @param note (%s) Ex.: Working from home
2939 #define SIPE_SOAP_SET_PRESENCE_NOTE_XML "<note>%s</note>"
2942 * Note's OOF publication entry
2944 #define SIPE_SOAP_SET_PRESENCE_OOF_XML "<oof></oof>"
2947 * States publication entry for User State
2949 * @param avail (%d) Availability 2007-style. Ex.: 9500
2950 * @param since_time_str (%s) Ex.: 2010-01-13T10:30:05Z
2951 * @param device_id (%s) epid. Ex.: 4c77e6ec72
2952 * @param activity_token (%s) Ex.: do-not-disturb
2954 #define SIPE_SOAP_SET_PRESENCE_STATES \
2955 "<states>"\
2956 "<state avail=\"%d\" since=\"%s\" validWith=\"any-device\" deviceId=\"%s\" set=\"manual\" xsi:type=\"userState\">%s</state>"\
2957 "</states>"
2960 * Presentity publication entry.
2962 * @param uri (%s) SIP URI without 'sip:' prefix. Ex.: fox@atlanta.local
2963 * @param aggr_availability (%d) Ex.: 300
2964 * @param aggr_activity (%d) Ex.: 600
2965 * @param host_name (%s) Uppercased. Ex.: ATLANTA
2966 * @param note_xml_str (%s) XML string as SIPE_SOAP_SET_PRESENCE_NOTE_XML
2967 * @param oof_xml_str (%s) XML string as SIPE_SOAP_SET_PRESENCE_OOF_XML
2968 * @param states_xml_str (%s) XML string as SIPE_SOAP_SET_PRESENCE_STATES
2969 * @param calendar_info_xml_str (%s) XML string as SIPE_SOAP_SET_PRESENCE_CALENDAR
2970 * @param device_id (%s) epid. Ex.: 4c77e6ec72
2971 * @param since_time_str (%s) Ex.: 2010-01-13T10:30:05Z
2972 * @param since_time_str (%s) Ex.: 2010-01-13T10:30:05Z
2973 * @param user_input (%s) active, idle
2975 #define SIPE_SOAP_SET_PRESENCE \
2976 "<s:Envelope" \
2977 " xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"" \
2978 " xmlns:m=\"http://schemas.microsoft.com/winrtc/2002/11/sip\"" \
2979 ">" \
2980 "<s:Body>" \
2981 "<m:setPresence>" \
2982 "<m:presentity xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" m:uri=\"sip:%s\">"\
2983 "<m:availability m:aggregate=\"%d\"/>"\
2984 "<m:activity m:aggregate=\"%d\"/>"\
2985 "<deviceName xmlns=\"http://schemas.microsoft.com/2002/09/sip/presence\" name=\"%s\"/>"\
2986 "<rtc:devicedata xmlns:rtc=\"http://schemas.microsoft.com/winrtc/2002/11/sip\" namespace=\"rtcService\">"\
2987 "<![CDATA[<caps><renders_gif/><renders_isf/></caps>]]></rtc:devicedata>"\
2988 "<userInfo xmlns=\"http://schemas.microsoft.com/2002/09/sip/presence\">"\
2989 "%s%s" \
2990 "%s" \
2991 "</userInfo>"\
2992 "%s" \
2993 "<device xmlns=\"http://schemas.microsoft.com/2002/09/sip/presence\" deviceId=\"%s\" since=\"%s\" >"\
2994 "<userInput since=\"%s\" >%s</userInput>"\
2995 "</device>"\
2996 "</m:presentity>" \
2997 "</m:setPresence>"\
2998 "</s:Body>" \
2999 "</s:Envelope>"
3001 static void
3002 send_presence_soap0(struct sipe_core_private *sipe_private,
3003 gboolean do_publish_calendar,
3004 gboolean do_reset_status)
3006 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
3007 struct sipe_calendar* cal = sip->cal;
3008 int availability = 0;
3009 int activity = 0;
3010 gchar *body;
3011 gchar *tmp;
3012 gchar *tmp2 = NULL;
3013 gchar *res_note = NULL;
3014 gchar *res_oof = NULL;
3015 const gchar *note_pub = NULL;
3016 gchar *states = NULL;
3017 gchar *calendar_data = NULL;
3018 gchar *epid = get_epid(sipe_private);
3019 gchar *from = sip_uri_self(sipe_private);
3020 time_t now = time(NULL);
3021 gchar *since_time_str = sipe_utils_time_to_str(now);
3022 const gchar *oof_note = cal ? sipe_ews_get_oof_note(cal) : NULL;
3023 const char *user_input;
3024 gboolean pub_oof = cal && oof_note && (!sip->note || cal->updated > sip->note_since);
3026 if (oof_note && sip->note) {
3027 SIPE_DEBUG_INFO("cal->oof_start : %s", asctime(localtime(&(cal->oof_start))));
3028 SIPE_DEBUG_INFO("sip->note_since : %s", asctime(localtime(&(sip->note_since))));
3031 SIPE_DEBUG_INFO("sip->note : %s", sip->note ? sip->note : "");
3033 if (!sip->initial_state_published ||
3034 do_reset_status)
3036 g_free(sip->status);
3037 sip->status = g_strdup(SIPE_STATUS_ID_AVAILABLE);
3040 sipe_get_act_avail_by_status_2005(sip->status, &activity, &availability);
3042 /* Note */
3043 if (pub_oof) {
3044 note_pub = oof_note;
3045 res_oof = SIPE_SOAP_SET_PRESENCE_OOF_XML;
3046 cal->published = TRUE;
3047 } else if (sip->note) {
3048 if (sip->is_oof_note && !oof_note) { /* stale OOF note, as it's not present in cal already */
3049 g_free(sip->note);
3050 sip->note = NULL;
3051 sip->is_oof_note = FALSE;
3052 sip->note_since = 0;
3053 } else {
3054 note_pub = sip->note;
3055 res_oof = sip->is_oof_note ? SIPE_SOAP_SET_PRESENCE_OOF_XML : "";
3059 if (note_pub)
3061 /* to protocol internal plain text format */
3062 tmp = sipe_backend_markup_strip_html(note_pub);
3063 res_note = g_markup_printf_escaped(SIPE_SOAP_SET_PRESENCE_NOTE_XML, tmp);
3064 g_free(tmp);
3067 /* User State */
3068 if (!do_reset_status) {
3069 if (sipe_is_user_state(sipe_private) && !do_publish_calendar && sip->initial_state_published)
3071 gchar *activity_token = NULL;
3072 int avail_2007 = sipe_get_availability_by_status(sip->status, &activity_token);
3074 states = g_strdup_printf(SIPE_SOAP_SET_PRESENCE_STATES,
3075 avail_2007,
3076 since_time_str,
3077 epid,
3078 activity_token);
3079 g_free(activity_token);
3081 else /* preserve existing publication */
3083 if (sip->user_states) {
3084 states = g_strdup(sip->user_states);
3087 } else {
3088 /* do nothing - then User state will be erased */
3090 sip->initial_state_published = TRUE;
3092 /* CalendarInfo */
3093 if (cal && (!is_empty(cal->legacy_dn) || !is_empty(cal->email)) && cal->fb_start && !is_empty(cal->free_busy))
3095 char *fb_start_str = sipe_utils_time_to_str(cal->fb_start);
3096 char *free_busy_base64 = sipe_cal_get_freebusy_base64(cal->free_busy);
3097 calendar_data = g_strdup_printf(SIPE_SOAP_SET_PRESENCE_CALENDAR,
3098 !is_empty(cal->legacy_dn) ? cal->legacy_dn : cal->email,
3099 fb_start_str,
3100 free_busy_base64);
3101 g_free(fb_start_str);
3102 g_free(free_busy_base64);
3105 user_input = (sipe_is_user_state(sipe_private) ||
3106 sipe_strequal(sip->status, SIPE_STATUS_ID_AVAILABLE)) ?
3107 "active" : "idle";
3109 /* forming resulting XML */
3110 body = g_strdup_printf(SIPE_SOAP_SET_PRESENCE,
3111 sipe_private->username,
3112 availability,
3113 activity,
3114 (tmp = g_ascii_strup(g_get_host_name(), -1)),
3115 res_note ? res_note : "",
3116 res_oof ? res_oof : "",
3117 states ? states : "",
3118 calendar_data ? calendar_data : "",
3119 epid,
3120 since_time_str,
3121 since_time_str,
3122 user_input);
3123 g_free(tmp);
3124 g_free(tmp2);
3125 g_free(res_note);
3126 g_free(states);
3127 g_free(calendar_data);
3128 g_free(since_time_str);
3129 g_free(epid);
3131 sip_soap_raw_request_cb(sipe_private, from, body, NULL, NULL);
3133 g_free(body);
3136 void
3137 send_presence_soap(struct sipe_core_private *sipe_private,
3138 gboolean do_publish_calendar)
3140 return send_presence_soap0(sipe_private, do_publish_calendar, FALSE);
3144 static gboolean
3145 process_send_presence_category_publish_response(struct sipe_core_private *sipe_private,
3146 struct sipmsg *msg,
3147 struct transaction *trans)
3149 const gchar *contenttype = sipmsg_find_header(msg, "Content-Type");
3151 if (msg->response == 409 && g_str_has_prefix(contenttype, "application/msrtc-fault+xml")) {
3152 sipe_xml *xml;
3153 const sipe_xml *node;
3154 gchar *fault_code;
3155 GHashTable *faults;
3156 int index_our;
3157 gboolean has_device_publication = FALSE;
3159 xml = sipe_xml_parse(msg->body, msg->bodylen);
3161 /* test if version mismatch fault */
3162 fault_code = sipe_xml_data(sipe_xml_child(xml, "Faultcode"));
3163 if (!sipe_strequal(fault_code, "Client.BadCall.WrongDelta")) {
3164 SIPE_DEBUG_INFO("process_send_presence_category_publish_response: unsupported fault code:%s returning.", fault_code);
3165 g_free(fault_code);
3166 sipe_xml_free(xml);
3167 return TRUE;
3169 g_free(fault_code);
3171 /* accumulating information about faulty versions */
3172 faults = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
3173 for (node = sipe_xml_child(xml, "details/operation");
3174 node;
3175 node = sipe_xml_twin(node))
3177 const gchar *index = sipe_xml_attribute(node, "index");
3178 const gchar *curVersion = sipe_xml_attribute(node, "curVersion");
3180 g_hash_table_insert(faults, g_strdup(index), g_strdup(curVersion));
3181 SIPE_DEBUG_INFO("fault added: index:%s curVersion:%s", index, curVersion);
3183 sipe_xml_free(xml);
3185 /* here we are parsing our own request to figure out what publication
3186 * referenced here only by index went wrong
3188 xml = sipe_xml_parse(trans->msg->body, trans->msg->bodylen);
3190 /* publication */
3191 for (node = sipe_xml_child(xml, "publications/publication"),
3192 index_our = 1; /* starts with 1 - our first publication */
3193 node;
3194 node = sipe_xml_twin(node), index_our++)
3196 gchar *idx = g_strdup_printf("%d", index_our);
3197 const gchar *curVersion = g_hash_table_lookup(faults, idx);
3198 const gchar *categoryName = sipe_xml_attribute(node, "categoryName");
3199 g_free(idx);
3201 if (sipe_strequal("device", categoryName)) {
3202 has_device_publication = TRUE;
3205 if (curVersion) { /* fault exist on this index */
3206 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
3207 const gchar *container = sipe_xml_attribute(node, "container");
3208 const gchar *instance = sipe_xml_attribute(node, "instance");
3209 /* key is <category><instance><container> */
3210 gchar *key = g_strdup_printf("<%s><%s><%s>", categoryName, instance, container);
3211 GHashTable *category = g_hash_table_lookup(sip->our_publications, categoryName);
3213 if (category) {
3214 struct sipe_publication *publication =
3215 g_hash_table_lookup(category, key);
3217 SIPE_DEBUG_INFO("key is %s", key);
3219 if (publication) {
3220 SIPE_DEBUG_INFO("Updating %s with version %s. Was %d before.",
3221 key, curVersion, publication->version);
3222 /* updating publication's version to the correct one */
3223 publication->version = atoi(curVersion);
3225 } else {
3226 /* We somehow lost this category from our publications... */
3227 struct sipe_publication *publication = g_new0(struct sipe_publication, 1);
3228 publication->category = g_strdup(categoryName);
3229 publication->instance = atoi(instance);
3230 publication->container = atoi(container);
3231 publication->version = atoi(curVersion);
3232 category = g_hash_table_new_full(g_str_hash, g_str_equal,
3233 g_free, (GDestroyNotify)free_publication);
3234 g_hash_table_insert(category, g_strdup(key), publication);
3235 g_hash_table_insert(sip->our_publications, g_strdup(categoryName), category);
3236 SIPE_DEBUG_INFO("added lost category '%s' key '%s'", categoryName, key);
3238 g_free(key);
3241 sipe_xml_free(xml);
3242 g_hash_table_destroy(faults);
3244 /* rebublishing with right versions */
3245 if (has_device_publication) {
3246 send_publish_category_initial(sipe_private);
3247 } else {
3248 send_presence_status(sipe_private, NULL);
3251 return TRUE;
3255 * Publishes 'device' category.
3256 * @param instance (%u) Ex.: 1938468728
3257 * @param version (%u) Ex.: 1
3258 * @param endpointId (%s) Ex.: C707E38E-1E10-5413-94D9-ECAC260A0269
3259 * @param uri (%s) Self URI. Ex.: sip:alice7@boston.local
3260 * @param timezone (%s) Ex.: 00:00:00+01:00
3261 * @param machineName (%s) Ex.: BOSTON-OCS07
3263 #define SIPE_PUB_XML_DEVICE \
3264 "<publication categoryName=\"device\" instance=\"%u\" container=\"2\" version=\"%u\" expireType=\"endpoint\">"\
3265 "<device xmlns=\"http://schemas.microsoft.com/2006/09/sip/device\" endpointId=\"%s\">"\
3266 "<capabilities preferred=\"false\" uri=\"%s\">"\
3267 "<text capture=\"true\" render=\"true\" publish=\"false\"/>"\
3268 "<gifInk capture=\"false\" render=\"true\" publish=\"false\"/>"\
3269 "<isfInk capture=\"false\" render=\"true\" publish=\"false\"/>"\
3270 "</capabilities>"\
3271 "<timezone>%s</timezone>"\
3272 "<machineName>%s</machineName>"\
3273 "</device>"\
3274 "</publication>"
3277 * Returns 'device' XML part for publication.
3278 * Must be g_free'd after use.
3280 static gchar *
3281 sipe_publish_get_category_device(struct sipe_core_private *sipe_private)
3283 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
3284 gchar *uri;
3285 gchar *doc;
3286 gchar *uuid = get_uuid(sipe_private);
3287 guint device_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_DEVICE);
3288 /* key is <category><instance><container> */
3289 gchar *key = g_strdup_printf("<%s><%u><%u>", "device", device_instance, 2);
3290 struct sipe_publication *publication =
3291 g_hash_table_lookup(g_hash_table_lookup(sip->our_publications, "device"), key);
3293 g_free(key);
3295 uri = sip_uri_self(sipe_private);
3296 doc = g_strdup_printf(SIPE_PUB_XML_DEVICE,
3297 device_instance,
3298 publication ? publication->version : 0,
3299 uuid,
3300 uri,
3301 "00:00:00+01:00", /* @TODO make timezone real*/
3302 g_get_host_name()
3305 g_free(uri);
3306 g_free(uuid);
3308 return doc;
3312 * Publishes 'machineState' category.
3313 * @param instance (%u) Ex.: 926460663
3314 * @param version (%u) Ex.: 22
3315 * @param availability (%d) Ex.: 3500
3316 * @param instance (%u) Ex.: 926460663
3317 * @param version (%u) Ex.: 22
3318 * @param availability (%d) Ex.: 3500
3320 #define SIPE_PUB_XML_STATE_MACHINE \
3321 "<publication categoryName=\"state\" instance=\"%u\" container=\"2\" version=\"%u\" expireType=\"endpoint\">"\
3322 "<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"false\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"machineState\">"\
3323 "<availability>%d</availability>"\
3324 "<endpointLocation/>"\
3325 "</state>"\
3326 "</publication>"\
3327 "<publication categoryName=\"state\" instance=\"%u\" container=\"3\" version=\"%u\" expireType=\"endpoint\">"\
3328 "<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"false\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"machineState\">"\
3329 "<availability>%d</availability>"\
3330 "<endpointLocation/>"\
3331 "</state>"\
3332 "</publication>"
3335 * Publishes 'userState' category.
3336 * @param instance (%u) User. Ex.: 536870912
3337 * @param version (%u) User Container 2. Ex.: 22
3338 * @param availability (%d) User Container 2. Ex.: 15500
3339 * @param instance (%u) User. Ex.: 536870912
3340 * @param version (%u) User Container 3.Ex.: 22
3341 * @param availability (%d) User Container 3. Ex.: 15500
3343 #define SIPE_PUB_XML_STATE_USER \
3344 "<publication categoryName=\"state\" instance=\"%u\" container=\"2\" version=\"%u\" expireType=\"static\">"\
3345 "<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"true\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"userState\">"\
3346 "<availability>%d</availability>"\
3347 "<endpointLocation/>"\
3348 "</state>"\
3349 "</publication>"\
3350 "<publication categoryName=\"state\" instance=\"%u\" container=\"3\" version=\"%u\" expireType=\"static\">"\
3351 "<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"true\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"userState\">"\
3352 "<availability>%d</availability>"\
3353 "<endpointLocation/>"\
3354 "</state>"\
3355 "</publication>"
3358 * A service method - use
3359 * - send_publish_get_category_state_machine and
3360 * - send_publish_get_category_state_user instead.
3361 * Must be g_free'd after use.
3363 static gchar *
3364 sipe_publish_get_category_state(struct sipe_core_private *sipe_private,
3365 gboolean is_user_state)
3367 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
3368 int availability = sipe_get_availability_by_status(sip->status, NULL);
3369 guint instance = is_user_state ? sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_USER) :
3370 sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_MACHINE);
3371 /* key is <category><instance><container> */
3372 gchar *key_2 = g_strdup_printf("<%s><%u><%u>", "state", instance, 2);
3373 gchar *key_3 = g_strdup_printf("<%s><%u><%u>", "state", instance, 3);
3374 struct sipe_publication *publication_2 =
3375 g_hash_table_lookup(g_hash_table_lookup(sip->our_publications, "state"), key_2);
3376 struct sipe_publication *publication_3 =
3377 g_hash_table_lookup(g_hash_table_lookup(sip->our_publications, "state"), key_3);
3379 g_free(key_2);
3380 g_free(key_3);
3382 if (publication_2 && (publication_2->availability == availability))
3384 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_state: state has NOT changed. Exiting.");
3385 return NULL; /* nothing to update */
3388 return g_strdup_printf( is_user_state ? SIPE_PUB_XML_STATE_USER : SIPE_PUB_XML_STATE_MACHINE,
3389 instance,
3390 publication_2 ? publication_2->version : 0,
3391 availability,
3392 instance,
3393 publication_3 ? publication_3->version : 0,
3394 availability);
3398 * An availability XML entry for SIPE_PUB_XML_STATE_CALENDAR
3399 * @param availability (%d) Ex.: 6500
3401 #define SIPE_PUB_XML_STATE_CALENDAR_AVAIL \
3402 "<availability>%d</availability>"
3404 * An activity XML entry for SIPE_PUB_XML_STATE_CALENDAR
3405 * @param token (%s) Ex.: in-a-meeting
3406 * @param minAvailability_attr (%s) Ex.: minAvailability="6500"
3407 * @param maxAvailability_attr (%s) Ex.: maxAvailability="8999" or none
3409 #define SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY \
3410 "<activity token=\"%s\" %s %s></activity>"
3412 * Publishes 'calendarState' category.
3413 * @param instance (%u) Ex.: 1339299275
3414 * @param version (%u) Ex.: 1
3415 * @param uri (%s) Ex.: john@contoso.com
3416 * @param start_time_str (%s) Ex.: 2008-01-11T19:00:00Z
3417 * @param availability (%s) XML string as SIPE_PUB_XML_STATE_CALENDAR_AVAIL
3418 * @param activity (%s) XML string as SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY
3419 * @param meeting_subject (%s) Ex.: Customer Meeting
3420 * @param meeting_location (%s) Ex.: Conf Room 100
3422 * @param instance (%u) Ex.: 1339299275
3423 * @param version (%u) Ex.: 1
3424 * @param uri (%s) Ex.: john@contoso.com
3425 * @param start_time_str (%s) Ex.: 2008-01-11T19:00:00Z
3426 * @param availability (%s) XML string as SIPE_PUB_XML_STATE_CALENDAR_AVAIL
3427 * @param activity (%s) XML string as SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY
3428 * @param meeting_subject (%s) Ex.: Customer Meeting
3429 * @param meeting_location (%s) Ex.: Conf Room 100
3431 #define SIPE_PUB_XML_STATE_CALENDAR \
3432 "<publication categoryName=\"state\" instance=\"%u\" container=\"2\" version=\"%u\" expireType=\"endpoint\">"\
3433 "<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"false\" uri=\"%s\" startTime=\"%s\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"calendarState\">"\
3434 "%s"\
3435 "%s"\
3436 "<endpointLocation/>"\
3437 "<meetingSubject>%s</meetingSubject>"\
3438 "<meetingLocation>%s</meetingLocation>"\
3439 "</state>"\
3440 "</publication>"\
3441 "<publication categoryName=\"state\" instance=\"%u\" container=\"3\" version=\"%u\" expireType=\"endpoint\">"\
3442 "<state xmlns=\"http://schemas.microsoft.com/2006/09/sip/state\" manual=\"false\" uri=\"%s\" startTime=\"%s\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:type=\"calendarState\">"\
3443 "%s"\
3444 "%s"\
3445 "<endpointLocation/>"\
3446 "<meetingSubject>%s</meetingSubject>"\
3447 "<meetingLocation>%s</meetingLocation>"\
3448 "</state>"\
3449 "</publication>"
3451 * Publishes to clear 'calendarState' category
3452 * @param instance (%u) Ex.: 1251210982
3453 * @param version (%u) Ex.: 1
3455 #define SIPE_PUB_XML_STATE_CALENDAR_CLEAR \
3456 "<publication categoryName=\"state\" instance=\"%u\" container=\"2\" version=\"%u\" expireType=\"endpoint\" expires=\"0\"/>"\
3457 "<publication categoryName=\"state\" instance=\"%u\" container=\"3\" version=\"%u\" expireType=\"endpoint\" expires=\"0\"/>"
3460 * Publishes to clear any category
3461 * @param category_name (%s) Ex.: state
3462 * @param instance (%u) Ex.: 536870912
3463 * @param container (%u) Ex.: 3
3464 * @param version (%u) Ex.: 1
3465 * @param expireType (%s) Ex.: static
3467 #define SIPE_PUB_XML_PUBLICATION_CLEAR \
3468 "<publication categoryName=\"%s\" instance=\"%u\" container=\"%u\" version=\"%u\" expireType=\"%s\" expires=\"0\"/>"
3471 * Publishes 'note' category.
3472 * @param instance (%u) Ex.: 2135971629; 0 for personal
3473 * @param container (%u) Ex.: 200
3474 * @param version (%u) Ex.: 2
3475 * @param type (%s) Ex.: personal or OOF
3476 * @param startTime_attr (%s) Ex.: startTime="2008-01-11T19:00:00Z"
3477 * @param endTime_attr (%s) Ex.: endTime="2008-01-15T19:00:00Z"
3478 * @param body (%s) Ex.: In the office
3480 #define SIPE_PUB_XML_NOTE \
3481 "<publication categoryName=\"note\" instance=\"%u\" container=\"%u\" version=\"%d\" expireType=\"static\">"\
3482 "<note xmlns=\"http://schemas.microsoft.com/2006/09/sip/note\">"\
3483 "<body type=\"%s\" uri=\"\"%s%s>%s</body>"\
3484 "</note>"\
3485 "</publication>"
3488 * Only Busy and OOF calendar event are published.
3489 * Different instances are used for that.
3491 * Must be g_free'd after use.
3493 static gchar *
3494 sipe_publish_get_category_state_calendar(struct sipe_core_private *sipe_private,
3495 struct sipe_cal_event *event,
3496 const char *uri,
3497 int cal_satus)
3499 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
3500 gchar *start_time_str;
3501 int availability = 0;
3502 gchar *res;
3503 gchar *tmp = NULL;
3504 guint instance = (cal_satus == SIPE_CAL_OOF) ?
3505 sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_CALENDAR_OOF) :
3506 sipe_get_pub_instance(sipe_private, SIPE_PUB_STATE_CALENDAR);
3508 /* key is <category><instance><container> */
3509 gchar *key_2 = g_strdup_printf("<%s><%u><%u>", "state", instance, 2);
3510 gchar *key_3 = g_strdup_printf("<%s><%u><%u>", "state", instance, 3);
3511 struct sipe_publication *publication_2 =
3512 g_hash_table_lookup(g_hash_table_lookup(sip->our_publications, "state"), key_2);
3513 struct sipe_publication *publication_3 =
3514 g_hash_table_lookup(g_hash_table_lookup(sip->our_publications, "state"), key_3);
3516 g_free(key_2);
3517 g_free(key_3);
3519 if (!publication_3 && !event) { /* was nothing, have nothing, exiting */
3520 SIPE_DEBUG_INFO("sipe_publish_get_category_state_calendar: "
3521 "Exiting as no publication and no event for cal_satus:%d", cal_satus);
3522 return NULL;
3525 if (event &&
3526 publication_3 &&
3527 (publication_3->availability == availability) &&
3528 sipe_strequal(publication_3->cal_event_hash, (tmp = sipe_cal_event_hash(event))))
3530 g_free(tmp);
3531 SIPE_DEBUG_INFO("sipe_publish_get_category_state_calendar: "
3532 "cal state has NOT changed for cal_satus:%d. Exiting.", cal_satus);
3533 return NULL; /* nothing to update */
3535 g_free(tmp);
3537 if (event &&
3538 (event->cal_status == SIPE_CAL_BUSY ||
3539 event->cal_status == SIPE_CAL_OOF))
3541 gchar *availability_xml_str = NULL;
3542 gchar *activity_xml_str = NULL;
3543 gchar *escaped_subject = event->subject ? g_markup_escape_text(event->subject, -1) : NULL;
3544 gchar *escaped_location = event->location ? g_markup_escape_text(event->location, -1) : NULL;
3546 if (event->cal_status == SIPE_CAL_BUSY) {
3547 availability_xml_str = g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_AVAIL, 6500);
3550 if (event->cal_status == SIPE_CAL_BUSY && event->is_meeting) {
3551 activity_xml_str = g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY,
3552 sipe_activity_map[SIPE_ACTIVITY_IN_MEETING].token,
3553 "minAvailability=\"6500\"",
3554 "maxAvailability=\"8999\"");
3555 } else if (event->cal_status == SIPE_CAL_OOF) {
3556 activity_xml_str = g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_ACTIVITY,
3557 sipe_activity_map[SIPE_ACTIVITY_OOF].token,
3558 "minAvailability=\"12000\"",
3559 "");
3561 start_time_str = sipe_utils_time_to_str(event->start_time);
3563 res = g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR,
3564 instance,
3565 publication_2 ? publication_2->version : 0,
3566 uri,
3567 start_time_str,
3568 availability_xml_str ? availability_xml_str : "",
3569 activity_xml_str ? activity_xml_str : "",
3570 escaped_subject ? escaped_subject : "",
3571 escaped_location ? escaped_location : "",
3573 instance,
3574 publication_3 ? publication_3->version : 0,
3575 uri,
3576 start_time_str,
3577 availability_xml_str ? availability_xml_str : "",
3578 activity_xml_str ? activity_xml_str : "",
3579 escaped_subject ? escaped_subject : "",
3580 escaped_location ? escaped_location : ""
3582 g_free(escaped_location);
3583 g_free(escaped_subject);
3584 g_free(start_time_str);
3585 g_free(availability_xml_str);
3586 g_free(activity_xml_str);
3589 else /* including !event, SIPE_CAL_FREE, SIPE_CAL_TENTATIVE */
3591 res = g_strdup_printf(SIPE_PUB_XML_STATE_CALENDAR_CLEAR,
3592 instance,
3593 publication_2 ? publication_2->version : 0,
3595 instance,
3596 publication_3 ? publication_3->version : 0
3600 return res;
3604 * Returns 'machineState' XML part for publication.
3605 * Must be g_free'd after use.
3607 static gchar *
3608 sipe_publish_get_category_state_machine(struct sipe_core_private *sipe_private)
3610 return sipe_publish_get_category_state(sipe_private, FALSE);
3614 * Returns 'userState' XML part for publication.
3615 * Must be g_free'd after use.
3617 static gchar *
3618 sipe_publish_get_category_state_user(struct sipe_core_private *sipe_private)
3620 return sipe_publish_get_category_state(sipe_private, TRUE);
3625 * Returns 'note' XML part for publication.
3626 * Must be g_free'd after use.
3628 * Protocol format for Note is plain text.
3630 * @param note a note in Sipe internal HTML format
3631 * @param note_type either personal or OOF
3633 static gchar *
3634 sipe_publish_get_category_note(struct sipe_core_private *sipe_private,
3635 const char *note, /* html */
3636 const char *note_type,
3637 time_t note_start,
3638 time_t note_end)
3640 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
3641 guint instance = sipe_strequal("OOF", note_type) ? sipe_get_pub_instance(sipe_private, SIPE_PUB_NOTE_OOF) : 0;
3642 /* key is <category><instance><container> */
3643 gchar *key_note_200 = g_strdup_printf("<%s><%u><%u>", "note", instance, 200);
3644 gchar *key_note_300 = g_strdup_printf("<%s><%u><%u>", "note", instance, 300);
3645 gchar *key_note_400 = g_strdup_printf("<%s><%u><%u>", "note", instance, 400);
3647 struct sipe_publication *publication_note_200 =
3648 g_hash_table_lookup(g_hash_table_lookup(sip->our_publications, "note"), key_note_200);
3649 struct sipe_publication *publication_note_300 =
3650 g_hash_table_lookup(g_hash_table_lookup(sip->our_publications, "note"), key_note_300);
3651 struct sipe_publication *publication_note_400 =
3652 g_hash_table_lookup(g_hash_table_lookup(sip->our_publications, "note"), key_note_400);
3654 char *tmp = note ? sipe_backend_markup_strip_html(note) : NULL;
3655 char *n1 = tmp ? g_markup_escape_text(tmp, -1) : NULL;
3656 const char *n2 = publication_note_200 ? publication_note_200->note : NULL;
3657 char *res, *tmp1, *tmp2, *tmp3;
3658 char *start_time_attr;
3659 char *end_time_attr;
3661 g_free(tmp);
3662 tmp = NULL;
3663 g_free(key_note_200);
3664 g_free(key_note_300);
3665 g_free(key_note_400);
3667 /* we even need to republish empty note */
3668 if (sipe_strequal(n1, n2))
3670 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_note: note has NOT changed. Exiting.");
3671 g_free(n1);
3672 return NULL; /* nothing to update */
3675 start_time_attr = note_start ? g_strdup_printf(" startTime=\"%s\"", (tmp = sipe_utils_time_to_str(note_start))) : NULL;
3676 g_free(tmp);
3677 tmp = NULL;
3678 end_time_attr = note_end ? g_strdup_printf(" endTime=\"%s\"", (tmp = sipe_utils_time_to_str(note_end))) : NULL;
3679 g_free(tmp);
3681 if (n1) {
3682 tmp1 = g_strdup_printf(SIPE_PUB_XML_NOTE,
3683 instance,
3684 200,
3685 publication_note_200 ? publication_note_200->version : 0,
3686 note_type,
3687 start_time_attr ? start_time_attr : "",
3688 end_time_attr ? end_time_attr : "",
3689 n1);
3691 tmp2 = g_strdup_printf(SIPE_PUB_XML_NOTE,
3692 instance,
3693 300,
3694 publication_note_300 ? publication_note_300->version : 0,
3695 note_type,
3696 start_time_attr ? start_time_attr : "",
3697 end_time_attr ? end_time_attr : "",
3698 n1);
3700 tmp3 = g_strdup_printf(SIPE_PUB_XML_NOTE,
3701 instance,
3702 400,
3703 publication_note_400 ? publication_note_400->version : 0,
3704 note_type,
3705 start_time_attr ? start_time_attr : "",
3706 end_time_attr ? end_time_attr : "",
3707 n1);
3708 } else {
3709 tmp1 = g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR,
3710 "note",
3711 instance,
3712 200,
3713 publication_note_200 ? publication_note_200->version : 0,
3714 "static");
3715 tmp2 = g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR,
3716 "note",
3717 instance,
3718 300,
3719 publication_note_200 ? publication_note_200->version : 0,
3720 "static");
3721 tmp3 = g_strdup_printf( SIPE_PUB_XML_PUBLICATION_CLEAR,
3722 "note",
3723 instance,
3724 400,
3725 publication_note_200 ? publication_note_200->version : 0,
3726 "static");
3728 res = g_strconcat(tmp1, tmp2, tmp3, NULL);
3730 g_free(start_time_attr);
3731 g_free(end_time_attr);
3732 g_free(tmp1);
3733 g_free(tmp2);
3734 g_free(tmp3);
3735 g_free(n1);
3737 return res;
3741 * Publishes 'calendarData' category's WorkingHours.
3743 * @param version (%u) Ex.: 1
3744 * @param email (%s) Ex.: alice@cosmo.local
3745 * @param working_hours_xml_str (%s) Ex.: <WorkingHours xmlns=.....
3747 * @param version (%u)
3749 * @param version (%u)
3750 * @param email (%s)
3751 * @param working_hours_xml_str (%s)
3753 * @param version (%u)
3754 * @param email (%s)
3755 * @param working_hours_xml_str (%s)
3757 * @param version (%u)
3758 * @param email (%s)
3759 * @param working_hours_xml_str (%s)
3761 * @param version (%u)
3763 #define SIPE_PUB_XML_WORKING_HOURS \
3764 "<publication categoryName=\"calendarData\" instance=\"0\" container=\"1\" version=\"%d\" expireType=\"static\">"\
3765 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">%s"\
3766 "</calendarData>"\
3767 "</publication>"\
3768 "<publication categoryName=\"calendarData\" instance=\"0\" container=\"100\" version=\"%d\" expireType=\"static\">"\
3769 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\"/>"\
3770 "</publication>"\
3771 "<publication categoryName=\"calendarData\" instance=\"0\" container=\"200\" version=\"%d\" expireType=\"static\">"\
3772 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">%s"\
3773 "</calendarData>"\
3774 "</publication>"\
3775 "<publication categoryName=\"calendarData\" instance=\"0\" container=\"300\" version=\"%d\" expireType=\"static\">"\
3776 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">%s"\
3777 "</calendarData>"\
3778 "</publication>"\
3779 "<publication categoryName=\"calendarData\" instance=\"0\" container=\"400\" version=\"%d\" expireType=\"static\">"\
3780 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">%s"\
3781 "</calendarData>"\
3782 "</publication>"\
3783 "<publication categoryName=\"calendarData\" instance=\"0\" container=\"32000\" version=\"%d\" expireType=\"static\">"\
3784 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\"/>"\
3785 "</publication>"
3788 * Returns 'calendarData' XML part with WorkingHours for publication.
3789 * Must be g_free'd after use.
3791 static gchar *
3792 sipe_publish_get_category_cal_working_hours(struct sipe_core_private *sipe_private)
3794 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
3795 struct sipe_calendar* cal = sip->cal;
3797 /* key is <category><instance><container> */
3798 gchar *key_cal_1 = g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 1);
3799 gchar *key_cal_100 = g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 100);
3800 gchar *key_cal_200 = g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 200);
3801 gchar *key_cal_300 = g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 300);
3802 gchar *key_cal_400 = g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 400);
3803 gchar *key_cal_32000 = g_strdup_printf("<%s><%u><%u>", "calendarData", 0, 32000);
3805 struct sipe_publication *publication_cal_1 =
3806 g_hash_table_lookup(g_hash_table_lookup(sip->our_publications, "calendarData"), key_cal_1);
3807 struct sipe_publication *publication_cal_100 =
3808 g_hash_table_lookup(g_hash_table_lookup(sip->our_publications, "calendarData"), key_cal_100);
3809 struct sipe_publication *publication_cal_200 =
3810 g_hash_table_lookup(g_hash_table_lookup(sip->our_publications, "calendarData"), key_cal_200);
3811 struct sipe_publication *publication_cal_300 =
3812 g_hash_table_lookup(g_hash_table_lookup(sip->our_publications, "calendarData"), key_cal_300);
3813 struct sipe_publication *publication_cal_400 =
3814 g_hash_table_lookup(g_hash_table_lookup(sip->our_publications, "calendarData"), key_cal_400);
3815 struct sipe_publication *publication_cal_32000 =
3816 g_hash_table_lookup(g_hash_table_lookup(sip->our_publications, "calendarData"), key_cal_32000);
3818 const char *n1 = cal ? cal->working_hours_xml_str : NULL;
3819 const char *n2 = publication_cal_300 ? publication_cal_300->working_hours_xml_str : NULL;
3821 g_free(key_cal_1);
3822 g_free(key_cal_100);
3823 g_free(key_cal_200);
3824 g_free(key_cal_300);
3825 g_free(key_cal_400);
3826 g_free(key_cal_32000);
3828 if (!cal || is_empty(cal->email) || is_empty(cal->working_hours_xml_str)) {
3829 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_working_hours: no data to publish, exiting");
3830 return NULL;
3833 if (sipe_strequal(n1, n2))
3835 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_working_hours: WorkingHours has NOT changed. Exiting.");
3836 return NULL; /* nothing to update */
3839 return g_strdup_printf(SIPE_PUB_XML_WORKING_HOURS,
3840 /* 1 */
3841 publication_cal_1 ? publication_cal_1->version : 0,
3842 cal->email,
3843 cal->working_hours_xml_str,
3844 /* 100 - Public */
3845 publication_cal_100 ? publication_cal_100->version : 0,
3846 /* 200 - Company */
3847 publication_cal_200 ? publication_cal_200->version : 0,
3848 cal->email,
3849 cal->working_hours_xml_str,
3850 /* 300 - Team */
3851 publication_cal_300 ? publication_cal_300->version : 0,
3852 cal->email,
3853 cal->working_hours_xml_str,
3854 /* 400 - Personal */
3855 publication_cal_400 ? publication_cal_400->version : 0,
3856 cal->email,
3857 cal->working_hours_xml_str,
3858 /* 32000 - Blocked */
3859 publication_cal_32000 ? publication_cal_32000->version : 0
3864 * Publishes 'calendarData' category's FreeBusy.
3866 * @param instance (%u) Ex.: 1300372959
3867 * @param version (%u) Ex.: 1
3869 * @param instance (%u) Ex.: 1300372959
3870 * @param version (%u) Ex.: 1
3872 * @param instance (%u) Ex.: 1300372959
3873 * @param version (%u) Ex.: 1
3874 * @param email (%s) Ex.: alice@cosmo.local
3875 * @param fb_start_time_str (%s) Ex.: 2009-12-03T00:00:00Z
3876 * @param free_busy_base64 (%s) Ex.: AAAAAAAAAAAAAAAAAAAAA.....
3878 * @param instance (%u) Ex.: 1300372959
3879 * @param version (%u) Ex.: 1
3880 * @param email (%s) Ex.: alice@cosmo.local
3881 * @param fb_start_time_str (%s) Ex.: 2009-12-03T00:00:00Z
3882 * @param free_busy_base64 (%s) Ex.: AAAAAAAAAAAAAAAAAAAAA.....
3884 * @param instance (%u) Ex.: 1300372959
3885 * @param version (%u) Ex.: 1
3886 * @param email (%s) Ex.: alice@cosmo.local
3887 * @param fb_start_time_str (%s) Ex.: 2009-12-03T00:00:00Z
3888 * @param free_busy_base64 (%s) Ex.: AAAAAAAAAAAAAAAAAAAAA.....
3890 * @param instance (%u) Ex.: 1300372959
3891 * @param version (%u) Ex.: 1
3893 #define SIPE_PUB_XML_FREE_BUSY \
3894 "<publication categoryName=\"calendarData\" instance=\"%u\" container=\"1\" version=\"%d\" expireType=\"endpoint\">"\
3895 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\"/>"\
3896 "</publication>"\
3897 "<publication categoryName=\"calendarData\" instance=\"%u\" container=\"100\" version=\"%d\" expireType=\"endpoint\">"\
3898 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\"/>"\
3899 "</publication>"\
3900 "<publication categoryName=\"calendarData\" instance=\"%u\" container=\"200\" version=\"%d\" expireType=\"endpoint\">"\
3901 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">"\
3902 "<freeBusy startTime=\"%s\" granularity=\"PT15M\" encodingVersion=\"1\">%s</freeBusy>"\
3903 "</calendarData>"\
3904 "</publication>"\
3905 "<publication categoryName=\"calendarData\" instance=\"%u\" container=\"300\" version=\"%d\" expireType=\"endpoint\">"\
3906 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">"\
3907 "<freeBusy startTime=\"%s\" granularity=\"PT15M\" encodingVersion=\"1\">%s</freeBusy>"\
3908 "</calendarData>"\
3909 "</publication>"\
3910 "<publication categoryName=\"calendarData\" instance=\"%u\" container=\"400\" version=\"%d\" expireType=\"endpoint\">"\
3911 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\" mailboxID=\"%s\">"\
3912 "<freeBusy startTime=\"%s\" granularity=\"PT15M\" encodingVersion=\"1\">%s</freeBusy>"\
3913 "</calendarData>"\
3914 "</publication>"\
3915 "<publication categoryName=\"calendarData\" instance=\"%u\" container=\"32000\" version=\"%d\" expireType=\"endpoint\">"\
3916 "<calendarData xmlns=\"http://schemas.microsoft.com/2006/09/sip/calendarData\"/>"\
3917 "</publication>"
3920 * Returns 'calendarData' XML part with FreeBusy for publication.
3921 * Must be g_free'd after use.
3923 static gchar *
3924 sipe_publish_get_category_cal_free_busy(struct sipe_core_private *sipe_private)
3926 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
3927 struct sipe_calendar* cal = sip->cal;
3928 guint cal_data_instance = sipe_get_pub_instance(sipe_private, SIPE_PUB_CALENDAR_DATA);
3929 char *fb_start_str;
3930 char *free_busy_base64;
3931 /* const char *st; */
3932 /* const char *fb; */
3933 char *res;
3935 /* key is <category><instance><container> */
3936 gchar *key_cal_1 = g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 1);
3937 gchar *key_cal_100 = g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 100);
3938 gchar *key_cal_200 = g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 200);
3939 gchar *key_cal_300 = g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 300);
3940 gchar *key_cal_400 = g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 400);
3941 gchar *key_cal_32000 = g_strdup_printf("<%s><%u><%u>", "calendarData", cal_data_instance, 32000);
3943 struct sipe_publication *publication_cal_1 =
3944 g_hash_table_lookup(g_hash_table_lookup(sip->our_publications, "calendarData"), key_cal_1);
3945 struct sipe_publication *publication_cal_100 =
3946 g_hash_table_lookup(g_hash_table_lookup(sip->our_publications, "calendarData"), key_cal_100);
3947 struct sipe_publication *publication_cal_200 =
3948 g_hash_table_lookup(g_hash_table_lookup(sip->our_publications, "calendarData"), key_cal_200);
3949 struct sipe_publication *publication_cal_300 =
3950 g_hash_table_lookup(g_hash_table_lookup(sip->our_publications, "calendarData"), key_cal_300);
3951 struct sipe_publication *publication_cal_400 =
3952 g_hash_table_lookup(g_hash_table_lookup(sip->our_publications, "calendarData"), key_cal_400);
3953 struct sipe_publication *publication_cal_32000 =
3954 g_hash_table_lookup(g_hash_table_lookup(sip->our_publications, "calendarData"), key_cal_32000);
3956 g_free(key_cal_1);
3957 g_free(key_cal_100);
3958 g_free(key_cal_200);
3959 g_free(key_cal_300);
3960 g_free(key_cal_400);
3961 g_free(key_cal_32000);
3963 if (!cal || is_empty(cal->email) || !cal->fb_start || is_empty(cal->free_busy)) {
3964 SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_free_busy: no data to publish, exiting");
3965 return NULL;
3968 fb_start_str = sipe_utils_time_to_str(cal->fb_start);
3969 free_busy_base64 = sipe_cal_get_freebusy_base64(cal->free_busy);
3971 /* we will rebuplish the same data to refresh publication time,
3972 * so if data from multiple sources, most recent will be choosen
3974 // st = publication_cal_300 ? publication_cal_300->fb_start_str : NULL;
3975 // fb = publication_cal_300 ? publication_cal_300->free_busy_base64 : NULL;
3977 //if (sipe_strequal(st, fb_start_str) && sipe_strequal(fb, free_busy_base64))
3979 // SIPE_DEBUG_INFO_NOFORMAT("sipe_publish_get_category_cal_free_busy: FreeBusy has NOT changed. Exiting.");
3980 // g_free(fb_start_str);
3981 // g_free(free_busy_base64);
3982 // return NULL; /* nothing to update */
3985 res = g_strdup_printf(SIPE_PUB_XML_FREE_BUSY,
3986 /* 1 */
3987 cal_data_instance,
3988 publication_cal_1 ? publication_cal_1->version : 0,
3989 /* 100 - Public */
3990 cal_data_instance,
3991 publication_cal_100 ? publication_cal_100->version : 0,
3992 /* 200 - Company */
3993 cal_data_instance,
3994 publication_cal_200 ? publication_cal_200->version : 0,
3995 cal->email,
3996 fb_start_str,
3997 free_busy_base64,
3998 /* 300 - Team */
3999 cal_data_instance,
4000 publication_cal_300 ? publication_cal_300->version : 0,
4001 cal->email,
4002 fb_start_str,
4003 free_busy_base64,
4004 /* 400 - Personal */
4005 cal_data_instance,
4006 publication_cal_400 ? publication_cal_400->version : 0,
4007 cal->email,
4008 fb_start_str,
4009 free_busy_base64,
4010 /* 32000 - Blocked */
4011 cal_data_instance,
4012 publication_cal_32000 ? publication_cal_32000->version : 0
4015 g_free(fb_start_str);
4016 g_free(free_busy_base64);
4017 return res;
4021 * Publishes categories.
4022 * @param uri (%s) Self URI. Ex.: sip:alice7@boston.local
4023 * @param publications (%s) XML publications
4025 #define SIPE_SEND_PRESENCE \
4026 "<publish xmlns=\"http://schemas.microsoft.com/2006/09/sip/rich-presence\">"\
4027 "<publications uri=\"%s\">"\
4028 "%s"\
4029 "</publications>"\
4030 "</publish>"
4032 static void send_presence_publish(struct sipe_core_private *sipe_private,
4033 const char *publications)
4035 gchar *uri;
4036 gchar *doc;
4037 gchar *tmp;
4038 gchar *hdr;
4040 uri = sip_uri_self(sipe_private);
4041 doc = g_strdup_printf(SIPE_SEND_PRESENCE,
4042 uri,
4043 publications);
4045 tmp = get_contact(sipe_private);
4046 hdr = g_strdup_printf("Contact: %s\r\n"
4047 "Content-Type: application/msrtc-category-publish+xml\r\n", tmp);
4049 sip_transport_service(sipe_private,
4050 uri,
4051 hdr,
4052 doc,
4053 process_send_presence_category_publish_response);
4055 g_free(tmp);
4056 g_free(hdr);
4057 g_free(uri);
4058 g_free(doc);
4061 static void
4062 send_publish_category_initial(struct sipe_core_private *sipe_private)
4064 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
4065 gchar *pub_device = sipe_publish_get_category_device(sipe_private);
4066 gchar *pub_machine;
4067 gchar *publications;
4069 g_free(sip->status);
4070 sip->status = g_strdup(SIPE_STATUS_ID_AVAILABLE); /* our initial state */
4072 pub_machine = sipe_publish_get_category_state_machine(sipe_private);
4073 publications = g_strdup_printf("%s%s",
4074 pub_device,
4075 pub_machine ? pub_machine : "");
4076 g_free(pub_device);
4077 g_free(pub_machine);
4079 send_presence_publish(sipe_private, publications);
4080 g_free(publications);
4083 static void
4084 send_presence_category_publish(struct sipe_core_private *sipe_private)
4086 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
4087 gchar *pub_state = sipe_is_user_state(sipe_private) ?
4088 sipe_publish_get_category_state_user(sipe_private) :
4089 sipe_publish_get_category_state_machine(sipe_private);
4090 gchar *pub_note = sipe_publish_get_category_note(sipe_private,
4091 sip->note,
4092 sip->is_oof_note ? "OOF" : "personal",
4095 gchar *publications;
4097 if (!pub_state && !pub_note) {
4098 SIPE_DEBUG_INFO_NOFORMAT("send_presence_category_publish: nothing has changed. Exiting.");
4099 return;
4102 publications = g_strdup_printf("%s%s",
4103 pub_state ? pub_state : "",
4104 pub_note ? pub_note : "");
4106 g_free(pub_state);
4107 g_free(pub_note);
4109 send_presence_publish(sipe_private, publications);
4110 g_free(publications);
4114 * Publishes self status
4115 * based on own calendar information.
4117 * For 2007+
4119 void
4120 publish_calendar_status_self(struct sipe_core_private *sipe_private,
4121 SIPE_UNUSED_PARAMETER void *unused)
4123 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
4124 struct sipe_cal_event* event = NULL;
4125 gchar *pub_cal_working_hours = NULL;
4126 gchar *pub_cal_free_busy = NULL;
4127 gchar *pub_calendar = NULL;
4128 gchar *pub_calendar2 = NULL;
4129 gchar *pub_oof_note = NULL;
4130 const gchar *oof_note;
4131 time_t oof_start = 0;
4132 time_t oof_end = 0;
4134 if (!sip->cal) {
4135 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self() no calendar data.");
4136 return;
4139 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self() started.");
4140 if (sip->cal->cal_events) {
4141 event = sipe_cal_get_event(sip->cal->cal_events, time(NULL));
4144 if (!event) {
4145 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self: current event is NULL");
4146 } else {
4147 char *desc = sipe_cal_event_describe(event);
4148 SIPE_DEBUG_INFO("publish_calendar_status_self: current event is:\n%s", desc ? desc : "");
4149 g_free(desc);
4152 /* Logic
4153 if OOF
4154 OOF publish, Busy clean
4155 ilse if Busy
4156 OOF clean, Busy publish
4157 else
4158 OOF clean, Busy clean
4160 if (event && event->cal_status == SIPE_CAL_OOF) {
4161 pub_calendar = sipe_publish_get_category_state_calendar(sipe_private, event, sip->cal->email, SIPE_CAL_OOF);
4162 pub_calendar2 = sipe_publish_get_category_state_calendar(sipe_private, NULL, sip->cal->email, SIPE_CAL_BUSY);
4163 } else if (event && event->cal_status == SIPE_CAL_BUSY) {
4164 pub_calendar = sipe_publish_get_category_state_calendar(sipe_private, NULL, sip->cal->email, SIPE_CAL_OOF);
4165 pub_calendar2 = sipe_publish_get_category_state_calendar(sipe_private, event, sip->cal->email, SIPE_CAL_BUSY);
4166 } else {
4167 pub_calendar = sipe_publish_get_category_state_calendar(sipe_private, NULL, sip->cal->email, SIPE_CAL_OOF);
4168 pub_calendar2 = sipe_publish_get_category_state_calendar(sipe_private, NULL, sip->cal->email, SIPE_CAL_BUSY);
4171 oof_note = sipe_ews_get_oof_note(sip->cal);
4172 if (sipe_strequal("Scheduled", sip->cal->oof_state)) {
4173 oof_start = sip->cal->oof_start;
4174 oof_end = sip->cal->oof_end;
4176 pub_oof_note = sipe_publish_get_category_note(sipe_private, oof_note, "OOF", oof_start, oof_end);
4178 pub_cal_working_hours = sipe_publish_get_category_cal_working_hours(sipe_private);
4179 pub_cal_free_busy = sipe_publish_get_category_cal_free_busy(sipe_private);
4181 if (!pub_cal_working_hours && !pub_cal_free_busy && !pub_calendar && !pub_calendar2 && !pub_oof_note) {
4182 SIPE_DEBUG_INFO_NOFORMAT("publish_calendar_status_self: nothing has changed.");
4183 } else {
4184 gchar *publications = g_strdup_printf("%s%s%s%s%s",
4185 pub_cal_working_hours ? pub_cal_working_hours : "",
4186 pub_cal_free_busy ? pub_cal_free_busy : "",
4187 pub_calendar ? pub_calendar : "",
4188 pub_calendar2 ? pub_calendar2 : "",
4189 pub_oof_note ? pub_oof_note : "");
4191 send_presence_publish(sipe_private, publications);
4192 g_free(publications);
4195 g_free(pub_cal_working_hours);
4196 g_free(pub_cal_free_busy);
4197 g_free(pub_calendar);
4198 g_free(pub_calendar2);
4199 g_free(pub_oof_note);
4201 /* repeat scheduling */
4202 sipe_sched_calendar_status_self_publish(sipe_private, time(NULL));
4205 static void send_presence_status(struct sipe_core_private *sipe_private,
4206 SIPE_UNUSED_PARAMETER void *unused)
4208 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
4209 PurpleStatus * status = purple_account_get_active_status(sip->account);
4211 if (!status) return;
4213 SIPE_DEBUG_INFO("send_presence_status: status: %s (%s)",
4214 purple_status_get_id(status) ? purple_status_get_id(status) : "",
4215 sipe_is_user_state(sipe_private) ? "USER" : "MACHINE");
4217 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
4218 send_presence_category_publish(sipe_private);
4219 } else {
4220 send_presence_soap(sipe_private, FALSE);
4224 static guint sipe_ht_hash_nick(const char *nick)
4226 char *lc = g_utf8_strdown(nick, -1);
4227 guint bucket = g_str_hash(lc);
4228 g_free(lc);
4230 return bucket;
4233 static gboolean sipe_ht_equals_nick(const char *nick1, const char *nick2)
4235 char *nick1_norm = NULL;
4236 char *nick2_norm = NULL;
4237 gboolean equal;
4239 if (nick1 == NULL && nick2 == NULL) return TRUE;
4240 if (nick1 == NULL || nick2 == NULL ||
4241 !g_utf8_validate(nick1, -1, NULL) ||
4242 !g_utf8_validate(nick2, -1, NULL)) return FALSE;
4244 nick1_norm = g_utf8_casefold(nick1, -1);
4245 nick2_norm = g_utf8_casefold(nick2, -1);
4246 equal = g_utf8_collate(nick1_norm, nick2_norm) == 0;
4247 g_free(nick2_norm);
4248 g_free(nick1_norm);
4250 return equal;
4253 /* temporary function */
4254 void sipe_purple_setup(struct sipe_core_public *sipe_public,
4255 PurpleConnection *gc)
4257 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA;
4258 sip->gc = gc;
4259 sip->account = purple_connection_get_account(gc);
4262 struct sipe_core_public *sipe_core_allocate(const gchar *signin_name,
4263 const gchar *login_domain,
4264 const gchar *login_account,
4265 const gchar *password,
4266 const gchar *email,
4267 const gchar *email_url,
4268 const gchar **errmsg)
4270 struct sipe_core_private *sipe_private;
4271 struct sipe_account_data *sip;
4272 gchar **user_domain;
4274 SIPE_DEBUG_INFO("sipe_core_allocate: signin_name '%s'", signin_name);
4276 /* ensure that sign-in name doesn't contain invalid characters */
4277 if (strpbrk(signin_name, "\t\v\r\n") != NULL) {
4278 *errmsg = _("SIP Exchange user name contains invalid characters");
4279 return NULL;
4282 /* ensure that sign-in name format is name@domain */
4283 if (!strchr(signin_name, '@') ||
4284 g_str_has_prefix(signin_name, "@") ||
4285 g_str_has_suffix(signin_name, "@")) {
4286 *errmsg = _("User name should be a valid SIP URI\nExample: user@company.com");
4287 return NULL;
4290 /* ensure that email format is name@domain (if provided) */
4291 if (!is_empty(email) &&
4292 (!strchr(email, '@') ||
4293 g_str_has_prefix(email, "@") ||
4294 g_str_has_suffix(email, "@")))
4296 *errmsg = _("Email address should be valid if provided\nExample: user@company.com");
4297 return NULL;
4300 /* ensure that user name doesn't contain spaces */
4301 user_domain = g_strsplit(signin_name, "@", 2);
4302 SIPE_DEBUG_INFO("sipe_core_allocate: user '%s' domain '%s'", user_domain[0], user_domain[1]);
4303 if (strchr(user_domain[0], ' ') != NULL) {
4304 g_strfreev(user_domain);
4305 *errmsg = _("SIP Exchange user name contains whitespace");
4306 return NULL;
4309 /* ensure that email_url is in proper format if enabled (if provided).
4310 * Example (Exchange): https://server.company.com/EWS/Exchange.asmx
4311 * Example (Domino) : https://[domino_server]/[mail_database_name].nsf
4313 if (!is_empty(email_url)) {
4314 char *tmp = g_ascii_strdown(email_url, -1);
4315 if (!g_str_has_prefix(tmp, "https://"))
4317 g_free(tmp);
4318 g_strfreev(user_domain);
4319 *errmsg = _("Email services URL should be valid if provided\n"
4320 "Example: https://exchange.corp.com/EWS/Exchange.asmx\n"
4321 "Example: https://domino.corp.com/maildatabase.nsf");
4322 return NULL;
4324 g_free(tmp);
4327 sipe_private = g_new0(struct sipe_core_private, 1);
4328 sipe_private->temporary = sip = g_new0(struct sipe_account_data, 1);
4329 sip->subscribed_buddies = FALSE;
4330 sip->initial_state_published = FALSE;
4331 sipe_private->username = g_strdup(signin_name);
4332 sip->email = is_empty(email) ? g_strdup(signin_name) : g_strdup(email);
4333 sip->authdomain = is_empty(login_domain) ? NULL : g_strdup(login_domain);
4334 sip->authuser = is_empty(login_account) ? NULL : g_strdup(login_account);
4335 sip->password = g_strdup(password);
4336 sipe_private->public.sip_name = g_strdup(user_domain[0]);
4337 sipe_private->public.sip_domain = g_strdup(user_domain[1]);
4338 g_strfreev(user_domain);
4340 sipe_private->buddies = g_hash_table_new((GHashFunc)sipe_ht_hash_nick, (GEqualFunc)sipe_ht_equals_nick);
4341 sip->our_publications = g_hash_table_new_full(g_str_hash, g_str_equal,
4342 g_free, (GDestroyNotify)g_hash_table_destroy);
4343 sipe_subscriptions_init(sipe_private);
4344 sip->status = g_strdup(SIPE_STATUS_ID_UNKNOWN);
4346 return((struct sipe_core_public *)sipe_private);
4349 static void
4350 sipe_blist_menu_free_containers(struct sipe_core_private *sipe_private);
4352 void sipe_connection_cleanup(struct sipe_core_private *sipe_private)
4354 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
4356 g_free(sipe_private->epid);
4357 sipe_private->epid = NULL;
4359 sip_transport_disconnect(sipe_private);
4361 sipe_schedule_cancel_all(sipe_private);
4363 if (sip->allow_events) {
4364 GSList *entry = sip->allow_events;
4365 while (entry) {
4366 g_free(entry->data);
4367 entry = entry->next;
4370 g_slist_free(sip->allow_events);
4372 if (sip->containers) {
4373 GSList *entry = sip->containers;
4374 while (entry) {
4375 free_container((struct sipe_container *)entry->data);
4376 entry = entry->next;
4379 g_slist_free(sip->containers);
4381 /* libpurple memory leak workaround */
4382 sipe_blist_menu_free_containers(sipe_private);
4384 if (sipe_private->contact)
4385 g_free(sipe_private->contact);
4386 sipe_private->contact = NULL;
4387 if (sip->regcallid)
4388 g_free(sip->regcallid);
4389 sip->regcallid = NULL;
4391 if (sipe_private->focus_factory_uri)
4392 g_free(sipe_private->focus_factory_uri);
4393 sipe_private->focus_factory_uri = NULL;
4395 if (sip->cal) {
4396 sipe_cal_calendar_free(sip->cal);
4398 sip->cal = NULL;
4400 sipe_groupchat_free(sipe_private);
4404 * A callback for g_hash_table_foreach_remove
4406 static gboolean sipe_buddy_remove(SIPE_UNUSED_PARAMETER gpointer key, gpointer buddy,
4407 SIPE_UNUSED_PARAMETER gpointer user_data)
4409 sipe_free_buddy((struct sipe_buddy *) buddy);
4411 /* We must return TRUE as the key/value have already been deleted */
4412 return(TRUE);
4415 void sipe_buddy_free_all(struct sipe_core_private *sipe_private)
4417 g_hash_table_foreach_steal(sipe_private->buddies, sipe_buddy_remove, NULL);
4420 static void sipe_searchresults_im_buddy(PurpleConnection *gc, GList *row,
4421 SIPE_UNUSED_PARAMETER void *user_data)
4423 PurpleAccount *acct = purple_connection_get_account(gc);
4424 char *id = sip_uri_from_name((gchar *)g_list_nth_data(row, 0));
4425 PurpleConversation *conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, id, acct);
4426 if (conv == NULL)
4427 conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, acct, id);
4428 purple_conversation_present(conv);
4429 g_free(id);
4432 static void sipe_searchresults_add_buddy(PurpleConnection *gc, GList *row,
4433 SIPE_UNUSED_PARAMETER void *user_data)
4436 purple_blist_request_add_buddy(purple_connection_get_account(gc),
4437 g_list_nth_data(row, 0), _("Other Contacts"), g_list_nth_data(row, 1));
4440 static gboolean process_search_contact_response(struct sipe_core_private *sipe_private,
4441 struct sipmsg *msg,
4442 SIPE_UNUSED_PARAMETER struct transaction *trans)
4444 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
4445 PurpleNotifySearchResults *results;
4446 PurpleNotifySearchColumn *column;
4447 sipe_xml *searchResults;
4448 const sipe_xml *mrow;
4449 int match_count = 0;
4450 gboolean more = FALSE;
4451 gchar *secondary;
4453 /* valid response? */
4454 if (msg->response != 200) {
4455 SIPE_DEBUG_ERROR("process_search_contact_response: request failed (%d)",
4456 msg->response);
4457 purple_notify_error(sip->gc, NULL,
4458 _("Contact search failed"),
4459 NULL);
4460 return(FALSE);
4463 SIPE_DEBUG_INFO("process_search_contact_response: body:\n%s", msg->body ? msg->body : "");
4465 /* valid XML? */
4466 searchResults = sipe_xml_parse(msg->body, msg->bodylen);
4467 if (!searchResults) {
4468 SIPE_DEBUG_INFO_NOFORMAT("process_search_contact_response: no parseable searchResults");
4469 purple_notify_error(sip->gc, NULL,
4470 _("Contact search failed"),
4471 NULL);
4472 return FALSE;
4475 /* any matches? */
4476 mrow = sipe_xml_child(searchResults, "Body/Array/row");
4477 if (!mrow) {
4478 SIPE_DEBUG_ERROR_NOFORMAT("process_search_contact_response: no matches");
4479 purple_notify_error(sip->gc, NULL,
4480 _("No contacts found"),
4481 NULL);
4483 sipe_xml_free(searchResults);
4484 return(FALSE);
4487 /* OK, we found something - show the results to the user */
4488 results = purple_notify_searchresults_new();
4489 if (!results) {
4490 SIPE_DEBUG_ERROR_NOFORMAT("process_search_contact_response: Unable to display the search results.");
4491 purple_notify_error(sip->gc, NULL, _("Unable to display the search results"), NULL);
4493 sipe_xml_free(searchResults);
4494 return FALSE;
4497 column = purple_notify_searchresults_column_new(_("User name"));
4498 purple_notify_searchresults_column_add(results, column);
4500 column = purple_notify_searchresults_column_new(_("Name"));
4501 purple_notify_searchresults_column_add(results, column);
4503 column = purple_notify_searchresults_column_new(_("Company"));
4504 purple_notify_searchresults_column_add(results, column);
4506 column = purple_notify_searchresults_column_new(_("Country"));
4507 purple_notify_searchresults_column_add(results, column);
4509 column = purple_notify_searchresults_column_new(_("Email"));
4510 purple_notify_searchresults_column_add(results, column);
4512 for (/* initialized above */ ; mrow; mrow = sipe_xml_twin(mrow)) {
4513 GList *row = NULL;
4515 gchar **uri_parts = g_strsplit(sipe_xml_attribute(mrow, "uri"), ":", 2);
4516 row = g_list_append(row, g_strdup(uri_parts[1]));
4517 g_strfreev(uri_parts);
4519 row = g_list_append(row, g_strdup(sipe_xml_attribute(mrow, "displayName")));
4520 row = g_list_append(row, g_strdup(sipe_xml_attribute(mrow, "company")));
4521 row = g_list_append(row, g_strdup(sipe_xml_attribute(mrow, "country")));
4522 row = g_list_append(row, g_strdup(sipe_xml_attribute(mrow, "email")));
4524 purple_notify_searchresults_row_add(results, row);
4525 match_count++;
4528 if ((mrow = sipe_xml_child(searchResults, "Body/directorySearch/moreAvailable")) != NULL) {
4529 char *data = sipe_xml_data(mrow);
4530 more = (g_strcasecmp(data, "true") == 0);
4531 g_free(data);
4534 secondary = g_strdup_printf(
4535 dngettext(PACKAGE_NAME,
4536 "Found %d contact%s:",
4537 "Found %d contacts%s:", match_count),
4538 match_count, more ? _(" (more matched your query)") : "");
4540 purple_notify_searchresults_button_add(results, PURPLE_NOTIFY_BUTTON_IM, sipe_searchresults_im_buddy);
4541 purple_notify_searchresults_button_add(results, PURPLE_NOTIFY_BUTTON_ADD, sipe_searchresults_add_buddy);
4542 purple_notify_searchresults(sip->gc, NULL, NULL, secondary, results, NULL, NULL);
4544 g_free(secondary);
4545 sipe_xml_free(searchResults);
4546 return TRUE;
4549 #define SIPE_SOAP_SEARCH_ROW "<m:row m:attrib=\"%s\" m:value=\"%s\"/>"
4551 void sipe_search_contact_with_cb(PurpleConnection *gc, PurpleRequestFields *fields)
4553 GList *entries = purple_request_field_group_get_fields(purple_request_fields_get_groups(fields)->data);
4554 gchar **attrs = g_new(gchar *, g_list_length(entries) + 1);
4555 unsigned i = 0;
4557 if (!attrs) return;
4559 do {
4560 PurpleRequestField *field = entries->data;
4561 const char *id = purple_request_field_get_id(field);
4562 const char *value = purple_request_field_string_get_value(field);
4564 SIPE_DEBUG_INFO("sipe_search_contact_with_cb: %s = '%s'", id, value ? value : "");
4566 if (value != NULL) attrs[i++] = g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW, id, value);
4567 } while ((entries = g_list_next(entries)) != NULL);
4568 attrs[i] = NULL;
4570 if (i > 0) {
4571 struct sipe_core_private *sipe_private = PURPLE_GC_TO_SIPE_CORE_PRIVATE;
4572 gchar *query = g_strjoinv(NULL, attrs);
4573 SIPE_DEBUG_INFO("sipe_search_contact_with_cb: rows:\n%s", query ? query : "");
4574 sip_soap_directory_search(sipe_private,
4575 100,
4576 query,
4577 process_search_contact_response,
4578 NULL);
4579 g_free(query);
4582 g_strfreev(attrs);
4585 static void sipe_publish_get_cat_state_user_to_clear(SIPE_UNUSED_PARAMETER const char *name,
4586 gpointer value,
4587 GString* str)
4589 struct sipe_publication *publication = value;
4591 g_string_append_printf( str,
4592 SIPE_PUB_XML_PUBLICATION_CLEAR,
4593 publication->category,
4594 publication->instance,
4595 publication->container,
4596 publication->version,
4597 "static");
4600 void sipe_core_reset_status(struct sipe_core_public *sipe_public)
4602 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
4603 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA;
4604 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) /* 2007+ */
4606 GString* str;
4607 gchar *publications;
4609 if (!sip->user_state_publications || g_hash_table_size(sip->user_state_publications) == 0) {
4610 SIPE_DEBUG_INFO_NOFORMAT("sipe_reset_status: no userState publications, exiting.");
4611 return;
4614 str = g_string_new(NULL);
4615 g_hash_table_foreach(sip->user_state_publications, (GHFunc)sipe_publish_get_cat_state_user_to_clear, str);
4616 publications = g_string_free(str, FALSE);
4618 send_presence_publish(sipe_private, publications);
4619 g_free(publications);
4621 else /* 2005 */
4623 send_presence_soap0(sipe_private, FALSE, TRUE);
4627 /** for Access levels menu */
4628 #define INDENT_FMT " %s"
4630 /** Member is directly placed to access level container.
4631 * For example SIP URI of user is in the container.
4633 #define INDENT_MARKED_FMT "* %s"
4635 /** Member is indirectly belong to access level container.
4636 * For example 'sameEnterprise' is in the container and user
4637 * belongs to that same enterprise.
4639 #define INDENT_MARKED_INHERITED_FMT "= %s"
4641 GSList *sipe_core_buddy_info(struct sipe_core_public *sipe_public,
4642 const gchar *name,
4643 const gchar *status_name,
4644 gboolean is_online)
4646 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
4647 gchar *note = NULL;
4648 gboolean is_oof_note = FALSE;
4649 const gchar *activity = NULL;
4650 gchar *calendar = NULL;
4651 const gchar *meeting_subject = NULL;
4652 const gchar *meeting_location = NULL;
4653 gchar *access_text = NULL;
4654 GSList *info = NULL;
4656 #define SIPE_ADD_BUDDY_INFO_COMMON(l, t) \
4658 struct sipe_buddy_info *sbi = g_malloc(sizeof(struct sipe_buddy_info)); \
4659 sbi->label = (l); \
4660 sbi->text = (t); \
4661 info = g_slist_append(info, sbi); \
4663 #define SIPE_ADD_BUDDY_INFO(l, t) SIPE_ADD_BUDDY_INFO_COMMON((l), g_markup_escape_text((t), -1))
4664 #define SIPE_ADD_BUDDY_INFO_NOESCAPE(l, t) SIPE_ADD_BUDDY_INFO_COMMON((l), (t))
4666 if (sipe_public) { //happens on pidgin exit
4667 struct sipe_buddy *sbuddy = g_hash_table_lookup(sipe_private->buddies, name);
4668 if (sbuddy) {
4669 note = sbuddy->note;
4670 is_oof_note = sbuddy->is_oof_note;
4671 activity = sbuddy->activity;
4672 calendar = sipe_cal_get_description(sbuddy);
4673 meeting_subject = sbuddy->meeting_subject;
4674 meeting_location = sbuddy->meeting_location;
4676 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
4677 gboolean is_group_access = FALSE;
4678 const int container_id = sipe_find_access_level(sipe_private, "user", sipe_get_no_sip_uri(name), &is_group_access);
4679 const char *access_level = sipe_get_access_level_name(container_id);
4680 access_text = is_group_access ?
4681 g_strdup(access_level) :
4682 g_strdup_printf(INDENT_MARKED_FMT, access_level);
4686 //Layout
4687 if (is_online)
4689 const gchar *status_str = activity ? activity : status_name;
4691 SIPE_ADD_BUDDY_INFO(_("Status"), status_str);
4693 if (is_online && !is_empty(calendar))
4695 SIPE_ADD_BUDDY_INFO(_("Calendar"), calendar);
4697 g_free(calendar);
4698 if (!is_empty(meeting_location))
4700 SIPE_DEBUG_INFO("sipe_tooltip_text: %s meeting location: '%s'", name, meeting_location);
4701 SIPE_ADD_BUDDY_INFO(_("Meeting in"), meeting_location);
4703 if (!is_empty(meeting_subject))
4705 SIPE_DEBUG_INFO("sipe_tooltip_text: %s meeting subject: '%s'", name, meeting_subject);
4706 SIPE_ADD_BUDDY_INFO(_("Meeting about"), meeting_subject);
4708 if (note)
4710 SIPE_DEBUG_INFO("sipe_tooltip_text: %s note: '%s'", name, note);
4711 SIPE_ADD_BUDDY_INFO_NOESCAPE(is_oof_note ? _("Out of office note") : _("Note"),
4712 g_strdup_printf("<i>%s</i>", note));
4714 if (access_text) {
4715 SIPE_ADD_BUDDY_INFO(_("Access level"), access_text);
4716 g_free(access_text);
4719 return(info);
4722 static PurpleBuddy *
4723 purple_blist_add_buddy_clone(PurpleGroup * group, PurpleBuddy * buddy)
4725 PurpleBuddy *clone;
4726 const gchar *server_alias, *email;
4727 const PurpleStatus *status = purple_presence_get_active_status(purple_buddy_get_presence(buddy));
4729 clone = purple_buddy_new(buddy->account, buddy->name, buddy->alias);
4731 purple_blist_add_buddy(clone, NULL, group, NULL);
4733 server_alias = purple_buddy_get_server_alias(buddy);
4734 if (server_alias) {
4735 purple_blist_server_alias_buddy(clone, server_alias);
4738 email = purple_blist_node_get_string(&buddy->node, EMAIL_PROP);
4739 if (email) {
4740 purple_blist_node_set_string(&clone->node, EMAIL_PROP, email);
4743 purple_presence_set_status_active(purple_buddy_get_presence(clone), purple_status_get_id(status), TRUE);
4744 //for UI to update;
4745 purple_prpl_got_user_status(clone->account, clone->name, purple_status_get_id(status), NULL);
4746 return clone;
4749 static void
4750 sipe_buddy_menu_copy_to_cb(PurpleBlistNode *node, const char *group_name)
4752 PurpleBuddy *buddy, *b;
4753 PurpleConnection *gc;
4754 PurpleGroup * group = purple_find_group(group_name);
4756 g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
4758 buddy = (PurpleBuddy *)node;
4760 SIPE_DEBUG_INFO("sipe_buddy_menu_copy_to_cb: copying %s to %s", buddy->name, group_name);
4761 gc = purple_account_get_connection(buddy->account);
4763 b = purple_find_buddy_in_group(buddy->account, buddy->name, group);
4764 if (!b){
4765 b = purple_blist_add_buddy_clone(group, buddy);
4768 sipe_add_buddy(gc, b, group);
4771 static void
4772 sipe_buddy_menu_chat_new_cb(PurpleBuddy *buddy)
4774 struct sipe_core_private *sipe_private = PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE;
4776 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_new_cb: buddy->name=%s", buddy->name);
4778 /* 2007+ conference */
4779 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007))
4781 sipe_conf_add(sipe_private, buddy->name);
4783 else /* 2005- multiparty chat */
4785 gchar *self = sip_uri_self(sipe_private);
4786 struct sip_session *session;
4788 session = sipe_session_add_chat(sipe_private,
4789 NULL,
4790 TRUE,
4791 self);
4792 session->chat_session->backend = sipe_backend_chat_create(SIPE_CORE_PUBLIC,
4793 session->chat_session,
4794 session->chat_session->title,
4795 self);
4796 g_free(self);
4798 sipe_im_invite(sipe_private, session, buddy->name, NULL, NULL, NULL, FALSE);
4803 * For 2007+ conference only.
4805 static void
4806 sipe_buddy_menu_chat_make_leader_cb(PurpleBuddy *buddy,
4807 struct sipe_chat_session *chat_session)
4809 struct sipe_core_private *sipe_private = PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE;
4810 struct sip_session *session;
4812 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_make_leader_cb: buddy->name=%s", buddy->name);
4813 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_make_leader_cb: chat_title=%s", chat_session->title);
4815 session = sipe_session_find_chat(sipe_private, chat_session);
4817 sipe_conf_modify_user_role(sipe_private, session, buddy->name);
4821 * For 2007+ conference only.
4823 static void
4824 sipe_buddy_menu_chat_remove_cb(PurpleBuddy *buddy,
4825 struct sipe_chat_session *chat_session)
4827 struct sipe_core_private *sipe_private = PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE;
4828 struct sip_session *session;
4830 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_remove_cb: buddy->name=%s", buddy->name);
4831 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_remove_cb: chat_title=%s", chat_session->title);
4833 session = sipe_session_find_chat(sipe_private, chat_session);
4835 sipe_conf_delete_user(sipe_private, session, buddy->name);
4838 static void
4839 sipe_buddy_menu_chat_invite_cb(PurpleBuddy *buddy,
4840 struct sipe_chat_session *chat_session)
4842 struct sipe_core_private *sipe_private = PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE;
4844 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_invite_cb: buddy->name=%s", buddy->name);
4845 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_invite_cb: chat_title=%s", chat_session->title);
4847 sipe_core_chat_invite(SIPE_CORE_PUBLIC, chat_session, buddy->name);
4850 static void
4851 sipe_buddy_menu_make_call_cb(PurpleBuddy *buddy, const char *phone)
4853 struct sipe_core_private *sipe_private = PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE;
4855 SIPE_DEBUG_INFO("sipe_buddy_menu_make_call_cb: buddy->name=%s", buddy->name);
4856 if (phone) {
4857 char *tel_uri = sip_to_tel_uri(phone);
4859 SIPE_DEBUG_INFO("sipe_buddy_menu_make_call_cb: going to call number: %s", tel_uri ? tel_uri : "");
4860 sip_csta_make_call(sipe_private, tel_uri);
4862 g_free(tel_uri);
4866 static void
4867 sipe_buddy_menu_access_level_help_cb(PurpleBuddy *buddy)
4869 /** Translators: replace with URL to localized page
4870 * If it doesn't exist copy the original URL */
4871 purple_notify_uri(buddy->account->gc, _("https://sourceforge.net/apps/mediawiki/sipe/index.php?title=Access_Levels"));
4874 static void
4875 sipe_buddy_menu_send_email_cb(PurpleBuddy *buddy)
4877 const gchar *email;
4878 SIPE_DEBUG_INFO("sipe_buddy_menu_send_email_cb: buddy->name=%s", buddy->name);
4880 email = purple_blist_node_get_string(&buddy->node, EMAIL_PROP);
4881 if (email)
4883 char *command_line = g_strdup_printf(
4884 #ifdef _WIN32
4885 "cmd /c start"
4886 #else
4887 "xdg-email"
4888 #endif
4889 " mailto:%s", email);
4890 SIPE_DEBUG_INFO("sipe_buddy_menu_send_email_cb: going to call email client: %s", command_line);
4892 g_spawn_command_line_async(command_line, NULL);
4893 g_free(command_line);
4895 else
4897 SIPE_DEBUG_INFO("sipe_buddy_menu_send_email_cb: no email address stored for buddy=%s", buddy->name);
4901 static void
4902 sipe_buddy_menu_access_level_cb(PurpleBuddy *buddy,
4903 struct sipe_container *container)
4905 struct sipe_core_private *sipe_private = PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE;
4906 struct sipe_container_member *member;
4908 if (!container || !container->members) return;
4910 member = ((struct sipe_container_member *)container->members->data);
4912 if (!member->type) return;
4914 SIPE_DEBUG_INFO("sipe_buddy_menu_access_level_cb: container->id=%d, member->type=%s, member->value=%s",
4915 container->id, member->type, member->value ? member->value : "");
4917 sipe_change_access_level(sipe_private, container->id, member->type, member->value);
4920 static GList *
4921 sipe_get_access_control_menu(struct sipe_core_private *sipe_private,
4922 const char* uri);
4925 * A menu which appear when right-clicking on buddy in contact list.
4927 GList *
4928 sipe_buddy_menu(PurpleBuddy *buddy)
4930 PurpleBlistNode *g_node;
4931 PurpleGroup *gr_parent;
4932 PurpleMenuAction *act;
4933 GList *menu = NULL;
4934 GList *menu_groups = NULL;
4935 struct sipe_core_private *sipe_private = PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE;
4936 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
4937 const char *email;
4938 gchar *self = sip_uri_self(sipe_private);
4940 SIPE_SESSION_FOREACH {
4941 if (!sipe_strcase_equal(self, buddy->name) && session->chat_session)
4943 struct sipe_chat_session *chat_session = session->chat_session;
4944 gboolean is_conf = (chat_session->type == SIPE_CHAT_TYPE_CONFERENCE);
4946 if (sipe_backend_chat_find(chat_session->backend, buddy->name))
4948 gboolean conf_op = sipe_backend_chat_is_operator(chat_session->backend, self);
4950 if (is_conf
4951 && !sipe_backend_chat_is_operator(chat_session->backend, buddy->name) /* Not conf OP */
4952 && conf_op) /* We are a conf OP */
4954 gchar *label = g_strdup_printf(_("Make leader of '%s'"),
4955 chat_session->title);
4956 act = purple_menu_action_new(label,
4957 PURPLE_CALLBACK(sipe_buddy_menu_chat_make_leader_cb),
4958 chat_session, NULL);
4959 g_free(label);
4960 menu = g_list_prepend(menu, act);
4963 if (is_conf
4964 && conf_op) /* We are a conf OP */
4966 gchar *label = g_strdup_printf(_("Remove from '%s'"),
4967 chat_session->title);
4968 act = purple_menu_action_new(label,
4969 PURPLE_CALLBACK(sipe_buddy_menu_chat_remove_cb),
4970 chat_session, NULL);
4971 g_free(label);
4972 menu = g_list_prepend(menu, act);
4975 else
4977 if (!is_conf
4978 || (is_conf && !session->locked))
4980 gchar *label = g_strdup_printf(_("Invite to '%s'"),
4981 chat_session->title);
4982 act = purple_menu_action_new(label,
4983 PURPLE_CALLBACK(sipe_buddy_menu_chat_invite_cb),
4984 chat_session, NULL);
4985 g_free(label);
4986 menu = g_list_prepend(menu, act);
4990 } SIPE_SESSION_FOREACH_END;
4992 act = purple_menu_action_new(_("New chat"),
4993 PURPLE_CALLBACK(sipe_buddy_menu_chat_new_cb),
4994 NULL, NULL);
4995 menu = g_list_prepend(menu, act);
4997 if (sip->csta && !sip->csta->line_status) {
4998 const char *phone;
4999 const char *phone_disp_str;
5000 gchar *tmp = NULL;
5001 /* work phone */
5002 phone = purple_blist_node_get_string(&buddy->node, PHONE_PROP);
5003 phone_disp_str = purple_blist_node_get_string(&buddy->node, PHONE_DISPLAY_PROP);
5004 if (phone) {
5005 gchar *label = g_strdup_printf(_("Work %s"),
5006 phone_disp_str ? phone_disp_str : (tmp = sip_tel_uri_denormalize(phone)));
5007 act = purple_menu_action_new(label, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb), (gpointer) phone, NULL);
5008 g_free(tmp);
5009 tmp = NULL;
5010 g_free(label);
5011 menu = g_list_prepend(menu, act);
5014 /* mobile phone */
5015 phone = purple_blist_node_get_string(&buddy->node, PHONE_MOBILE_PROP);
5016 phone_disp_str = purple_blist_node_get_string(&buddy->node, PHONE_MOBILE_DISPLAY_PROP);
5017 if (phone) {
5018 gchar *label = g_strdup_printf(_("Mobile %s"),
5019 phone_disp_str ? phone_disp_str : (tmp = sip_tel_uri_denormalize(phone)));
5020 act = purple_menu_action_new(label, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb), (gpointer) phone, NULL);
5021 g_free(tmp);
5022 tmp = NULL;
5023 g_free(label);
5024 menu = g_list_prepend(menu, act);
5027 /* home phone */
5028 phone = purple_blist_node_get_string(&buddy->node, PHONE_HOME_PROP);
5029 phone_disp_str = purple_blist_node_get_string(&buddy->node, PHONE_HOME_DISPLAY_PROP);
5030 if (phone) {
5031 gchar *label = g_strdup_printf(_("Home %s"),
5032 phone_disp_str ? phone_disp_str : (tmp = sip_tel_uri_denormalize(phone)));
5033 act = purple_menu_action_new(label, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb), (gpointer) phone, NULL);
5034 g_free(tmp);
5035 tmp = NULL;
5036 g_free(label);
5037 menu = g_list_prepend(menu, act);
5040 /* other phone */
5041 phone = purple_blist_node_get_string(&buddy->node, PHONE_OTHER_PROP);
5042 phone_disp_str = purple_blist_node_get_string(&buddy->node, PHONE_OTHER_DISPLAY_PROP);
5043 if (phone) {
5044 gchar *label = g_strdup_printf(_("Other %s"),
5045 phone_disp_str ? phone_disp_str : (tmp = sip_tel_uri_denormalize(phone)));
5046 act = purple_menu_action_new(label, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb), (gpointer) phone, NULL);
5047 g_free(tmp);
5048 tmp = NULL;
5049 g_free(label);
5050 menu = g_list_prepend(menu, act);
5053 /* custom1 phone */
5054 phone = purple_blist_node_get_string(&buddy->node, PHONE_CUSTOM1_PROP);
5055 phone_disp_str = purple_blist_node_get_string(&buddy->node, PHONE_CUSTOM1_DISPLAY_PROP);
5056 if (phone) {
5057 gchar *label = g_strdup_printf(_("Custom1 %s"),
5058 phone_disp_str ? phone_disp_str : (tmp = sip_tel_uri_denormalize(phone)));
5059 act = purple_menu_action_new(label, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb), (gpointer) phone, NULL);
5060 g_free(tmp);
5061 tmp = NULL;
5062 g_free(label);
5063 menu = g_list_prepend(menu, act);
5067 email = purple_blist_node_get_string(&buddy->node, EMAIL_PROP);
5068 if (email) {
5069 act = purple_menu_action_new(_("Send email..."),
5070 PURPLE_CALLBACK(sipe_buddy_menu_send_email_cb),
5071 NULL, NULL);
5072 menu = g_list_prepend(menu, act);
5075 /* Access Level */
5076 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
5077 GList *menu_access_levels = sipe_get_access_control_menu(sipe_private, buddy->name);
5079 act = purple_menu_action_new(_("Access level"),
5080 NULL,
5081 NULL, menu_access_levels);
5082 menu = g_list_prepend(menu, act);
5085 /* Copy to */
5086 gr_parent = purple_buddy_get_group(buddy);
5087 for (g_node = purple_blist_get_root(); g_node; g_node = g_node->next) {
5088 PurpleGroup *group;
5090 if (g_node->type != PURPLE_BLIST_GROUP_NODE)
5091 continue;
5093 group = (PurpleGroup *)g_node;
5094 if (group == gr_parent)
5095 continue;
5097 if (purple_find_buddy_in_group(buddy->account, buddy->name, group))
5098 continue;
5100 act = purple_menu_action_new(purple_group_get_name(group),
5101 PURPLE_CALLBACK(sipe_buddy_menu_copy_to_cb),
5102 group->name, NULL);
5103 menu_groups = g_list_prepend(menu_groups, act);
5105 menu_groups = g_list_reverse(menu_groups);
5107 act = purple_menu_action_new(_("Copy to"),
5108 NULL,
5109 NULL, menu_groups);
5110 menu = g_list_prepend(menu, act);
5112 menu = g_list_reverse(menu);
5114 g_free(self);
5115 return menu;
5118 static void
5119 sipe_ask_access_domain_cb(PurpleConnection *gc, PurpleRequestFields *fields)
5121 struct sipe_core_private *sipe_private = PURPLE_GC_TO_SIPE_CORE_PRIVATE;
5122 const char *domain = purple_request_fields_get_string(fields, "access_domain");
5123 int index = purple_request_fields_get_choice(fields, "container_id");
5124 /* move Blocked first */
5125 int i = (index == 4) ? 0 : index + 1;
5126 int container_id = containers[i];
5128 SIPE_DEBUG_INFO("sipe_ask_access_domain_cb: domain=%s, container_id=(%d)%d", domain ? domain : "", index, container_id);
5130 sipe_change_access_level(sipe_private, container_id, "domain", domain);
5133 static void
5134 sipe_ask_access_domain(struct sipe_core_private *sipe_private)
5136 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
5137 PurpleAccount *account = sip->account;
5138 PurpleConnection *gc = sip->gc;
5139 PurpleRequestFields *fields;
5140 PurpleRequestFieldGroup *g;
5141 PurpleRequestField *f;
5143 fields = purple_request_fields_new();
5145 g = purple_request_field_group_new(NULL);
5146 f = purple_request_field_string_new("access_domain", _("Domain"), "partner-company.com", FALSE);
5147 purple_request_field_set_required(f, TRUE);
5148 purple_request_field_group_add_field(g, f);
5150 f = purple_request_field_choice_new("container_id", _("Access level"), 0);
5151 purple_request_field_choice_add(f, _("Personal")); /* index 0 */
5152 purple_request_field_choice_add(f, _("Team"));
5153 purple_request_field_choice_add(f, _("Company"));
5154 purple_request_field_choice_add(f, _("Public"));
5155 purple_request_field_choice_add(f, _("Blocked")); /* index 4 */
5156 purple_request_field_choice_set_default_value(f, 3); /* index */
5157 purple_request_field_set_required(f, TRUE);
5158 purple_request_field_group_add_field(g, f);
5160 purple_request_fields_add_group(fields, g);
5162 purple_request_fields(gc, _("Add new domain"),
5163 _("Add new domain"), NULL, fields,
5164 _("Add"), G_CALLBACK(sipe_ask_access_domain_cb),
5165 _("Cancel"), NULL,
5166 account, NULL, NULL, gc);
5169 static void
5170 sipe_buddy_menu_access_level_add_domain_cb(PurpleBuddy *buddy)
5172 sipe_ask_access_domain(PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE);
5176 * Workaround for missing libpurple API to release resources allocated
5177 * during blist_node_menu() callback. See also:
5179 * <http://developer.pidgin.im/ticket/12597>
5181 * We remember all memory blocks in a list and deallocate them when
5183 * - the next time we enter the callback, or
5184 * - the account is disconnected
5186 * That means that after the buddy menu has been closed we have unused
5187 * resources but at least we don't leak them anymore...
5189 static void
5190 sipe_blist_menu_free_containers(struct sipe_core_private *sipe_private)
5192 GSList *entry = sipe_private->blist_menu_containers;
5193 while (entry) {
5194 free_container(entry->data);
5195 entry = entry->next;
5197 g_slist_free(sipe_private->blist_menu_containers);
5198 sipe_private->blist_menu_containers = NULL;
5201 static void
5202 sipe_blist_menu_remember_container(struct sipe_core_private *sipe_private,
5203 struct sipe_container *container)
5205 sipe_private->blist_menu_containers = g_slist_prepend(sipe_private->blist_menu_containers,
5206 container);
5209 static GList *
5210 sipe_get_access_levels_menu(struct sipe_core_private *sipe_private,
5211 const char* member_type,
5212 const char* member_value,
5213 const gboolean extra_menu)
5215 GList *menu_access_levels = NULL;
5216 unsigned int i;
5217 char *menu_name;
5218 PurpleMenuAction *act;
5219 struct sipe_container *container;
5220 struct sipe_container_member *member;
5221 gboolean is_group_access = FALSE;
5222 int container_id = sipe_find_access_level(sipe_private, member_type, member_value, &is_group_access);
5224 for (i = 1; i <= CONTAINERS_LEN; i++) {
5225 /* to put Blocked level last in menu list.
5226 * Blocked should remaim in the first place in the containers[] array.
5228 unsigned int j = (i == CONTAINERS_LEN) ? 0 : i;
5229 const char *acc_level_name = sipe_get_access_level_name(containers[j]);
5231 container = g_new0(struct sipe_container, 1);
5232 member = g_new0(struct sipe_container_member, 1);
5233 container->id = containers[j];
5234 container->members = g_slist_append(container->members, member);
5235 member->type = g_strdup(member_type);
5236 member->value = g_strdup(member_value);
5238 /* libpurple memory leak workaround */
5239 sipe_blist_menu_remember_container(sipe_private, container);
5241 /* current container/access level */
5242 if (((int)containers[j]) == container_id) {
5243 menu_name = is_group_access ?
5244 g_strdup_printf(INDENT_MARKED_INHERITED_FMT, acc_level_name) :
5245 g_strdup_printf(INDENT_MARKED_FMT, acc_level_name);
5246 } else {
5247 menu_name = g_strdup_printf(INDENT_FMT, acc_level_name);
5250 act = purple_menu_action_new(menu_name,
5251 PURPLE_CALLBACK(sipe_buddy_menu_access_level_cb),
5252 container, NULL);
5253 g_free(menu_name);
5254 menu_access_levels = g_list_prepend(menu_access_levels, act);
5257 if (extra_menu && (container_id >= 0)) {
5258 /* separator */
5259 act = purple_menu_action_new(" --------------", NULL, NULL, NULL);
5260 menu_access_levels = g_list_prepend(menu_access_levels, act);
5262 if (!is_group_access) {
5263 container = g_new0(struct sipe_container, 1);
5264 member = g_new0(struct sipe_container_member, 1);
5265 container->id = -1;
5266 container->members = g_slist_append(container->members, member);
5267 member->type = g_strdup(member_type);
5268 member->value = g_strdup(member_value);
5270 /* libpurple memory leak workaround */
5271 sipe_blist_menu_remember_container(sipe_private, container);
5273 /* Translators: remove (clear) previously assigned access level */
5274 menu_name = g_strdup_printf(INDENT_FMT, _("Unspecify"));
5275 act = purple_menu_action_new(menu_name,
5276 PURPLE_CALLBACK(sipe_buddy_menu_access_level_cb),
5277 container, NULL);
5278 g_free(menu_name);
5279 menu_access_levels = g_list_prepend(menu_access_levels, act);
5283 menu_access_levels = g_list_reverse(menu_access_levels);
5284 return menu_access_levels;
5287 static GList *
5288 sipe_get_access_groups_menu(struct sipe_core_private *sipe_private)
5290 GList *menu_access_groups = NULL;
5291 PurpleMenuAction *act;
5292 GSList *access_domains = NULL;
5293 GSList *entry;
5294 char *menu_name;
5295 char *domain;
5297 act = purple_menu_action_new(_("People in my company"),
5298 NULL,
5299 NULL, sipe_get_access_levels_menu(sipe_private, "sameEnterprise", NULL, FALSE));
5300 menu_access_groups = g_list_prepend(menu_access_groups, act);
5302 /* this is original name, don't edit */
5303 act = purple_menu_action_new(_("People in domains connected with my company"),
5304 NULL,
5305 NULL, sipe_get_access_levels_menu(sipe_private, "federated", NULL, FALSE));
5306 menu_access_groups = g_list_prepend(menu_access_groups, act);
5308 act = purple_menu_action_new(_("People in public domains"),
5309 NULL,
5310 NULL, sipe_get_access_levels_menu(sipe_private, "publicCloud", NULL, TRUE));
5311 menu_access_groups = g_list_prepend(menu_access_groups, act);
5313 access_domains = sipe_get_access_domains(sipe_private);
5314 entry = access_domains;
5315 while (entry) {
5316 domain = entry->data;
5318 menu_name = g_strdup_printf(_("People at %s"), domain);
5319 act = purple_menu_action_new(menu_name,
5320 NULL,
5321 NULL, sipe_get_access_levels_menu(sipe_private, "domain", g_strdup(domain), TRUE));
5322 menu_access_groups = g_list_prepend(menu_access_groups, act);
5323 g_free(menu_name);
5325 entry = entry->next;
5328 /* separator */
5329 /* People in domains connected with my company */
5330 act = purple_menu_action_new("-------------------------------------------", NULL, NULL, NULL);
5331 menu_access_groups = g_list_prepend(menu_access_groups, act);
5333 act = purple_menu_action_new(_("Add new domain..."),
5334 PURPLE_CALLBACK(sipe_buddy_menu_access_level_add_domain_cb),
5335 NULL, NULL);
5336 menu_access_groups = g_list_prepend(menu_access_groups, act);
5338 menu_access_groups = g_list_reverse(menu_access_groups);
5340 return menu_access_groups;
5343 static GList *
5344 sipe_get_access_control_menu(struct sipe_core_private *sipe_private,
5345 const char* uri)
5347 GList *menu_access_levels = NULL;
5348 GList *menu_access_groups = NULL;
5349 char *menu_name;
5350 PurpleMenuAction *act;
5352 /* libpurple memory leak workaround */
5353 sipe_blist_menu_free_containers(sipe_private);
5355 menu_access_levels = sipe_get_access_levels_menu(sipe_private, "user", sipe_get_no_sip_uri(uri), TRUE);
5357 menu_access_groups = sipe_get_access_groups_menu(sipe_private);
5359 menu_name = g_strdup_printf(INDENT_FMT, _("Access groups"));
5360 act = purple_menu_action_new(menu_name,
5361 NULL,
5362 NULL, menu_access_groups);
5363 g_free(menu_name);
5364 menu_access_levels = g_list_append(menu_access_levels, act);
5366 menu_name = g_strdup_printf(INDENT_FMT, _("Online help..."));
5367 act = purple_menu_action_new(menu_name,
5368 PURPLE_CALLBACK(sipe_buddy_menu_access_level_help_cb),
5369 NULL, NULL);
5370 g_free(menu_name);
5371 menu_access_levels = g_list_append(menu_access_levels, act);
5373 return menu_access_levels;
5376 static gboolean
5377 process_get_info_response(struct sipe_core_private *sipe_private,
5378 struct sipmsg *msg, struct transaction *trans)
5380 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
5381 char *uri = trans->payload->data;
5383 PurpleNotifyUserInfo *info;
5384 PurpleBuddy *pbuddy = NULL;
5385 struct sipe_buddy *sbuddy;
5386 const char *alias = NULL;
5387 char *device_name = NULL;
5388 char *server_alias = NULL;
5389 char *phone_number = NULL;
5390 char *email = NULL;
5391 const char *site;
5392 char *first_name = NULL;
5393 char *last_name = NULL;
5395 if (!sip) return FALSE;
5397 SIPE_DEBUG_INFO("Fetching %s's user info for %s", uri, sipe_private->username);
5399 pbuddy = purple_find_buddy((PurpleAccount *)sip->account, uri);
5400 alias = purple_buddy_get_local_alias(pbuddy);
5402 //will query buddy UA's capabilities and send answer to log
5403 sipe_options_request(sipe_private, uri);
5405 sbuddy = g_hash_table_lookup(sipe_private->buddies, uri);
5406 if (sbuddy) {
5407 device_name = sbuddy->device_name ? g_strdup(sbuddy->device_name) : NULL;
5410 info = purple_notify_user_info_new();
5412 if (msg->response != 200) {
5413 SIPE_DEBUG_INFO("process_get_info_response: SERVICE response is %d", msg->response);
5414 } else {
5415 sipe_xml *searchResults;
5416 const sipe_xml *mrow;
5418 SIPE_DEBUG_INFO("process_get_info_response: body:\n%s", msg->body ? msg->body : "");
5419 searchResults = sipe_xml_parse(msg->body, msg->bodylen);
5420 if (!searchResults) {
5421 SIPE_DEBUG_INFO_NOFORMAT("process_get_info_response: no parseable searchResults");
5422 } else if ((mrow = sipe_xml_child(searchResults, "Body/Array/row"))) {
5423 const char *value;
5424 server_alias = g_strdup(sipe_xml_attribute(mrow, "displayName"));
5425 email = g_strdup(sipe_xml_attribute(mrow, "email"));
5426 phone_number = g_strdup(sipe_xml_attribute(mrow, "phone"));
5428 /* For 2007 system we will take this from ContactCard -
5429 * it has cleaner tel: URIs at least
5431 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
5432 char *tel_uri = sip_to_tel_uri(phone_number);
5433 /* trims its parameters, so call first */
5434 sipe_update_user_info(sipe_private, uri, SIPE_BUDDY_INFO_DISPLAY_NAME, server_alias);
5435 sipe_update_user_info(sipe_private, uri, SIPE_BUDDY_INFO_EMAIL, email);
5436 sipe_update_user_info(sipe_private, uri, SIPE_BUDDY_INFO_WORK_PHONE, tel_uri);
5437 sipe_update_user_info(sipe_private, uri, SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY, phone_number);
5438 g_free(tel_uri);
5441 #if PURPLE_VERSION_CHECK(3,0,0)
5442 #define PURPLE_NOTIFY_USER_INFO_ADD_PAIR purple_notify_user_info_add_pair_html
5443 #else
5444 #define PURPLE_NOTIFY_USER_INFO_ADD_PAIR purple_notify_user_info_add_pair
5445 #endif
5447 if (server_alias && strlen(server_alias) > 0) {
5448 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info, _("Display name"), server_alias);
5450 if ((value = sipe_xml_attribute(mrow, "title")) && strlen(value) > 0) {
5451 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info, _("Job title"), value);
5453 if ((value = sipe_xml_attribute(mrow, "office")) && strlen(value) > 0) {
5454 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info, _("Office"), value);
5456 if (phone_number && strlen(phone_number) > 0) {
5457 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info, _("Business phone"), phone_number);
5459 if ((value = sipe_xml_attribute(mrow, "company")) && strlen(value) > 0) {
5460 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info, _("Company"), value);
5462 if ((value = sipe_xml_attribute(mrow, "city")) && strlen(value) > 0) {
5463 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info, _("City"), value);
5465 if ((value = sipe_xml_attribute(mrow, "state")) && strlen(value) > 0) {
5466 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info, _("State"), value);
5468 if ((value = sipe_xml_attribute(mrow, "country")) && strlen(value) > 0) {
5469 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info, _("Country"), value);
5471 if (email && strlen(email) > 0) {
5472 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info, _("Email address"), email);
5476 sipe_xml_free(searchResults);
5479 purple_notify_user_info_add_section_break(info);
5481 if (is_empty(server_alias)) {
5482 g_free(server_alias);
5483 server_alias = g_strdup(purple_buddy_get_server_alias(pbuddy));
5484 if (server_alias) {
5485 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info, _("Display name"), server_alias);
5489 /* present alias if it differs from server alias */
5490 if (alias && !sipe_strequal(alias, server_alias))
5492 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info, _("Alias"), alias);
5495 if (is_empty(email)) {
5496 g_free(email);
5497 email = g_strdup(purple_blist_node_get_string(&pbuddy->node, EMAIL_PROP));
5498 if (email) {
5499 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info, _("Email address"), email);
5503 site = purple_blist_node_get_string(&pbuddy->node, SITE_PROP);
5504 if (site) {
5505 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info, _("Site"), site);
5508 sipe_get_first_last_names(sipe_private, uri, &first_name, &last_name);
5509 if (first_name && last_name) {
5510 char *link = g_strconcat("http://www.linkedin.com/pub/dir/", first_name, "/", last_name, NULL);
5512 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info, _("Find on LinkedIn"), link);
5513 g_free(link);
5515 g_free(first_name);
5516 g_free(last_name);
5518 if (device_name) {
5519 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info, _("Device"), device_name);
5522 /* show a buddy's user info in a nice dialog box */
5523 purple_notify_userinfo(sip->gc, /* connection the buddy info came through */
5524 uri, /* buddy's URI */
5525 info, /* body */
5526 NULL, /* callback called when dialog closed */
5527 NULL); /* userdata for callback */
5529 g_free(phone_number);
5530 g_free(server_alias);
5531 g_free(email);
5532 g_free(device_name);
5534 return TRUE;
5538 * AD search first, LDAP based
5540 void sipe_get_info(PurpleConnection *gc, const char *username)
5542 struct sipe_core_private *sipe_private = PURPLE_GC_TO_SIPE_CORE_PRIVATE;
5543 char *row = g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW, "msRTCSIP-PrimaryUserAddress", username);
5544 struct transaction_payload *payload = g_new0(struct transaction_payload, 1);
5546 payload->destroy = g_free;
5547 payload->data = g_strdup(username);
5549 SIPE_DEBUG_INFO("sipe_get_info: row: %s", row ? row : "");
5550 sip_soap_directory_search(sipe_private,
5552 row,
5553 process_get_info_response,
5554 payload);
5555 g_free(row);
5559 Local Variables:
5560 mode: c
5561 c-file-style: "bsd"
5562 indent-tabs-mode: t
5563 tab-width: 8
5564 End: