4 *****************************************************************************
5 *** !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ***
7 *** THIS MODULE IS DEPECRATED ***
9 *** DO NOT ADD ANY NEW CODE TO THIS MODULE ***
11 *** !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ***
12 *****************************************************************************
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>
25 * Thanks to Google's Summer of Code Program and the helpful mentors
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
61 #include "sipe-common.h"
65 #include "connection.h"
66 #include "conversation.h"
72 #include "savedstatuses.h"
75 #include "core-depurple.h" /* Temporary for the core de-purple transition */
77 #include "http-conn.h"
81 #include "sip-transport.h"
82 #include "sipe-backend.h"
83 #include "sipe-buddy.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-groupchat.h"
94 #include "sipe-ocs2005.h"
95 #include "sipe-ocs2007.h"
96 #include "sipe-schedule.h"
97 #include "sipe-session.h"
98 #include "sipe-subscriptions.h"
99 #include "sipe-utils.h"
100 #include "sipe-xml.h"
102 #define _SIPE_NEED_ACTIVITIES /* ugly hack :-( */
105 #define SIPE_IDLE_SET_DELAY 1 /* 1 sec */
107 /* Status identifiers (see also: sipe_status_types()) */
108 #define SIPE_STATUS_ID_UNKNOWN purple_primitive_get_id_from_type(PURPLE_STATUS_UNSET) /* Unset (primitive) */
109 #define SIPE_STATUS_ID_OFFLINE purple_primitive_get_id_from_type(PURPLE_STATUS_OFFLINE) /* Offline (primitive) */
110 #define SIPE_STATUS_ID_AVAILABLE purple_primitive_get_id_from_type(PURPLE_STATUS_AVAILABLE) /* Online */
111 /* PURPLE_STATUS_UNAVAILABLE: */
112 #define SIPE_STATUS_ID_BUSY "busy" /* Busy */
113 #define SIPE_STATUS_ID_BUSYIDLE "busyidle" /* BusyIdle */
114 #define SIPE_STATUS_ID_DND "do-not-disturb" /* Do Not Disturb */
115 #define SIPE_STATUS_ID_IN_MEETING "in-a-meeting" /* In a meeting */
116 #define SIPE_STATUS_ID_IN_CONF "in-a-conference" /* In a conference */
117 #define SIPE_STATUS_ID_ON_PHONE "on-the-phone" /* On the phone */
118 #define SIPE_STATUS_ID_INVISIBLE purple_primitive_get_id_from_type(PURPLE_STATUS_INVISIBLE) /* Appear Offline */
119 /* PURPLE_STATUS_AWAY: */
120 #define SIPE_STATUS_ID_IDLE "idle" /* Idle/Inactive */
121 #define SIPE_STATUS_ID_BRB "be-right-back" /* Be Right Back */
122 #define SIPE_STATUS_ID_AWAY purple_primitive_get_id_from_type(PURPLE_STATUS_AWAY) /* Away (primitive) */
123 /** Reuters status (user settable) */
124 #define SIPE_STATUS_ID_LUNCH "out-to-lunch" /* Out To Lunch */
125 /* ??? PURPLE_STATUS_EXTENDED_AWAY */
126 /* ??? PURPLE_STATUS_MOBILE */
127 /* ??? PURPLE_STATUS_TUNE */
129 /* Status attributes (see also sipe_status_types() */
130 #define SIPE_STATUS_ATTR_ID_MESSAGE "message"
132 static struct sipe_activity_map_struct
137 const char *status_id
;
139 } const sipe_activity_map
[] =
141 /* This has nothing to do with Availability numbers, like 3500 (online).
142 * Just a mapping of Communicator Activities to Purple statuses to be able display them in Pidgin.
144 { SIPE_ACTIVITY_UNSET
, "unset", NULL
, NULL
},
145 { SIPE_ACTIVITY_ONLINE
, "online", NULL
, NULL
},
146 { SIPE_ACTIVITY_INACTIVE
, SIPE_STATUS_ID_IDLE
, N_("Inactive") , NULL
},
147 { SIPE_ACTIVITY_BUSY
, SIPE_STATUS_ID_BUSY
, N_("Busy") , SIPE_STATUS_ID_BUSY
},
148 { SIPE_ACTIVITY_BUSYIDLE
, SIPE_STATUS_ID_BUSYIDLE
, N_("Busy-Idle") , NULL
},
149 { SIPE_ACTIVITY_DND
, SIPE_STATUS_ID_DND
, NULL
, SIPE_STATUS_ID_DND
},
150 { SIPE_ACTIVITY_BRB
, SIPE_STATUS_ID_BRB
, N_("Be right back") , SIPE_STATUS_ID_BRB
},
151 { SIPE_ACTIVITY_AWAY
, "away", NULL
, NULL
},
152 { SIPE_ACTIVITY_LUNCH
, SIPE_STATUS_ID_LUNCH
, N_("Out to lunch") , NULL
},
153 { SIPE_ACTIVITY_OFFLINE
, "offline", NULL
, NULL
},
154 { SIPE_ACTIVITY_ON_PHONE
, SIPE_STATUS_ID_ON_PHONE
, N_("In a call") , NULL
},
155 { SIPE_ACTIVITY_IN_CONF
, SIPE_STATUS_ID_IN_CONF
, N_("In a conference") , NULL
},
156 { SIPE_ACTIVITY_IN_MEETING
, SIPE_STATUS_ID_IN_MEETING
, N_("In a meeting") , NULL
},
157 { SIPE_ACTIVITY_OOF
, "out-of-office", N_("Out of office") , NULL
},
158 { SIPE_ACTIVITY_URGENT_ONLY
, "urgent-interruptions-only", N_("Urgent interruptions only") , NULL
}
160 /** @param x is sipe_activity */
161 #define SIPE_ACTIVITY_I18N(x) gettext(sipe_activity_map[x].desc)
163 const gchar
*sipe_activity_to_token(sipe_activity type
)
165 return(sipe_activity_map
[type
].token
);
168 const gchar
*sipe_activity_description(sipe_activity type
)
170 return(SIPE_ACTIVITY_I18N(type
));
173 void sipe_set_unknown_status(struct sipe_core_private
*sipe_private
)
175 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
178 sip
->status
= g_strdup(SIPE_STATUS_ID_UNKNOWN
);
181 void sipe_set_initial_status(struct sipe_core_private
*sipe_private
)
183 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
186 sip
->status
= g_strdup(SIPE_STATUS_ID_AVAILABLE
); /* our initial state */
189 void sipe_set_invisible_status(struct sipe_core_private
*sipe_private
)
191 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
194 sip
->status
= g_strdup(SIPE_STATUS_ID_INVISIBLE
);
198 sipe_get_activity_by_token(const char *token
)
202 for (i
= 0; i
< SIPE_ACTIVITY_NUM_TYPES
; i
++)
204 if (sipe_strequal(token
, sipe_activity_to_token(i
)))
205 return sipe_activity_map
[i
].type
;
208 return sipe_activity_map
[0].type
;
211 const gchar
*sipe_activity_description_from_token(const gchar
*token
)
213 if (!token
) return NULL
;
215 return sipe_activity_description(sipe_get_activity_by_token(token
));
219 sipe_apply_calendar_status(struct sipe_core_private
*sipe_private
,
220 struct sipe_buddy
*sbuddy
,
221 const char *status_id
)
223 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
224 time_t cal_avail_since
;
225 int cal_status
= sipe_cal_get_status(sbuddy
, time(NULL
), &cal_avail_since
);
231 if (cal_status
< SIPE_CAL_NO_DATA
) {
232 SIPE_DEBUG_INFO("sipe_apply_calendar_status: cal_status : %d for %s", cal_status
, sbuddy
->name
);
233 SIPE_DEBUG_INFO("sipe_apply_calendar_status: cal_avail_since : %s", asctime(localtime(&cal_avail_since
)));
236 /* scheduled Cal update call */
238 status_id
= sbuddy
->last_non_cal_status_id
;
239 g_free(sbuddy
->activity
);
240 sbuddy
->activity
= g_strdup(sbuddy
->last_non_cal_activity
);
244 SIPE_DEBUG_INFO("sipe_apply_calendar_status: status_id is NULL for %s, exiting.",
245 sbuddy
->name
? sbuddy
->name
: "" );
249 /* adjust to calendar status */
250 if (cal_status
!= SIPE_CAL_NO_DATA
) {
251 SIPE_DEBUG_INFO("sipe_apply_calendar_status: user_avail_since: %s", asctime(localtime(&sbuddy
->user_avail_since
)));
253 if (cal_status
== SIPE_CAL_BUSY
254 && cal_avail_since
> sbuddy
->user_avail_since
255 && 6500 >= sipe_get_availability_by_status(status_id
, NULL
))
257 status_id
= SIPE_STATUS_ID_BUSY
;
258 g_free(sbuddy
->activity
);
259 sbuddy
->activity
= g_strdup(sipe_activity_description(SIPE_ACTIVITY_IN_MEETING
));
261 avail
= sipe_get_availability_by_status(status_id
, NULL
);
263 SIPE_DEBUG_INFO("sipe_apply_calendar_status: activity_since : %s", asctime(localtime(&sbuddy
->activity_since
)));
264 if (cal_avail_since
> sbuddy
->activity_since
) {
265 if (cal_status
== SIPE_CAL_OOF
266 && avail
>= 15000) /* 12000 in 2007 */
268 g_free(sbuddy
->activity
);
269 sbuddy
->activity
= g_strdup(sipe_activity_description(SIPE_ACTIVITY_OOF
));
274 /* then set status_id actually */
275 SIPE_DEBUG_INFO("sipe_apply_calendar_status: to %s for %s", status_id
, sbuddy
->name
? sbuddy
->name
: "" );
276 sipe_backend_buddy_set_status(SIPE_CORE_PUBLIC
, sbuddy
->name
, status_id
);
278 /* set our account state to the one in roaming (including calendar info) */
279 self_uri
= sip_uri_self(sipe_private
);
280 if (sip
->initial_state_published
&& sipe_strcase_equal(sbuddy
->name
, self_uri
)) {
281 if (sipe_strequal(status_id
, SIPE_STATUS_ID_OFFLINE
)) {
282 status_id
= g_strdup(SIPE_STATUS_ID_INVISIBLE
); /* not not let offline status switch us off */
285 SIPE_DEBUG_INFO("sipe_apply_calendar_status: switch to '%s' for the account", sip
->status
);
286 sipe_backend_account_status_and_note(sipe_private
, status_id
);
292 sipe_core_buddy_got_status(struct sipe_core_public
*sipe_public
,
294 const gchar
*status_id
)
296 struct sipe_core_private
*sipe_private
= SIPE_CORE_PRIVATE
;
297 struct sipe_buddy
*sbuddy
= g_hash_table_lookup(sipe_private
->buddies
, uri
);
301 /* Check if on 2005 system contact's calendar,
302 * then set/preserve it.
304 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
305 sipe_apply_calendar_status(sipe_private
, sbuddy
, status_id
);
307 sipe_backend_buddy_set_status(sipe_public
, uri
, status_id
);
311 void sipe_set_status(PurpleAccount
*account
, PurpleStatus
*status
)
313 SIPE_DEBUG_INFO("sipe_set_status: status=%s", purple_status_get_id(status
));
315 if (!purple_status_is_active(status
))
319 struct sipe_core_private
*sipe_private
= PURPLE_ACCOUNT_TO_SIPE_CORE_PRIVATE
;
320 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
325 time_t now
= time(NULL
);
326 const char *status_id
= purple_status_get_id(status
);
327 const char *note
= purple_status_get_attr_string(status
, SIPE_STATUS_ATTR_ID_MESSAGE
);
328 sipe_activity activity
= sipe_get_activity_by_token(status_id
);
329 gboolean do_not_publish
= ((now
- sip
->do_not_publish
[activity
]) <= 2);
331 /* when other point of presence clears note, but we are keeping
334 if (do_not_publish
&& !note
&& sip
->cal
&& sip
->cal
->oof_note
) {
335 SIPE_DEBUG_INFO_NOFORMAT("sipe_set_status: enabling publication as OOF note keepers.");
336 do_not_publish
= FALSE
;
339 SIPE_DEBUG_INFO("sipe_set_status: was: sip->do_not_publish[%s]=%d [?] now(time)=%d",
340 status_id
, (int)sip
->do_not_publish
[activity
], (int)now
);
342 sip
->do_not_publish
[activity
] = 0;
343 SIPE_DEBUG_INFO("sipe_set_status: set: sip->do_not_publish[%s]=%d [0]",
344 status_id
, (int)sip
->do_not_publish
[activity
]);
348 SIPE_DEBUG_INFO_NOFORMAT("sipe_set_status: publication was switched off, exiting.");
353 sip
->status
= g_strdup(status_id
);
355 /* hack to escape apostrof before comparison */
356 tmp
= note
? sipe_utils_str_replace(note
, "'", "'") : NULL
;
358 /* this will preserve OOF flag as well */
359 if (!sipe_strequal(tmp
, sip
->note
)) {
360 sip
->is_oof_note
= FALSE
;
362 sip
->note
= g_strdup(note
);
363 sip
->note_since
= time(NULL
);
367 /* schedule 2 sec to capture idle flag */
368 action_name
= g_strdup_printf("<%s>", "+set-status");
369 sipe_schedule_seconds(sipe_private
,
373 send_presence_status
,
381 sipe_set_idle(PurpleConnection
* gc
,
384 SIPE_DEBUG_INFO("sipe_set_idle: interval=%d", interval
);
387 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
388 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
391 sip
->idle_switch
= time(NULL
);
392 SIPE_DEBUG_INFO("sipe_set_idle: sip->idle_switch : %s", asctime(localtime(&(sip
->idle_switch
))));
397 const gchar
*sipe_get_buddy_status(struct sipe_core_private
*sipe_private
,
400 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
401 PurpleBuddy
*pbuddy
= purple_find_buddy((PurpleAccount
*)sip
->account
, uri
);
402 const PurplePresence
*presence
= purple_buddy_get_presence(pbuddy
);
403 const PurpleStatus
*pstatus
= purple_presence_get_active_status(presence
);
404 return(purple_status_get_id(pstatus
));
407 void sipe_buddy_status_from_activity(struct sipe_core_private
*sipe_private
,
409 const gchar
*activity
,
413 const gchar
*status_id
= NULL
;
415 if (sipe_strequal(activity
, sipe_activity_to_token(SIPE_ACTIVITY_BUSY
))) {
416 status_id
= SIPE_STATUS_ID_BUSY
;
417 } else if (sipe_strequal(activity
, sipe_activity_to_token(SIPE_ACTIVITY_AWAY
))) {
418 status_id
= SIPE_STATUS_ID_AWAY
;
423 status_id
= SIPE_STATUS_ID_AVAILABLE
;
426 SIPE_DEBUG_INFO("sipe_buddy_status_from_activity: status_id(%s)", status_id
);
427 sipe_core_buddy_got_status(SIPE_CORE_PUBLIC
, uri
, status_id
);
429 sipe_core_buddy_got_status(SIPE_CORE_PUBLIC
, uri
, SIPE_STATUS_ID_OFFLINE
);
433 void sipe_add_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
435 SIPE_DEBUG_INFO("sipe_add_buddy[CB]: buddy:%s group:%s", buddy
? buddy
->name
: "", group
? group
->name
: "");
437 /* libpurple can call us with undefined buddy or group */
438 if (buddy
&& group
) {
439 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
441 /* Buddy name must be lower case as we use purple_normalize_nocase() to compare */
442 gchar
*buddy_name
= g_ascii_strdown(buddy
->name
, -1);
443 purple_blist_rename_buddy(buddy
, buddy_name
);
446 /* Prepend sip: if needed */
447 if (!g_str_has_prefix(buddy
->name
, "sip:")) {
448 gchar
*buf
= sip_uri_from_name(buddy
->name
);
449 purple_blist_rename_buddy(buddy
, buf
);
453 if (!g_hash_table_lookup(sipe_private
->buddies
, buddy
->name
)) {
454 struct sipe_buddy
*b
= g_new0(struct sipe_buddy
, 1);
455 SIPE_DEBUG_INFO("sipe_add_buddy: adding %s", buddy
->name
);
456 b
->name
= g_strdup(buddy
->name
);
457 b
->just_added
= TRUE
;
458 g_hash_table_insert(sipe_private
->buddies
, b
->name
, b
);
459 /* @TODO should go to callback */
460 sipe_subscribe_presence_single(sipe_private
,
463 SIPE_DEBUG_INFO("sipe_add_buddy: buddy %s already in internal list", buddy
->name
);
466 sipe_core_buddy_group(PURPLE_GC_TO_SIPE_CORE_PUBLIC
, buddy
->name
, NULL
, group
->name
);
470 static void sipe_free_buddy(struct sipe_buddy
*buddy
)
474 * We are calling g_hash_table_foreach_steal(). That means that no
475 * key/value deallocation functions are called. Therefore the glib
476 * hash code does not touch the key (buddy->name) or value (buddy)
477 * of the to-be-deleted hash node at all. It follows that we
479 * - MUST free the memory for the key ourselves and
480 * - ARE allowed to do it in this function
482 * Conclusion: glib must be broken on the Windows platform if sipe
483 * crashes with SIGTRAP when closing. You'll have to live
484 * with the memory leak until this is fixed.
488 g_free(buddy
->activity
);
489 g_free(buddy
->meeting_subject
);
490 g_free(buddy
->meeting_location
);
493 g_free(buddy
->cal_start_time
);
494 g_free(buddy
->cal_free_busy_base64
);
495 g_free(buddy
->cal_free_busy
);
496 g_free(buddy
->last_non_cal_activity
);
498 sipe_cal_free_working_hours(buddy
->cal_working_hours
);
500 g_free(buddy
->device_name
);
501 g_slist_free(buddy
->groups
);
506 * Unassociates buddy from group first.
507 * Then see if no groups left, removes buddy completely.
508 * Otherwise updates buddy groups on server.
510 void sipe_remove_buddy(PurpleConnection
*gc
, PurpleBuddy
*buddy
, PurpleGroup
*group
)
512 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
513 struct sipe_buddy
*b
;
514 struct sipe_group
*g
= NULL
;
516 SIPE_DEBUG_INFO("sipe_remove_buddy[CB]: buddy:%s group:%s", buddy
? buddy
->name
: "", group
? group
->name
: "");
519 b
= g_hash_table_lookup(sipe_private
->buddies
, buddy
->name
);
523 g
= sipe_group_find_by_name(sipe_private
, group
->name
);
527 b
->groups
= g_slist_remove(b
->groups
, g
);
528 SIPE_DEBUG_INFO("buddy %s removed from group %s", buddy
->name
, g
->name
);
531 if (g_slist_length(b
->groups
) < 1) {
532 gchar
*action_name
= sipe_utils_presence_key(buddy
->name
);
533 sipe_schedule_cancel(sipe_private
, action_name
);
536 g_hash_table_remove(sipe_private
->buddies
, buddy
->name
);
539 gchar
*request
= g_strdup_printf("<m:URI>%s</m:URI>",
541 sip_soap_request(sipe_private
,
549 //updates groups on server
550 sipe_core_group_set_user(SIPE_CORE_PUBLIC
, b
->name
);
555 /** Property names to store in blist.xml */
556 #define ALIAS_PROP "alias"
557 #define EMAIL_PROP "email"
558 #define PHONE_PROP "phone"
559 #define PHONE_DISPLAY_PROP "phone-display"
560 #define PHONE_MOBILE_PROP "phone-mobile"
561 #define PHONE_MOBILE_DISPLAY_PROP "phone-mobile-display"
562 #define PHONE_HOME_PROP "phone-home"
563 #define PHONE_HOME_DISPLAY_PROP "phone-home-display"
564 #define PHONE_OTHER_PROP "phone-other"
565 #define PHONE_OTHER_DISPLAY_PROP "phone-other-display"
566 #define PHONE_CUSTOM1_PROP "phone-custom1"
567 #define PHONE_CUSTOM1_DISPLAY_PROP "phone-custom1-display"
568 #define SITE_PROP "site"
569 #define COMPANY_PROP "company"
570 #define DEPARTMENT_PROP "department"
571 #define TITLE_PROP "title"
572 #define OFFICE_PROP "office"
573 /** implies work address */
574 #define ADDRESS_STREET_PROP "address-street"
575 #define ADDRESS_CITY_PROP "address-city"
576 #define ADDRESS_STATE_PROP "address-state"
577 #define ADDRESS_ZIPCODE_PROP "address-zipcode"
578 #define ADDRESS_COUNTRYCODE_PROP "address-country-code"
581 * Tries to figure out user first and last name
582 * based on Display Name and email properties.
584 * Allocates memory - must be g_free()'d
588 * First Last - Company Name
591 * Last, First (C)(STP) (Company)
592 * first.last@company.com (preprocessed as "first last")
593 * first.last.company.com@reuters.net (preprocessed as "first last company com")
596 * user@company.com (preprocessed as "user")
597 * first.m.last@company.com (preprocessed as "first m last")
598 * user.company.com@reuters.net (preprocessed as "user company com")
601 sipe_get_first_last_names(struct sipe_core_private
*sipe_private
,
606 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
607 sipe_backend_buddy p_buddy
;
610 const char *first
, *last
;
613 gboolean has_comma
= FALSE
;
615 if (!sip
|| !uri
) return;
617 p_buddy
= sipe_backend_buddy_find(SIPE_CORE_PUBLIC
, uri
, NULL
);
619 if (!p_buddy
) return;
621 display_name
= sipe_backend_buddy_get_alias(SIPE_CORE_PUBLIC
, p_buddy
);
622 email
= sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC
, p_buddy
, SIPE_BUDDY_INFO_EMAIL
);
624 if (!display_name
&& !email
) return;
626 /* if no display name, make "first last anything_else" out of email */
627 if (email
&& !display_name
) {
628 display_name
= g_strndup(email
, strstr(email
, "@") - email
);
629 display_name
= sipe_utils_str_replace((tmp
= display_name
), ".", " ");
634 has_comma
= (strstr(display_name
, ",") != NULL
);
635 display_name
= sipe_utils_str_replace((tmp
= display_name
), ", ", " ");
637 display_name
= sipe_utils_str_replace((tmp
= display_name
), ",", " ");
641 parts
= g_strsplit(display_name
, " ", 0);
643 if (!parts
[0] || !parts
[1]) {
645 g_free(display_name
);
659 *first_name
= g_strstrip(g_strdup(first
));
663 *last_name
= g_strstrip(g_strdup(last
));
667 g_free(display_name
);
672 * Update user information
674 * @param uri buddy SIP URI with 'sip:' prefix whose info we want to change.
675 * @param property_name
676 * @param property_value may be modified to strip white space
678 void sipe_update_user_info(struct sipe_core_private
*sipe_private
,
680 sipe_buddy_info_fields propkey
,
681 char *property_value
)
683 GSList
*buddies
, *entry
;
686 property_value
= g_strstrip(property_value
);
688 entry
= buddies
= sipe_backend_buddy_find_all(SIPE_CORE_PUBLIC
, uri
, NULL
); /* all buddies in different groups */
693 sipe_backend_buddy p_buddy
= entry
->data
;
695 /* for Display Name */
696 if (propkey
== SIPE_BUDDY_INFO_DISPLAY_NAME
) {
697 alias
= sipe_backend_buddy_get_alias(SIPE_CORE_PUBLIC
, p_buddy
);
698 if (property_value
&& sipe_is_bad_alias(uri
, alias
)) {
699 SIPE_DEBUG_INFO("Replacing alias for %s with %s", uri
, property_value
);
700 sipe_backend_buddy_set_alias(SIPE_CORE_PUBLIC
, p_buddy
, property_value
);
704 server_alias
= sipe_backend_buddy_get_server_alias(SIPE_CORE_PUBLIC
, p_buddy
);
705 if (!is_empty(property_value
) &&
706 (!sipe_strequal(property_value
, server_alias
) || is_empty(server_alias
)) )
708 SIPE_DEBUG_INFO("Replacing service alias for %s with %s", uri
, property_value
);
709 sipe_backend_buddy_set_server_alias(SIPE_CORE_PUBLIC
, p_buddy
, property_value
);
711 g_free(server_alias
);
713 /* for other properties */
715 if (!is_empty(property_value
)) {
716 prop_str
= sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC
, p_buddy
, propkey
);
717 if (!prop_str
|| !sipe_strcase_equal(prop_str
, property_value
)) {
718 sipe_backend_buddy_set_string(SIPE_CORE_PUBLIC
, p_buddy
, propkey
, property_value
);
726 g_slist_free(buddies
);
730 * This method motivates Purple's Host (e.g. Pidgin) to update its UI
731 * by using standard Purple's means of signals and saved statuses.
733 * Thus all UI elements get updated: Status Button with Note, docklet.
734 * This is ablolutely important as both our status and note can come
735 * inbound (roaming) or be updated programmatically (e.g. based on our
738 void sipe_backend_account_status_and_note(struct sipe_core_private
*sipe_private
,
739 const gchar
*status_id
)
741 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
742 PurpleAccount
*account
= sip
->account
;
743 PurpleStatus
*status
= purple_account_get_active_status(account
);
744 const gchar
*message
= sip
->note
;
745 time_t *do_not_publish
= sip
->do_not_publish
;
746 gboolean changed
= TRUE
;
748 if (g_str_equal(status_id
, purple_status_get_id(status
)) &&
749 sipe_strequal(message
, purple_status_get_attr_string(status
, SIPE_STATUS_ATTR_ID_MESSAGE
)))
754 if (purple_savedstatus_is_idleaway()) {
759 PurpleSavedStatus
*saved_status
;
760 const PurpleStatusType
*acct_status_type
=
761 purple_status_type_find_with_id(account
->status_types
, status_id
);
762 PurpleStatusPrimitive primitive
= purple_status_type_get_primitive(acct_status_type
);
763 sipe_activity activity
= sipe_get_activity_by_token(status_id
);
765 saved_status
= purple_savedstatus_find_transient_by_type_and_message(primitive
, message
);
767 purple_savedstatus_set_substatus(saved_status
, account
, acct_status_type
, message
);
770 /* If this type+message is unique then create a new transient saved status
771 * Ref: gtkstatusbox.c
775 GList
*active_accts
= purple_accounts_get_all_active();
777 saved_status
= purple_savedstatus_new(NULL
, primitive
);
778 purple_savedstatus_set_message(saved_status
, message
);
780 for (tmp
= active_accts
; tmp
!= NULL
; tmp
= tmp
->next
) {
781 purple_savedstatus_set_substatus(saved_status
,
782 (PurpleAccount
*)tmp
->data
, acct_status_type
, message
);
784 g_list_free(active_accts
);
787 do_not_publish
[activity
] = time(NULL
);
788 SIPE_DEBUG_INFO("sipe_set_purple_account_status_and_note: do_not_publish[%s]=%d [now]",
789 status_id
, (int)do_not_publish
[activity
]);
791 /* Set the status for each account */
792 purple_savedstatus_activate(saved_status
);
796 /* IM Session (INVITE and MESSAGE methods) */
799 process_options_response(SIPE_UNUSED_PARAMETER
struct sipe_core_private
*sipe_private
,
801 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
805 if (msg
->response
!= 200) {
806 SIPE_DEBUG_INFO("process_options_response: OPTIONS response is %d", msg
->response
);
810 SIPE_DEBUG_INFO("process_options_response: body:\n%s", msg
->body
? msg
->body
: "");
816 * Asks UA/proxy about its capabilities.
818 static void sipe_options_request(struct sipe_core_private
*sipe_private
,
821 gchar
*to
= sip_uri(who
);
822 gchar
*contact
= get_contact(sipe_private
);
823 gchar
*request
= g_strdup_printf(
824 "Accept: application/sdp\r\n"
825 "Contact: %s\r\n", contact
);
828 sip_transport_request(sipe_private
,
835 process_options_response
);
842 sipe_convo_closed(PurpleConnection
* gc
, const char *who
)
844 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
846 SIPE_DEBUG_INFO("conversation with %s closed", who
);
847 sipe_session_close(sipe_private
,
848 sipe_session_find_im(sipe_private
, who
));
852 * Returns 2005-style activity and Availability.
854 * @param status Sipe statis id.
856 void sipe_get_act_avail_by_status_2005(const char *status
,
860 int avail
= 300; /* online */
861 int act
= 400; /* Available */
863 if (sipe_strequal(status
, SIPE_STATUS_ID_AWAY
)) {
865 //} else if (sipe_strequal(status, SIPE_STATUS_ID_LUNCH)) {
867 } else if (sipe_strequal(status
, SIPE_STATUS_ID_BRB
)) {
869 } else if (sipe_strequal(status
, SIPE_STATUS_ID_AVAILABLE
)) {
871 //} else if (sipe_strequal(status, SIPE_STATUS_ID_ON_PHONE)) {
873 } else if (sipe_strequal(status
, SIPE_STATUS_ID_BUSY
) ||
874 sipe_strequal(status
, SIPE_STATUS_ID_DND
)) {
876 } else if (sipe_strequal(status
, SIPE_STATUS_ID_INVISIBLE
) ||
877 sipe_strequal(status
, SIPE_STATUS_ID_OFFLINE
)) {
878 avail
= 0; /* offline */
881 act
= 400; /* Available */
884 if (activity
) *activity
= act
;
885 if (availability
) *availability
= avail
;
891 * @param activity 2005 aggregated activity. Ex.: 600
892 * @param availablity 2005 aggregated availablity. Ex.: 300
895 sipe_get_status_by_act_avail_2005(const int activity
,
896 const int availablity
,
897 char **activity_desc
)
899 const char *status_id
= NULL
;
900 const char *act
= NULL
;
902 if (activity
< 150) {
903 status_id
= SIPE_STATUS_ID_AWAY
;
904 } else if (activity
< 200) {
905 //status_id = SIPE_STATUS_ID_LUNCH;
906 status_id
= SIPE_STATUS_ID_AWAY
;
907 act
= sipe_activity_description(SIPE_ACTIVITY_LUNCH
);
908 } else if (activity
< 300) {
909 //status_id = SIPE_STATUS_ID_IDLE;
910 status_id
= SIPE_STATUS_ID_AWAY
;
911 act
= sipe_activity_description(SIPE_ACTIVITY_INACTIVE
);
912 } else if (activity
< 400) {
913 status_id
= SIPE_STATUS_ID_BRB
;
914 } else if (activity
< 500) {
915 status_id
= SIPE_STATUS_ID_AVAILABLE
;
916 } else if (activity
< 600) {
917 //status_id = SIPE_STATUS_ID_ON_PHONE;
918 status_id
= SIPE_STATUS_ID_BUSY
;
919 act
= sipe_activity_description(SIPE_ACTIVITY_ON_PHONE
);
920 } else if (activity
< 700) {
921 status_id
= SIPE_STATUS_ID_BUSY
;
922 } else if (activity
< 800) {
923 status_id
= SIPE_STATUS_ID_AWAY
;
925 status_id
= SIPE_STATUS_ID_AVAILABLE
;
928 if (availablity
< 100)
929 status_id
= SIPE_STATUS_ID_OFFLINE
;
931 if (activity_desc
&& act
) {
932 g_free(*activity_desc
);
933 *activity_desc
= g_strdup(act
);
940 * [MS-PRES] Table 3: Conversion of legacyInterop elements and attributes to MSRTC elements and attributes.
943 sipe_get_status_by_availability(int avail
,
944 gchar
**activity_desc
)
947 const char *act
= NULL
;
950 status
= SIPE_STATUS_ID_OFFLINE
;
951 } else if (avail
< 4500) {
952 status
= SIPE_STATUS_ID_AVAILABLE
;
953 } else if (avail
< 6000) {
954 //status = SIPE_STATUS_ID_IDLE;
955 status
= SIPE_STATUS_ID_AVAILABLE
;
956 act
= sipe_activity_description(SIPE_ACTIVITY_INACTIVE
);
957 } else if (avail
< 7500) {
958 status
= SIPE_STATUS_ID_BUSY
;
959 } else if (avail
< 9000) {
960 //status = SIPE_STATUS_ID_BUSYIDLE;
961 status
= SIPE_STATUS_ID_BUSY
;
962 act
= sipe_activity_description(SIPE_ACTIVITY_BUSYIDLE
);
963 } else if (avail
< 12000) {
964 status
= SIPE_STATUS_ID_DND
;
965 } else if (avail
< 15000) {
966 status
= SIPE_STATUS_ID_BRB
;
967 } else if (avail
< 18000) {
968 status
= SIPE_STATUS_ID_AWAY
;
970 status
= SIPE_STATUS_ID_OFFLINE
;
973 if (activity_desc
&& act
) {
974 g_free(*activity_desc
);
975 *activity_desc
= g_strdup(act
);
982 * Returns 2007-style availability value
984 * @param sipe_status_id (in)
985 * @param activity_token (out) Must be g_free()'d after use if consumed.
988 sipe_get_availability_by_status(const char* sipe_status_id
, char** activity_token
)
991 sipe_activity activity
= SIPE_ACTIVITY_UNSET
;
993 if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_AWAY
)) {
994 availability
= 15500;
995 if (!activity_token
|| !(*activity_token
)) {
996 activity
= SIPE_ACTIVITY_AWAY
;
998 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_BRB
)) {
999 availability
= 12500;
1000 activity
= SIPE_ACTIVITY_BRB
;
1001 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_DND
)) {
1002 availability
= 9500;
1003 activity
= SIPE_ACTIVITY_DND
;
1004 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_BUSY
)) {
1005 availability
= 6500;
1006 if (!activity_token
|| !(*activity_token
)) {
1007 activity
= SIPE_ACTIVITY_BUSY
;
1009 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_AVAILABLE
)) {
1010 availability
= 3500;
1011 activity
= SIPE_ACTIVITY_ONLINE
;
1012 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_UNKNOWN
)) {
1015 // Offline or invisible
1016 availability
= 18500;
1017 activity
= SIPE_ACTIVITY_OFFLINE
;
1020 if (activity_token
) {
1021 *activity_token
= g_strdup(sipe_activity_to_token(activity
));
1023 return availability
;
1027 * Whether user manually changed status or
1028 * it was changed automatically due to user
1029 * became inactive/active again
1032 sipe_is_user_state(struct sipe_core_private
*sipe_private
)
1034 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
1036 time_t now
= time(NULL
);
1038 SIPE_DEBUG_INFO("sipe_is_user_state: sip->idle_switch : %s", asctime(localtime(&(sip
->idle_switch
))));
1039 SIPE_DEBUG_INFO("sipe_is_user_state: now : %s", asctime(localtime(&now
)));
1041 res
= ((now
- SIPE_IDLE_SET_DELAY
* 2) >= sip
->idle_switch
);
1043 SIPE_DEBUG_INFO("sipe_is_user_state: res = %s", res
? "USER" : "MACHINE");
1047 gboolean
sipe_is_user_available(struct sipe_core_private
*sipe_private
)
1049 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
1050 return(sipe_strequal(sip
->status
, SIPE_STATUS_ID_AVAILABLE
));
1053 void send_presence_status(struct sipe_core_private
*sipe_private
,
1054 SIPE_UNUSED_PARAMETER gpointer unused
)
1056 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
1057 PurpleStatus
* status
= purple_account_get_active_status(sip
->account
);
1059 if (!status
) return;
1061 SIPE_DEBUG_INFO("send_presence_status: status: %s (%s)",
1062 purple_status_get_id(status
) ? purple_status_get_id(status
) : "",
1063 sipe_is_user_state(sipe_private
) ? "USER" : "MACHINE");
1065 sipe_cal_presence_publish(sipe_private
, FALSE
);
1068 /* temporary function */
1069 void sipe_purple_setup(struct sipe_core_public
*sipe_public
,
1070 PurpleConnection
*gc
)
1072 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA
;
1074 sip
->account
= purple_connection_get_account(gc
);
1078 sipe_blist_menu_free_containers(struct sipe_core_private
*sipe_private
);
1080 void sipe_connection_cleanup(struct sipe_core_private
*sipe_private
)
1082 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
1084 g_free(sipe_private
->epid
);
1085 sipe_private
->epid
= NULL
;
1087 sip_transport_disconnect(sipe_private
);
1089 sipe_schedule_cancel_all(sipe_private
);
1091 if (sip
->allow_events
) {
1092 GSList
*entry
= sip
->allow_events
;
1094 g_free(entry
->data
);
1095 entry
= entry
->next
;
1098 g_slist_free(sip
->allow_events
);
1100 sipe_ocs2007_free(sipe_private
);
1102 /* libpurple memory leak workaround */
1103 sipe_blist_menu_free_containers(sipe_private
);
1105 if (sipe_private
->contact
)
1106 g_free(sipe_private
->contact
);
1107 sipe_private
->contact
= NULL
;
1109 g_free(sip
->regcallid
);
1110 sip
->regcallid
= NULL
;
1112 if (sipe_private
->focus_factory_uri
)
1113 g_free(sipe_private
->focus_factory_uri
);
1114 sipe_private
->focus_factory_uri
= NULL
;
1117 sipe_cal_calendar_free(sip
->cal
);
1121 sipe_groupchat_free(sipe_private
);
1125 * A callback for g_hash_table_foreach_remove
1127 static gboolean
sipe_buddy_remove(SIPE_UNUSED_PARAMETER gpointer key
, gpointer buddy
,
1128 SIPE_UNUSED_PARAMETER gpointer user_data
)
1130 sipe_free_buddy((struct sipe_buddy
*) buddy
);
1132 /* We must return TRUE as the key/value have already been deleted */
1136 void sipe_buddy_free_all(struct sipe_core_private
*sipe_private
)
1138 g_hash_table_foreach_steal(sipe_private
->buddies
, sipe_buddy_remove
, NULL
);
1141 static void sipe_searchresults_im_buddy(PurpleConnection
*gc
, GList
*row
,
1142 SIPE_UNUSED_PARAMETER
void *user_data
)
1144 PurpleAccount
*acct
= purple_connection_get_account(gc
);
1145 char *id
= sip_uri_from_name((gchar
*)g_list_nth_data(row
, 0));
1146 PurpleConversation
*conv
= purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM
, id
, acct
);
1148 conv
= purple_conversation_new(PURPLE_CONV_TYPE_IM
, acct
, id
);
1149 purple_conversation_present(conv
);
1153 static void sipe_searchresults_add_buddy(PurpleConnection
*gc
, GList
*row
,
1154 SIPE_UNUSED_PARAMETER
void *user_data
)
1157 purple_blist_request_add_buddy(purple_connection_get_account(gc
),
1158 g_list_nth_data(row
, 0), _("Other Contacts"), g_list_nth_data(row
, 1));
1161 static gboolean
process_search_contact_response(struct sipe_core_private
*sipe_private
,
1163 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
1165 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
1166 PurpleNotifySearchResults
*results
;
1167 PurpleNotifySearchColumn
*column
;
1168 sipe_xml
*searchResults
;
1169 const sipe_xml
*mrow
;
1170 int match_count
= 0;
1171 gboolean more
= FALSE
;
1174 /* valid response? */
1175 if (msg
->response
!= 200) {
1176 SIPE_DEBUG_ERROR("process_search_contact_response: request failed (%d)",
1178 purple_notify_error(sip
->gc
, NULL
,
1179 _("Contact search failed"),
1184 SIPE_DEBUG_INFO("process_search_contact_response: body:\n%s", msg
->body
? msg
->body
: "");
1187 searchResults
= sipe_xml_parse(msg
->body
, msg
->bodylen
);
1188 if (!searchResults
) {
1189 SIPE_DEBUG_INFO_NOFORMAT("process_search_contact_response: no parseable searchResults");
1190 purple_notify_error(sip
->gc
, NULL
,
1191 _("Contact search failed"),
1197 mrow
= sipe_xml_child(searchResults
, "Body/Array/row");
1199 SIPE_DEBUG_ERROR_NOFORMAT("process_search_contact_response: no matches");
1200 purple_notify_error(sip
->gc
, NULL
,
1201 _("No contacts found"),
1204 sipe_xml_free(searchResults
);
1208 /* OK, we found something - show the results to the user */
1209 results
= purple_notify_searchresults_new();
1211 SIPE_DEBUG_ERROR_NOFORMAT("process_search_contact_response: Unable to display the search results.");
1212 purple_notify_error(sip
->gc
, NULL
, _("Unable to display the search results"), NULL
);
1214 sipe_xml_free(searchResults
);
1218 column
= purple_notify_searchresults_column_new(_("User name"));
1219 purple_notify_searchresults_column_add(results
, column
);
1221 column
= purple_notify_searchresults_column_new(_("Name"));
1222 purple_notify_searchresults_column_add(results
, column
);
1224 column
= purple_notify_searchresults_column_new(_("Company"));
1225 purple_notify_searchresults_column_add(results
, column
);
1227 column
= purple_notify_searchresults_column_new(_("Country"));
1228 purple_notify_searchresults_column_add(results
, column
);
1230 column
= purple_notify_searchresults_column_new(_("Email"));
1231 purple_notify_searchresults_column_add(results
, column
);
1233 for (/* initialized above */ ; mrow
; mrow
= sipe_xml_twin(mrow
)) {
1236 gchar
**uri_parts
= g_strsplit(sipe_xml_attribute(mrow
, "uri"), ":", 2);
1237 row
= g_list_append(row
, g_strdup(uri_parts
[1]));
1238 g_strfreev(uri_parts
);
1240 row
= g_list_append(row
, g_strdup(sipe_xml_attribute(mrow
, "displayName")));
1241 row
= g_list_append(row
, g_strdup(sipe_xml_attribute(mrow
, "company")));
1242 row
= g_list_append(row
, g_strdup(sipe_xml_attribute(mrow
, "country")));
1243 row
= g_list_append(row
, g_strdup(sipe_xml_attribute(mrow
, "email")));
1245 purple_notify_searchresults_row_add(results
, row
);
1249 if ((mrow
= sipe_xml_child(searchResults
, "Body/directorySearch/moreAvailable")) != NULL
) {
1250 char *data
= sipe_xml_data(mrow
);
1251 more
= (g_strcasecmp(data
, "true") == 0);
1255 secondary
= g_strdup_printf(
1256 dngettext(PACKAGE_NAME
,
1257 "Found %d contact%s:",
1258 "Found %d contacts%s:", match_count
),
1259 match_count
, more
? _(" (more matched your query)") : "");
1261 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_IM
, sipe_searchresults_im_buddy
);
1262 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_ADD
, sipe_searchresults_add_buddy
);
1263 purple_notify_searchresults(sip
->gc
, NULL
, NULL
, secondary
, results
, NULL
, NULL
);
1266 sipe_xml_free(searchResults
);
1270 #define SIPE_SOAP_SEARCH_ROW "<m:row m:attrib=\"%s\" m:value=\"%s\"/>"
1272 void sipe_search_contact_with_cb(PurpleConnection
*gc
, PurpleRequestFields
*fields
)
1274 GList
*entries
= purple_request_field_group_get_fields(purple_request_fields_get_groups(fields
)->data
);
1275 gchar
**attrs
= g_new(gchar
*, g_list_length(entries
) + 1);
1281 PurpleRequestField
*field
= entries
->data
;
1282 const char *id
= purple_request_field_get_id(field
);
1283 const char *value
= purple_request_field_string_get_value(field
);
1285 SIPE_DEBUG_INFO("sipe_search_contact_with_cb: %s = '%s'", id
, value
? value
: "");
1287 if (value
!= NULL
) attrs
[i
++] = g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW
, id
, value
);
1288 } while ((entries
= g_list_next(entries
)) != NULL
);
1292 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
1293 gchar
*query
= g_strjoinv(NULL
, attrs
);
1294 SIPE_DEBUG_INFO("sipe_search_contact_with_cb: rows:\n%s", query
? query
: "");
1295 sip_soap_directory_search(sipe_private
,
1298 process_search_contact_response
,
1306 void sipe_core_reset_status(struct sipe_core_public
*sipe_public
)
1308 struct sipe_core_private
*sipe_private
= SIPE_CORE_PRIVATE
;
1309 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
))
1310 sipe_ocs2007_reset_status(sipe_private
);
1312 sipe_ocs2005_reset_status(sipe_private
);
1315 /** for Access levels menu */
1316 #define INDENT_FMT " %s"
1318 /** Member is directly placed to access level container.
1319 * For example SIP URI of user is in the container.
1321 #define INDENT_MARKED_FMT "* %s"
1323 /** Member is indirectly belong to access level container.
1324 * For example 'sameEnterprise' is in the container and user
1325 * belongs to that same enterprise.
1327 #define INDENT_MARKED_INHERITED_FMT "= %s"
1329 GSList
*sipe_core_buddy_info(struct sipe_core_public
*sipe_public
,
1331 const gchar
*status_name
,
1334 struct sipe_core_private
*sipe_private
= SIPE_CORE_PRIVATE
;
1336 gboolean is_oof_note
= FALSE
;
1337 const gchar
*activity
= NULL
;
1338 gchar
*calendar
= NULL
;
1339 const gchar
*meeting_subject
= NULL
;
1340 const gchar
*meeting_location
= NULL
;
1341 gchar
*access_text
= NULL
;
1342 GSList
*info
= NULL
;
1344 #define SIPE_ADD_BUDDY_INFO_COMMON(l, t) \
1346 struct sipe_buddy_info *sbi = g_malloc(sizeof(struct sipe_buddy_info)); \
1349 info = g_slist_append(info, sbi); \
1351 #define SIPE_ADD_BUDDY_INFO(l, t) SIPE_ADD_BUDDY_INFO_COMMON((l), g_markup_escape_text((t), -1))
1352 #define SIPE_ADD_BUDDY_INFO_NOESCAPE(l, t) SIPE_ADD_BUDDY_INFO_COMMON((l), (t))
1354 if (sipe_public
) { //happens on pidgin exit
1355 struct sipe_buddy
*sbuddy
= g_hash_table_lookup(sipe_private
->buddies
, name
);
1357 note
= sbuddy
->note
;
1358 is_oof_note
= sbuddy
->is_oof_note
;
1359 activity
= sbuddy
->activity
;
1360 calendar
= sipe_cal_get_description(sbuddy
);
1361 meeting_subject
= sbuddy
->meeting_subject
;
1362 meeting_location
= sbuddy
->meeting_location
;
1364 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
1365 gboolean is_group_access
= FALSE
;
1366 const int container_id
= sipe_ocs2007_find_access_level(sipe_private
, "user", sipe_get_no_sip_uri(name
), &is_group_access
);
1367 const char *access_level
= sipe_ocs2007_access_level_name(container_id
);
1368 access_text
= is_group_access
?
1369 g_strdup(access_level
) :
1370 g_strdup_printf(INDENT_MARKED_FMT
, access_level
);
1377 const gchar
*status_str
= activity
? activity
: status_name
;
1379 SIPE_ADD_BUDDY_INFO(_("Status"), status_str
);
1381 if (is_online
&& !is_empty(calendar
))
1383 SIPE_ADD_BUDDY_INFO(_("Calendar"), calendar
);
1386 if (!is_empty(meeting_location
))
1388 SIPE_DEBUG_INFO("sipe_tooltip_text: %s meeting location: '%s'", name
, meeting_location
);
1389 SIPE_ADD_BUDDY_INFO(_("Meeting in"), meeting_location
);
1391 if (!is_empty(meeting_subject
))
1393 SIPE_DEBUG_INFO("sipe_tooltip_text: %s meeting subject: '%s'", name
, meeting_subject
);
1394 SIPE_ADD_BUDDY_INFO(_("Meeting about"), meeting_subject
);
1398 SIPE_DEBUG_INFO("sipe_tooltip_text: %s note: '%s'", name
, note
);
1399 SIPE_ADD_BUDDY_INFO_NOESCAPE(is_oof_note
? _("Out of office note") : _("Note"),
1400 g_strdup_printf("<i>%s</i>", note
));
1403 SIPE_ADD_BUDDY_INFO(_("Access level"), access_text
);
1404 g_free(access_text
);
1410 static PurpleBuddy
*
1411 purple_blist_add_buddy_clone(PurpleGroup
* group
, PurpleBuddy
* buddy
)
1414 const gchar
*server_alias
, *email
;
1415 const PurpleStatus
*status
= purple_presence_get_active_status(purple_buddy_get_presence(buddy
));
1417 clone
= purple_buddy_new(buddy
->account
, buddy
->name
, buddy
->alias
);
1419 purple_blist_add_buddy(clone
, NULL
, group
, NULL
);
1421 server_alias
= purple_buddy_get_server_alias(buddy
);
1423 purple_blist_server_alias_buddy(clone
, server_alias
);
1426 email
= purple_blist_node_get_string(&buddy
->node
, EMAIL_PROP
);
1428 purple_blist_node_set_string(&clone
->node
, EMAIL_PROP
, email
);
1431 purple_presence_set_status_active(purple_buddy_get_presence(clone
), purple_status_get_id(status
), TRUE
);
1433 purple_prpl_got_user_status(clone
->account
, clone
->name
, purple_status_get_id(status
), NULL
);
1438 sipe_buddy_menu_copy_to_cb(PurpleBlistNode
*node
, const char *group_name
)
1440 PurpleBuddy
*buddy
, *b
;
1441 PurpleConnection
*gc
;
1442 PurpleGroup
* group
= purple_find_group(group_name
);
1444 g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node
));
1446 buddy
= (PurpleBuddy
*)node
;
1448 SIPE_DEBUG_INFO("sipe_buddy_menu_copy_to_cb: copying %s to %s", buddy
->name
, group_name
);
1449 gc
= purple_account_get_connection(buddy
->account
);
1451 b
= purple_find_buddy_in_group(buddy
->account
, buddy
->name
, group
);
1453 b
= purple_blist_add_buddy_clone(group
, buddy
);
1456 sipe_add_buddy(gc
, b
, group
);
1460 sipe_buddy_menu_chat_new_cb(PurpleBuddy
*buddy
)
1462 struct sipe_core_private
*sipe_private
= PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE
;
1464 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_new_cb: buddy->name=%s", buddy
->name
);
1466 /* 2007+ conference */
1467 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
))
1469 sipe_conf_add(sipe_private
, buddy
->name
);
1471 else /* 2005- multiparty chat */
1473 gchar
*self
= sip_uri_self(sipe_private
);
1474 struct sip_session
*session
;
1476 session
= sipe_session_add_chat(sipe_private
,
1480 session
->chat_session
->backend
= sipe_backend_chat_create(SIPE_CORE_PUBLIC
,
1481 session
->chat_session
,
1482 session
->chat_session
->title
,
1486 sipe_im_invite(sipe_private
, session
, buddy
->name
, NULL
, NULL
, NULL
, FALSE
);
1491 * For 2007+ conference only.
1494 sipe_buddy_menu_chat_make_leader_cb(PurpleBuddy
*buddy
,
1495 struct sipe_chat_session
*chat_session
)
1497 struct sipe_core_private
*sipe_private
= PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE
;
1498 struct sip_session
*session
;
1500 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_make_leader_cb: buddy->name=%s", buddy
->name
);
1501 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_make_leader_cb: chat_title=%s", chat_session
->title
);
1503 session
= sipe_session_find_chat(sipe_private
, chat_session
);
1505 sipe_conf_modify_user_role(sipe_private
, session
, buddy
->name
);
1509 * For 2007+ conference only.
1512 sipe_buddy_menu_chat_remove_cb(PurpleBuddy
*buddy
,
1513 struct sipe_chat_session
*chat_session
)
1515 struct sipe_core_private
*sipe_private
= PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE
;
1516 struct sip_session
*session
;
1518 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_remove_cb: buddy->name=%s", buddy
->name
);
1519 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_remove_cb: chat_title=%s", chat_session
->title
);
1521 session
= sipe_session_find_chat(sipe_private
, chat_session
);
1523 sipe_conf_delete_user(sipe_private
, session
, buddy
->name
);
1527 sipe_buddy_menu_chat_invite_cb(PurpleBuddy
*buddy
,
1528 struct sipe_chat_session
*chat_session
)
1530 struct sipe_core_private
*sipe_private
= PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE
;
1532 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_invite_cb: buddy->name=%s", buddy
->name
);
1533 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_invite_cb: chat_title=%s", chat_session
->title
);
1535 sipe_core_chat_invite(SIPE_CORE_PUBLIC
, chat_session
, buddy
->name
);
1539 sipe_buddy_menu_make_call_cb(PurpleBuddy
*buddy
, const char *phone
)
1541 struct sipe_core_private
*sipe_private
= PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE
;
1543 SIPE_DEBUG_INFO("sipe_buddy_menu_make_call_cb: buddy->name=%s", buddy
->name
);
1545 char *tel_uri
= sip_to_tel_uri(phone
);
1547 SIPE_DEBUG_INFO("sipe_buddy_menu_make_call_cb: going to call number: %s", tel_uri
? tel_uri
: "");
1548 sip_csta_make_call(sipe_private
, tel_uri
);
1555 sipe_buddy_menu_access_level_help_cb(PurpleBuddy
*buddy
)
1557 /** Translators: replace with URL to localized page
1558 * If it doesn't exist copy the original URL */
1559 purple_notify_uri(buddy
->account
->gc
, _("https://sourceforge.net/apps/mediawiki/sipe/index.php?title=Access_Levels"));
1563 sipe_buddy_menu_send_email_cb(PurpleBuddy
*buddy
)
1566 SIPE_DEBUG_INFO("sipe_buddy_menu_send_email_cb: buddy->name=%s", buddy
->name
);
1568 email
= purple_blist_node_get_string(&buddy
->node
, EMAIL_PROP
);
1571 char *command_line
= g_strdup_printf(
1577 " mailto:%s", email
);
1578 SIPE_DEBUG_INFO("sipe_buddy_menu_send_email_cb: going to call email client: %s", command_line
);
1580 g_spawn_command_line_async(command_line
, NULL
);
1581 g_free(command_line
);
1585 SIPE_DEBUG_INFO("sipe_buddy_menu_send_email_cb: no email address stored for buddy=%s", buddy
->name
);
1590 sipe_buddy_menu_access_level_cb(PurpleBuddy
*buddy
,
1591 struct sipe_container
*container
)
1593 struct sipe_core_private
*sipe_private
= PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE
;
1594 sipe_ocs2007_change_access_level_from_container(sipe_private
,
1599 sipe_get_access_control_menu(struct sipe_core_private
*sipe_private
,
1603 * A menu which appear when right-clicking on buddy in contact list.
1606 sipe_buddy_menu(PurpleBuddy
*buddy
)
1608 PurpleBlistNode
*g_node
;
1609 PurpleGroup
*gr_parent
;
1610 PurpleMenuAction
*act
;
1612 GList
*menu_groups
= NULL
;
1613 struct sipe_core_private
*sipe_private
= PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE
;
1614 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
1616 gchar
*self
= sip_uri_self(sipe_private
);
1618 SIPE_SESSION_FOREACH
{
1619 if (!sipe_strcase_equal(self
, buddy
->name
) && session
->chat_session
)
1621 struct sipe_chat_session
*chat_session
= session
->chat_session
;
1622 gboolean is_conf
= (chat_session
->type
== SIPE_CHAT_TYPE_CONFERENCE
);
1624 if (sipe_backend_chat_find(chat_session
->backend
, buddy
->name
))
1626 gboolean conf_op
= sipe_backend_chat_is_operator(chat_session
->backend
, self
);
1629 && !sipe_backend_chat_is_operator(chat_session
->backend
, buddy
->name
) /* Not conf OP */
1630 && conf_op
) /* We are a conf OP */
1632 gchar
*label
= g_strdup_printf(_("Make leader of '%s'"),
1633 chat_session
->title
);
1634 act
= purple_menu_action_new(label
,
1635 PURPLE_CALLBACK(sipe_buddy_menu_chat_make_leader_cb
),
1636 chat_session
, NULL
);
1638 menu
= g_list_prepend(menu
, act
);
1642 && conf_op
) /* We are a conf OP */
1644 gchar
*label
= g_strdup_printf(_("Remove from '%s'"),
1645 chat_session
->title
);
1646 act
= purple_menu_action_new(label
,
1647 PURPLE_CALLBACK(sipe_buddy_menu_chat_remove_cb
),
1648 chat_session
, NULL
);
1650 menu
= g_list_prepend(menu
, act
);
1656 || (is_conf
&& !session
->locked
))
1658 gchar
*label
= g_strdup_printf(_("Invite to '%s'"),
1659 chat_session
->title
);
1660 act
= purple_menu_action_new(label
,
1661 PURPLE_CALLBACK(sipe_buddy_menu_chat_invite_cb
),
1662 chat_session
, NULL
);
1664 menu
= g_list_prepend(menu
, act
);
1668 } SIPE_SESSION_FOREACH_END
;
1670 act
= purple_menu_action_new(_("New chat"),
1671 PURPLE_CALLBACK(sipe_buddy_menu_chat_new_cb
),
1673 menu
= g_list_prepend(menu
, act
);
1675 if (sip
->csta
&& !sip
->csta
->line_status
) {
1677 const char *phone_disp_str
;
1680 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_PROP
);
1681 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_DISPLAY_PROP
);
1683 gchar
*label
= g_strdup_printf(_("Work %s"),
1684 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
1685 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
1689 menu
= g_list_prepend(menu
, act
);
1693 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_MOBILE_PROP
);
1694 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_MOBILE_DISPLAY_PROP
);
1696 gchar
*label
= g_strdup_printf(_("Mobile %s"),
1697 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
1698 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
1702 menu
= g_list_prepend(menu
, act
);
1706 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_HOME_PROP
);
1707 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_HOME_DISPLAY_PROP
);
1709 gchar
*label
= g_strdup_printf(_("Home %s"),
1710 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
1711 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
1715 menu
= g_list_prepend(menu
, act
);
1719 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_OTHER_PROP
);
1720 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_OTHER_DISPLAY_PROP
);
1722 gchar
*label
= g_strdup_printf(_("Other %s"),
1723 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
1724 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
1728 menu
= g_list_prepend(menu
, act
);
1732 phone
= purple_blist_node_get_string(&buddy
->node
, PHONE_CUSTOM1_PROP
);
1733 phone_disp_str
= purple_blist_node_get_string(&buddy
->node
, PHONE_CUSTOM1_DISPLAY_PROP
);
1735 gchar
*label
= g_strdup_printf(_("Custom1 %s"),
1736 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
1737 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
1741 menu
= g_list_prepend(menu
, act
);
1745 email
= purple_blist_node_get_string(&buddy
->node
, EMAIL_PROP
);
1747 act
= purple_menu_action_new(_("Send email..."),
1748 PURPLE_CALLBACK(sipe_buddy_menu_send_email_cb
),
1750 menu
= g_list_prepend(menu
, act
);
1754 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
1755 GList
*menu_access_levels
= sipe_get_access_control_menu(sipe_private
, buddy
->name
);
1757 act
= purple_menu_action_new(_("Access level"),
1759 NULL
, menu_access_levels
);
1760 menu
= g_list_prepend(menu
, act
);
1764 gr_parent
= purple_buddy_get_group(buddy
);
1765 for (g_node
= purple_blist_get_root(); g_node
; g_node
= g_node
->next
) {
1768 if (g_node
->type
!= PURPLE_BLIST_GROUP_NODE
)
1771 group
= (PurpleGroup
*)g_node
;
1772 if (group
== gr_parent
)
1775 if (purple_find_buddy_in_group(buddy
->account
, buddy
->name
, group
))
1778 act
= purple_menu_action_new(purple_group_get_name(group
),
1779 PURPLE_CALLBACK(sipe_buddy_menu_copy_to_cb
),
1781 menu_groups
= g_list_prepend(menu_groups
, act
);
1783 /* Coverity complains about RESOURCE_LEAK here - no idea how to fix it */
1784 menu_groups
= g_list_reverse(menu_groups
);
1786 act
= purple_menu_action_new(_("Copy to"),
1789 menu
= g_list_prepend(menu
, act
);
1791 menu
= g_list_reverse(menu
);
1798 sipe_ask_access_domain_cb(PurpleConnection
*gc
, PurpleRequestFields
*fields
)
1800 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
1801 const char *domain
= purple_request_fields_get_string(fields
, "access_domain");
1802 guint index
= purple_request_fields_get_choice(fields
, "container_id");
1803 sipe_ocs2007_change_access_level_for_domain(sipe_private
,
1809 sipe_ask_access_domain(struct sipe_core_private
*sipe_private
)
1811 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
1812 PurpleAccount
*account
= sip
->account
;
1813 PurpleConnection
*gc
= sip
->gc
;
1814 PurpleRequestFields
*fields
;
1815 PurpleRequestFieldGroup
*g
;
1816 PurpleRequestField
*f
;
1818 fields
= purple_request_fields_new();
1820 g
= purple_request_field_group_new(NULL
);
1821 f
= purple_request_field_string_new("access_domain", _("Domain"), "partner-company.com", FALSE
);
1822 purple_request_field_set_required(f
, TRUE
);
1823 purple_request_field_group_add_field(g
, f
);
1825 f
= purple_request_field_choice_new("container_id", _("Access level"), 0);
1826 purple_request_field_choice_add(f
, _("Personal")); /* index 0 */
1827 purple_request_field_choice_add(f
, _("Team"));
1828 purple_request_field_choice_add(f
, _("Company"));
1829 purple_request_field_choice_add(f
, _("Public"));
1830 purple_request_field_choice_add(f
, _("Blocked")); /* index 4 */
1831 purple_request_field_choice_set_default_value(f
, 3); /* index */
1832 purple_request_field_set_required(f
, TRUE
);
1833 purple_request_field_group_add_field(g
, f
);
1835 purple_request_fields_add_group(fields
, g
);
1837 purple_request_fields(gc
, _("Add new domain"),
1838 _("Add new domain"), NULL
, fields
,
1839 _("Add"), G_CALLBACK(sipe_ask_access_domain_cb
),
1841 account
, NULL
, NULL
, gc
);
1845 sipe_buddy_menu_access_level_add_domain_cb(PurpleBuddy
*buddy
)
1847 sipe_ask_access_domain(PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE
);
1851 * Workaround for missing libpurple API to release resources allocated
1852 * during blist_node_menu() callback. See also:
1854 * <http://developer.pidgin.im/ticket/12597>
1856 * We remember all memory blocks in a list and deallocate them when
1858 * - the next time we enter the callback, or
1859 * - the account is disconnected
1861 * That means that after the buddy menu has been closed we have unused
1862 * resources but at least we don't leak them anymore...
1865 sipe_blist_menu_free_containers(struct sipe_core_private
*sipe_private
)
1867 GSList
*entry
= sipe_private
->blist_menu_containers
;
1869 sipe_ocs2007_free_container(entry
->data
);
1870 entry
= entry
->next
;
1872 g_slist_free(sipe_private
->blist_menu_containers
);
1873 sipe_private
->blist_menu_containers
= NULL
;
1877 sipe_blist_menu_remember_container(struct sipe_core_private
*sipe_private
,
1878 struct sipe_container
*container
)
1880 sipe_private
->blist_menu_containers
= g_slist_prepend(sipe_private
->blist_menu_containers
,
1885 sipe_get_access_levels_menu(struct sipe_core_private
*sipe_private
,
1886 const char* member_type
,
1887 const char* member_value
,
1888 const gboolean extra_menu
)
1890 GList
*menu_access_levels
= NULL
;
1893 PurpleMenuAction
*act
;
1894 struct sipe_container
*container
;
1895 gboolean is_group_access
= FALSE
;
1896 int container_id
= sipe_ocs2007_find_access_level(sipe_private
,
1900 guint container_max
= sipe_ocs2007_containers();
1902 for (i
= 1; i
<= container_max
; i
++) {
1903 /* to put Blocked level last in menu list.
1904 * Blocked should remaim in the first place in the containers[] array.
1906 unsigned int j
= (i
== container_max
) ? 0 : i
;
1907 int container_j
= sipe_ocs2007_container_id(j
);
1908 const gchar
*acc_level_name
= sipe_ocs2007_access_level_name(container_j
);
1910 container
= sipe_ocs2007_create_container(j
,
1915 /* libpurple memory leak workaround */
1916 sipe_blist_menu_remember_container(sipe_private
, container
);
1918 /* current container/access level */
1919 if (container_j
== container_id
) {
1920 menu_name
= is_group_access
?
1921 g_strdup_printf(INDENT_MARKED_INHERITED_FMT
, acc_level_name
) :
1922 g_strdup_printf(INDENT_MARKED_FMT
, acc_level_name
);
1924 menu_name
= g_strdup_printf(INDENT_FMT
, acc_level_name
);
1927 act
= purple_menu_action_new(menu_name
,
1928 PURPLE_CALLBACK(sipe_buddy_menu_access_level_cb
),
1931 menu_access_levels
= g_list_prepend(menu_access_levels
, act
);
1934 if (extra_menu
&& (container_id
>= 0)) {
1936 act
= purple_menu_action_new(" --------------", NULL
, NULL
, NULL
);
1937 menu_access_levels
= g_list_prepend(menu_access_levels
, act
);
1939 if (!is_group_access
) {
1940 container
= sipe_ocs2007_create_container(0,
1945 /* libpurple memory leak workaround */
1946 sipe_blist_menu_remember_container(sipe_private
, container
);
1948 /* Translators: remove (clear) previously assigned access level */
1949 menu_name
= g_strdup_printf(INDENT_FMT
, _("Unspecify"));
1950 act
= purple_menu_action_new(menu_name
,
1951 PURPLE_CALLBACK(sipe_buddy_menu_access_level_cb
),
1954 menu_access_levels
= g_list_prepend(menu_access_levels
, act
);
1958 menu_access_levels
= g_list_reverse(menu_access_levels
);
1959 return menu_access_levels
;
1963 sipe_get_access_groups_menu(struct sipe_core_private
*sipe_private
)
1965 GList
*menu_access_groups
= NULL
;
1966 PurpleMenuAction
*act
;
1967 GSList
*access_domains
;
1970 act
= purple_menu_action_new(_("People in my company"),
1972 NULL
, sipe_get_access_levels_menu(sipe_private
, "sameEnterprise", NULL
, FALSE
));
1973 menu_access_groups
= g_list_prepend(menu_access_groups
, act
);
1975 /* this is original name, don't edit */
1976 act
= purple_menu_action_new(_("People in domains connected with my company"),
1978 NULL
, sipe_get_access_levels_menu(sipe_private
, "federated", NULL
, FALSE
));
1979 menu_access_groups
= g_list_prepend(menu_access_groups
, act
);
1981 act
= purple_menu_action_new(_("People in public domains"),
1983 NULL
, sipe_get_access_levels_menu(sipe_private
, "publicCloud", NULL
, TRUE
));
1984 menu_access_groups
= g_list_prepend(menu_access_groups
, act
);
1986 access_domains
= sipe_ocs2007_get_access_domains(sipe_private
);
1987 entry
= access_domains
;
1989 gchar
*domain
= entry
->data
;
1990 gchar
*menu_name
= g_strdup_printf(_("People at %s"), domain
);
1992 /* takes over ownership of entry->data (= domain) */
1993 act
= purple_menu_action_new(menu_name
,
1995 NULL
, sipe_get_access_levels_menu(sipe_private
, "domain", domain
, TRUE
));
1996 menu_access_groups
= g_list_prepend(menu_access_groups
, act
);
1999 entry
= entry
->next
;
2001 g_slist_free(access_domains
);
2004 /* People in domains connected with my company */
2005 act
= purple_menu_action_new("-------------------------------------------", NULL
, NULL
, NULL
);
2006 menu_access_groups
= g_list_prepend(menu_access_groups
, act
);
2008 act
= purple_menu_action_new(_("Add new domain..."),
2009 PURPLE_CALLBACK(sipe_buddy_menu_access_level_add_domain_cb
),
2011 menu_access_groups
= g_list_prepend(menu_access_groups
, act
);
2013 menu_access_groups
= g_list_reverse(menu_access_groups
);
2015 return menu_access_groups
;
2019 sipe_get_access_control_menu(struct sipe_core_private
*sipe_private
,
2022 GList
*menu_access_levels
= NULL
;
2023 GList
*menu_access_groups
= NULL
;
2025 PurpleMenuAction
*act
;
2027 /* libpurple memory leak workaround */
2028 sipe_blist_menu_free_containers(sipe_private
);
2030 menu_access_levels
= sipe_get_access_levels_menu(sipe_private
, "user", sipe_get_no_sip_uri(uri
), TRUE
);
2032 menu_access_groups
= sipe_get_access_groups_menu(sipe_private
);
2034 menu_name
= g_strdup_printf(INDENT_FMT
, _("Access groups"));
2035 act
= purple_menu_action_new(menu_name
,
2037 NULL
, menu_access_groups
);
2039 menu_access_levels
= g_list_append(menu_access_levels
, act
);
2041 menu_name
= g_strdup_printf(INDENT_FMT
, _("Online help..."));
2042 act
= purple_menu_action_new(menu_name
,
2043 PURPLE_CALLBACK(sipe_buddy_menu_access_level_help_cb
),
2046 menu_access_levels
= g_list_append(menu_access_levels
, act
);
2048 return menu_access_levels
;
2052 process_get_info_response(struct sipe_core_private
*sipe_private
,
2053 struct sipmsg
*msg
, struct transaction
*trans
)
2055 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
2056 char *uri
= trans
->payload
->data
;
2058 PurpleNotifyUserInfo
*info
;
2059 PurpleBuddy
*pbuddy
= NULL
;
2060 struct sipe_buddy
*sbuddy
;
2061 const char *alias
= NULL
;
2062 char *device_name
= NULL
;
2063 char *server_alias
= NULL
;
2064 char *phone_number
= NULL
;
2067 char *first_name
= NULL
;
2068 char *last_name
= NULL
;
2070 if (!sip
) return FALSE
;
2072 SIPE_DEBUG_INFO("Fetching %s's user info for %s", uri
, sipe_private
->username
);
2074 pbuddy
= purple_find_buddy((PurpleAccount
*)sip
->account
, uri
);
2075 alias
= purple_buddy_get_local_alias(pbuddy
);
2077 //will query buddy UA's capabilities and send answer to log
2078 sipe_options_request(sipe_private
, uri
);
2080 sbuddy
= g_hash_table_lookup(sipe_private
->buddies
, uri
);
2082 device_name
= sbuddy
->device_name
? g_strdup(sbuddy
->device_name
) : NULL
;
2085 info
= purple_notify_user_info_new();
2087 if (msg
->response
!= 200) {
2088 SIPE_DEBUG_INFO("process_get_info_response: SERVICE response is %d", msg
->response
);
2090 sipe_xml
*searchResults
;
2091 const sipe_xml
*mrow
;
2093 SIPE_DEBUG_INFO("process_get_info_response: body:\n%s", msg
->body
? msg
->body
: "");
2094 searchResults
= sipe_xml_parse(msg
->body
, msg
->bodylen
);
2095 if (!searchResults
) {
2096 SIPE_DEBUG_INFO_NOFORMAT("process_get_info_response: no parseable searchResults");
2097 } else if ((mrow
= sipe_xml_child(searchResults
, "Body/Array/row"))) {
2099 server_alias
= g_strdup(sipe_xml_attribute(mrow
, "displayName"));
2100 email
= g_strdup(sipe_xml_attribute(mrow
, "email"));
2101 phone_number
= g_strdup(sipe_xml_attribute(mrow
, "phone"));
2103 /* For 2007 system we will take this from ContactCard -
2104 * it has cleaner tel: URIs at least
2106 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
2107 char *tel_uri
= sip_to_tel_uri(phone_number
);
2108 /* trims its parameters, so call first */
2109 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_DISPLAY_NAME
, server_alias
);
2110 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_EMAIL
, email
);
2111 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_WORK_PHONE
, tel_uri
);
2112 sipe_update_user_info(sipe_private
, uri
, SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY
, phone_number
);
2116 #if PURPLE_VERSION_CHECK(3,0,0)
2117 #define PURPLE_NOTIFY_USER_INFO_ADD_PAIR purple_notify_user_info_add_pair_html
2119 #define PURPLE_NOTIFY_USER_INFO_ADD_PAIR purple_notify_user_info_add_pair
2122 if (server_alias
&& strlen(server_alias
) > 0) {
2123 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info
, _("Display name"), server_alias
);
2125 if ((value
= sipe_xml_attribute(mrow
, "title")) && strlen(value
) > 0) {
2126 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info
, _("Job title"), value
);
2128 if ((value
= sipe_xml_attribute(mrow
, "office")) && strlen(value
) > 0) {
2129 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info
, _("Office"), value
);
2131 if (phone_number
&& strlen(phone_number
) > 0) {
2132 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info
, _("Business phone"), phone_number
);
2134 if ((value
= sipe_xml_attribute(mrow
, "company")) && strlen(value
) > 0) {
2135 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info
, _("Company"), value
);
2137 if ((value
= sipe_xml_attribute(mrow
, "city")) && strlen(value
) > 0) {
2138 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info
, _("City"), value
);
2140 if ((value
= sipe_xml_attribute(mrow
, "state")) && strlen(value
) > 0) {
2141 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info
, _("State"), value
);
2143 if ((value
= sipe_xml_attribute(mrow
, "country")) && strlen(value
) > 0) {
2144 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info
, _("Country"), value
);
2146 if (email
&& strlen(email
) > 0) {
2147 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info
, _("Email address"), email
);
2151 sipe_xml_free(searchResults
);
2154 purple_notify_user_info_add_section_break(info
);
2156 if (is_empty(server_alias
)) {
2157 g_free(server_alias
);
2158 server_alias
= g_strdup(purple_buddy_get_server_alias(pbuddy
));
2160 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info
, _("Display name"), server_alias
);
2164 /* present alias if it differs from server alias */
2165 if (alias
&& !sipe_strequal(alias
, server_alias
))
2167 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info
, _("Alias"), alias
);
2170 if (is_empty(email
)) {
2172 email
= g_strdup(purple_blist_node_get_string(&pbuddy
->node
, EMAIL_PROP
));
2174 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info
, _("Email address"), email
);
2178 site
= purple_blist_node_get_string(&pbuddy
->node
, SITE_PROP
);
2180 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info
, _("Site"), site
);
2183 sipe_get_first_last_names(sipe_private
, uri
, &first_name
, &last_name
);
2184 if (first_name
&& last_name
) {
2185 char *link
= g_strconcat("http://www.linkedin.com/pub/dir/", first_name
, "/", last_name
, NULL
);
2187 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info
, _("Find on LinkedIn"), link
);
2194 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info
, _("Device"), device_name
);
2197 /* show a buddy's user info in a nice dialog box */
2198 purple_notify_userinfo(sip
->gc
, /* connection the buddy info came through */
2199 uri
, /* buddy's URI */
2201 NULL
, /* callback called when dialog closed */
2202 NULL
); /* userdata for callback */
2204 g_free(phone_number
);
2205 g_free(server_alias
);
2207 g_free(device_name
);
2213 * AD search first, LDAP based
2215 void sipe_get_info(PurpleConnection
*gc
, const char *username
)
2217 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
2218 char *row
= g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW
, "msRTCSIP-PrimaryUserAddress", username
);
2219 struct transaction_payload
*payload
= g_new0(struct transaction_payload
, 1);
2221 payload
->destroy
= g_free
;
2222 payload
->data
= g_strdup(username
);
2224 SIPE_DEBUG_INFO("sipe_get_info: row: %s", row
? row
: "");
2225 sip_soap_directory_search(sipe_private
,
2228 process_get_info_response
,