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
);
556 * Tries to figure out user first and last name
557 * based on Display Name and email properties.
559 * Allocates memory - must be g_free()'d
563 * First Last - Company Name
566 * Last, First (C)(STP) (Company)
567 * first.last@company.com (preprocessed as "first last")
568 * first.last.company.com@reuters.net (preprocessed as "first last company com")
571 * user@company.com (preprocessed as "user")
572 * first.m.last@company.com (preprocessed as "first m last")
573 * user.company.com@reuters.net (preprocessed as "user company com")
576 sipe_get_first_last_names(struct sipe_core_private
*sipe_private
,
581 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
582 sipe_backend_buddy p_buddy
;
585 const char *first
, *last
;
588 gboolean has_comma
= FALSE
;
590 if (!sip
|| !uri
) return;
592 p_buddy
= sipe_backend_buddy_find(SIPE_CORE_PUBLIC
, uri
, NULL
);
594 if (!p_buddy
) return;
596 display_name
= sipe_backend_buddy_get_alias(SIPE_CORE_PUBLIC
, p_buddy
);
597 email
= sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC
, p_buddy
, SIPE_BUDDY_INFO_EMAIL
);
599 if (!display_name
&& !email
) return;
601 /* if no display name, make "first last anything_else" out of email */
602 if (email
&& !display_name
) {
603 display_name
= g_strndup(email
, strstr(email
, "@") - email
);
604 display_name
= sipe_utils_str_replace((tmp
= display_name
), ".", " ");
609 has_comma
= (strstr(display_name
, ",") != NULL
);
610 display_name
= sipe_utils_str_replace((tmp
= display_name
), ", ", " ");
612 display_name
= sipe_utils_str_replace((tmp
= display_name
), ",", " ");
616 parts
= g_strsplit(display_name
, " ", 0);
618 if (!parts
[0] || !parts
[1]) {
620 g_free(display_name
);
634 *first_name
= g_strstrip(g_strdup(first
));
638 *last_name
= g_strstrip(g_strdup(last
));
642 g_free(display_name
);
647 * This method motivates Purple's Host (e.g. Pidgin) to update its UI
648 * by using standard Purple's means of signals and saved statuses.
650 * Thus all UI elements get updated: Status Button with Note, docklet.
651 * This is ablolutely important as both our status and note can come
652 * inbound (roaming) or be updated programmatically (e.g. based on our
655 void sipe_backend_account_status_and_note(struct sipe_core_private
*sipe_private
,
656 const gchar
*status_id
)
658 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
659 PurpleAccount
*account
= sip
->account
;
660 PurpleStatus
*status
= purple_account_get_active_status(account
);
661 const gchar
*message
= sip
->note
;
662 time_t *do_not_publish
= sip
->do_not_publish
;
663 gboolean changed
= TRUE
;
665 if (g_str_equal(status_id
, purple_status_get_id(status
)) &&
666 sipe_strequal(message
, purple_status_get_attr_string(status
, SIPE_STATUS_ATTR_ID_MESSAGE
)))
671 if (purple_savedstatus_is_idleaway()) {
676 PurpleSavedStatus
*saved_status
;
677 const PurpleStatusType
*acct_status_type
=
678 purple_status_type_find_with_id(account
->status_types
, status_id
);
679 PurpleStatusPrimitive primitive
= purple_status_type_get_primitive(acct_status_type
);
680 sipe_activity activity
= sipe_get_activity_by_token(status_id
);
682 saved_status
= purple_savedstatus_find_transient_by_type_and_message(primitive
, message
);
684 purple_savedstatus_set_substatus(saved_status
, account
, acct_status_type
, message
);
687 /* If this type+message is unique then create a new transient saved status
688 * Ref: gtkstatusbox.c
692 GList
*active_accts
= purple_accounts_get_all_active();
694 saved_status
= purple_savedstatus_new(NULL
, primitive
);
695 purple_savedstatus_set_message(saved_status
, message
);
697 for (tmp
= active_accts
; tmp
!= NULL
; tmp
= tmp
->next
) {
698 purple_savedstatus_set_substatus(saved_status
,
699 (PurpleAccount
*)tmp
->data
, acct_status_type
, message
);
701 g_list_free(active_accts
);
704 do_not_publish
[activity
] = time(NULL
);
705 SIPE_DEBUG_INFO("sipe_set_purple_account_status_and_note: do_not_publish[%s]=%d [now]",
706 status_id
, (int)do_not_publish
[activity
]);
708 /* Set the status for each account */
709 purple_savedstatus_activate(saved_status
);
713 /* IM Session (INVITE and MESSAGE methods) */
716 process_options_response(SIPE_UNUSED_PARAMETER
struct sipe_core_private
*sipe_private
,
718 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
722 if (msg
->response
!= 200) {
723 SIPE_DEBUG_INFO("process_options_response: OPTIONS response is %d", msg
->response
);
727 SIPE_DEBUG_INFO("process_options_response: body:\n%s", msg
->body
? msg
->body
: "");
733 * Asks UA/proxy about its capabilities.
735 static void sipe_options_request(struct sipe_core_private
*sipe_private
,
738 gchar
*to
= sip_uri(who
);
739 gchar
*contact
= get_contact(sipe_private
);
740 gchar
*request
= g_strdup_printf(
741 "Accept: application/sdp\r\n"
742 "Contact: %s\r\n", contact
);
745 sip_transport_request(sipe_private
,
752 process_options_response
);
759 sipe_convo_closed(PurpleConnection
* gc
, const char *who
)
761 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
763 SIPE_DEBUG_INFO("conversation with %s closed", who
);
764 sipe_session_close(sipe_private
,
765 sipe_session_find_im(sipe_private
, who
));
769 * Returns 2005-style activity and Availability.
771 * @param status Sipe statis id.
773 void sipe_get_act_avail_by_status_2005(const char *status
,
777 int avail
= 300; /* online */
778 int act
= 400; /* Available */
780 if (sipe_strequal(status
, SIPE_STATUS_ID_AWAY
)) {
782 //} else if (sipe_strequal(status, SIPE_STATUS_ID_LUNCH)) {
784 } else if (sipe_strequal(status
, SIPE_STATUS_ID_BRB
)) {
786 } else if (sipe_strequal(status
, SIPE_STATUS_ID_AVAILABLE
)) {
788 //} else if (sipe_strequal(status, SIPE_STATUS_ID_ON_PHONE)) {
790 } else if (sipe_strequal(status
, SIPE_STATUS_ID_BUSY
) ||
791 sipe_strequal(status
, SIPE_STATUS_ID_DND
)) {
793 } else if (sipe_strequal(status
, SIPE_STATUS_ID_INVISIBLE
) ||
794 sipe_strequal(status
, SIPE_STATUS_ID_OFFLINE
)) {
795 avail
= 0; /* offline */
798 act
= 400; /* Available */
801 if (activity
) *activity
= act
;
802 if (availability
) *availability
= avail
;
808 * @param activity 2005 aggregated activity. Ex.: 600
809 * @param availablity 2005 aggregated availablity. Ex.: 300
812 sipe_get_status_by_act_avail_2005(const int activity
,
813 const int availablity
,
814 char **activity_desc
)
816 const char *status_id
= NULL
;
817 const char *act
= NULL
;
819 if (activity
< 150) {
820 status_id
= SIPE_STATUS_ID_AWAY
;
821 } else if (activity
< 200) {
822 //status_id = SIPE_STATUS_ID_LUNCH;
823 status_id
= SIPE_STATUS_ID_AWAY
;
824 act
= sipe_activity_description(SIPE_ACTIVITY_LUNCH
);
825 } else if (activity
< 300) {
826 //status_id = SIPE_STATUS_ID_IDLE;
827 status_id
= SIPE_STATUS_ID_AWAY
;
828 act
= sipe_activity_description(SIPE_ACTIVITY_INACTIVE
);
829 } else if (activity
< 400) {
830 status_id
= SIPE_STATUS_ID_BRB
;
831 } else if (activity
< 500) {
832 status_id
= SIPE_STATUS_ID_AVAILABLE
;
833 } else if (activity
< 600) {
834 //status_id = SIPE_STATUS_ID_ON_PHONE;
835 status_id
= SIPE_STATUS_ID_BUSY
;
836 act
= sipe_activity_description(SIPE_ACTIVITY_ON_PHONE
);
837 } else if (activity
< 700) {
838 status_id
= SIPE_STATUS_ID_BUSY
;
839 } else if (activity
< 800) {
840 status_id
= SIPE_STATUS_ID_AWAY
;
842 status_id
= SIPE_STATUS_ID_AVAILABLE
;
845 if (availablity
< 100)
846 status_id
= SIPE_STATUS_ID_OFFLINE
;
848 if (activity_desc
&& act
) {
849 g_free(*activity_desc
);
850 *activity_desc
= g_strdup(act
);
857 * [MS-PRES] Table 3: Conversion of legacyInterop elements and attributes to MSRTC elements and attributes.
860 sipe_get_status_by_availability(int avail
,
861 gchar
**activity_desc
)
864 const char *act
= NULL
;
867 status
= SIPE_STATUS_ID_OFFLINE
;
868 } else if (avail
< 4500) {
869 status
= SIPE_STATUS_ID_AVAILABLE
;
870 } else if (avail
< 6000) {
871 //status = SIPE_STATUS_ID_IDLE;
872 status
= SIPE_STATUS_ID_AVAILABLE
;
873 act
= sipe_activity_description(SIPE_ACTIVITY_INACTIVE
);
874 } else if (avail
< 7500) {
875 status
= SIPE_STATUS_ID_BUSY
;
876 } else if (avail
< 9000) {
877 //status = SIPE_STATUS_ID_BUSYIDLE;
878 status
= SIPE_STATUS_ID_BUSY
;
879 act
= sipe_activity_description(SIPE_ACTIVITY_BUSYIDLE
);
880 } else if (avail
< 12000) {
881 status
= SIPE_STATUS_ID_DND
;
882 } else if (avail
< 15000) {
883 status
= SIPE_STATUS_ID_BRB
;
884 } else if (avail
< 18000) {
885 status
= SIPE_STATUS_ID_AWAY
;
887 status
= SIPE_STATUS_ID_OFFLINE
;
890 if (activity_desc
&& act
) {
891 g_free(*activity_desc
);
892 *activity_desc
= g_strdup(act
);
899 * Returns 2007-style availability value
901 * @param sipe_status_id (in)
902 * @param activity_token (out) Must be g_free()'d after use if consumed.
905 sipe_get_availability_by_status(const char* sipe_status_id
, char** activity_token
)
908 sipe_activity activity
= SIPE_ACTIVITY_UNSET
;
910 if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_AWAY
)) {
911 availability
= 15500;
912 if (!activity_token
|| !(*activity_token
)) {
913 activity
= SIPE_ACTIVITY_AWAY
;
915 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_BRB
)) {
916 availability
= 12500;
917 activity
= SIPE_ACTIVITY_BRB
;
918 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_DND
)) {
920 activity
= SIPE_ACTIVITY_DND
;
921 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_BUSY
)) {
923 if (!activity_token
|| !(*activity_token
)) {
924 activity
= SIPE_ACTIVITY_BUSY
;
926 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_AVAILABLE
)) {
928 activity
= SIPE_ACTIVITY_ONLINE
;
929 } else if (sipe_strequal(sipe_status_id
, SIPE_STATUS_ID_UNKNOWN
)) {
932 // Offline or invisible
933 availability
= 18500;
934 activity
= SIPE_ACTIVITY_OFFLINE
;
937 if (activity_token
) {
938 *activity_token
= g_strdup(sipe_activity_to_token(activity
));
944 * Whether user manually changed status or
945 * it was changed automatically due to user
946 * became inactive/active again
949 sipe_is_user_state(struct sipe_core_private
*sipe_private
)
951 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
953 time_t now
= time(NULL
);
955 SIPE_DEBUG_INFO("sipe_is_user_state: sip->idle_switch : %s", asctime(localtime(&(sip
->idle_switch
))));
956 SIPE_DEBUG_INFO("sipe_is_user_state: now : %s", asctime(localtime(&now
)));
958 res
= ((now
- SIPE_IDLE_SET_DELAY
* 2) >= sip
->idle_switch
);
960 SIPE_DEBUG_INFO("sipe_is_user_state: res = %s", res
? "USER" : "MACHINE");
964 gboolean
sipe_is_user_available(struct sipe_core_private
*sipe_private
)
966 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
967 return(sipe_strequal(sip
->status
, SIPE_STATUS_ID_AVAILABLE
));
970 void send_presence_status(struct sipe_core_private
*sipe_private
,
971 SIPE_UNUSED_PARAMETER gpointer unused
)
973 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
974 PurpleStatus
* status
= purple_account_get_active_status(sip
->account
);
978 SIPE_DEBUG_INFO("send_presence_status: status: %s (%s)",
979 purple_status_get_id(status
) ? purple_status_get_id(status
) : "",
980 sipe_is_user_state(sipe_private
) ? "USER" : "MACHINE");
982 sipe_cal_presence_publish(sipe_private
, FALSE
);
985 /* temporary function */
986 void sipe_purple_setup(struct sipe_core_public
*sipe_public
,
987 PurpleConnection
*gc
)
989 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA
;
991 sip
->account
= purple_connection_get_account(gc
);
995 sipe_blist_menu_free_containers(struct sipe_core_private
*sipe_private
);
997 void sipe_connection_cleanup(struct sipe_core_private
*sipe_private
)
999 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
1001 g_free(sipe_private
->epid
);
1002 sipe_private
->epid
= NULL
;
1004 sip_transport_disconnect(sipe_private
);
1006 sipe_schedule_cancel_all(sipe_private
);
1008 if (sip
->allow_events
) {
1009 GSList
*entry
= sip
->allow_events
;
1011 g_free(entry
->data
);
1012 entry
= entry
->next
;
1015 g_slist_free(sip
->allow_events
);
1017 sipe_ocs2007_free(sipe_private
);
1019 /* libpurple memory leak workaround */
1020 sipe_blist_menu_free_containers(sipe_private
);
1022 if (sipe_private
->contact
)
1023 g_free(sipe_private
->contact
);
1024 sipe_private
->contact
= NULL
;
1026 g_free(sip
->regcallid
);
1027 sip
->regcallid
= NULL
;
1029 if (sipe_private
->focus_factory_uri
)
1030 g_free(sipe_private
->focus_factory_uri
);
1031 sipe_private
->focus_factory_uri
= NULL
;
1034 sipe_cal_calendar_free(sip
->cal
);
1038 sipe_groupchat_free(sipe_private
);
1042 * A callback for g_hash_table_foreach_remove
1044 static gboolean
sipe_buddy_remove(SIPE_UNUSED_PARAMETER gpointer key
, gpointer buddy
,
1045 SIPE_UNUSED_PARAMETER gpointer user_data
)
1047 sipe_free_buddy((struct sipe_buddy
*) buddy
);
1049 /* We must return TRUE as the key/value have already been deleted */
1053 void sipe_buddy_free_all(struct sipe_core_private
*sipe_private
)
1055 g_hash_table_foreach_steal(sipe_private
->buddies
, sipe_buddy_remove
, NULL
);
1058 static void sipe_searchresults_im_buddy(PurpleConnection
*gc
, GList
*row
,
1059 SIPE_UNUSED_PARAMETER
void *user_data
)
1061 PurpleAccount
*acct
= purple_connection_get_account(gc
);
1062 char *id
= sip_uri_from_name((gchar
*)g_list_nth_data(row
, 0));
1063 PurpleConversation
*conv
= purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM
, id
, acct
);
1065 conv
= purple_conversation_new(PURPLE_CONV_TYPE_IM
, acct
, id
);
1066 purple_conversation_present(conv
);
1070 static void sipe_searchresults_add_buddy(PurpleConnection
*gc
, GList
*row
,
1071 SIPE_UNUSED_PARAMETER
void *user_data
)
1074 purple_blist_request_add_buddy(purple_connection_get_account(gc
),
1075 g_list_nth_data(row
, 0), _("Other Contacts"), g_list_nth_data(row
, 1));
1078 static gboolean
process_search_contact_response(struct sipe_core_private
*sipe_private
,
1080 SIPE_UNUSED_PARAMETER
struct transaction
*trans
)
1082 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
1083 PurpleNotifySearchResults
*results
;
1084 PurpleNotifySearchColumn
*column
;
1085 sipe_xml
*searchResults
;
1086 const sipe_xml
*mrow
;
1087 int match_count
= 0;
1088 gboolean more
= FALSE
;
1091 /* valid response? */
1092 if (msg
->response
!= 200) {
1093 SIPE_DEBUG_ERROR("process_search_contact_response: request failed (%d)",
1095 sipe_backend_notify_error(SIPE_CORE_PUBLIC
,
1096 _("Contact search failed"),
1101 SIPE_DEBUG_INFO("process_search_contact_response: body:\n%s", msg
->body
? msg
->body
: "");
1104 searchResults
= sipe_xml_parse(msg
->body
, msg
->bodylen
);
1105 if (!searchResults
) {
1106 SIPE_DEBUG_INFO_NOFORMAT("process_search_contact_response: no parseable searchResults");
1107 sipe_backend_notify_error(SIPE_CORE_PUBLIC
,
1108 _("Contact search failed"),
1114 mrow
= sipe_xml_child(searchResults
, "Body/Array/row");
1116 SIPE_DEBUG_ERROR_NOFORMAT("process_search_contact_response: no matches");
1117 sipe_backend_notify_error(SIPE_CORE_PUBLIC
,
1118 _("No contacts found"),
1121 sipe_xml_free(searchResults
);
1125 /* OK, we found something - show the results to the user */
1126 results
= purple_notify_searchresults_new();
1128 SIPE_DEBUG_ERROR_NOFORMAT("process_search_contact_response: Unable to display the search results.");
1129 sipe_backend_notify_error(SIPE_CORE_PUBLIC
,
1130 _("Unable to display the search results"),
1133 sipe_xml_free(searchResults
);
1137 column
= purple_notify_searchresults_column_new(_("User name"));
1138 purple_notify_searchresults_column_add(results
, column
);
1140 column
= purple_notify_searchresults_column_new(_("Name"));
1141 purple_notify_searchresults_column_add(results
, column
);
1143 column
= purple_notify_searchresults_column_new(_("Company"));
1144 purple_notify_searchresults_column_add(results
, column
);
1146 column
= purple_notify_searchresults_column_new(_("Country"));
1147 purple_notify_searchresults_column_add(results
, column
);
1149 column
= purple_notify_searchresults_column_new(_("Email"));
1150 purple_notify_searchresults_column_add(results
, column
);
1152 for (/* initialized above */ ; mrow
; mrow
= sipe_xml_twin(mrow
)) {
1155 gchar
**uri_parts
= g_strsplit(sipe_xml_attribute(mrow
, "uri"), ":", 2);
1156 row
= g_list_append(row
, g_strdup(uri_parts
[1]));
1157 g_strfreev(uri_parts
);
1159 row
= g_list_append(row
, g_strdup(sipe_xml_attribute(mrow
, "displayName")));
1160 row
= g_list_append(row
, g_strdup(sipe_xml_attribute(mrow
, "company")));
1161 row
= g_list_append(row
, g_strdup(sipe_xml_attribute(mrow
, "country")));
1162 row
= g_list_append(row
, g_strdup(sipe_xml_attribute(mrow
, "email")));
1164 purple_notify_searchresults_row_add(results
, row
);
1168 if ((mrow
= sipe_xml_child(searchResults
, "Body/directorySearch/moreAvailable")) != NULL
) {
1169 char *data
= sipe_xml_data(mrow
);
1170 more
= (g_strcasecmp(data
, "true") == 0);
1174 secondary
= g_strdup_printf(
1175 dngettext(PACKAGE_NAME
,
1176 "Found %d contact%s:",
1177 "Found %d contacts%s:", match_count
),
1178 match_count
, more
? _(" (more matched your query)") : "");
1180 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_IM
, sipe_searchresults_im_buddy
);
1181 purple_notify_searchresults_button_add(results
, PURPLE_NOTIFY_BUTTON_ADD
, sipe_searchresults_add_buddy
);
1182 purple_notify_searchresults(sip
->gc
, NULL
, NULL
, secondary
, results
, NULL
, NULL
);
1185 sipe_xml_free(searchResults
);
1189 #define SIPE_SOAP_SEARCH_ROW "<m:row m:attrib=\"%s\" m:value=\"%s\"/>"
1191 void sipe_search_contact_with_cb(PurpleConnection
*gc
, PurpleRequestFields
*fields
)
1193 GList
*entries
= purple_request_field_group_get_fields(purple_request_fields_get_groups(fields
)->data
);
1194 gchar
**attrs
= g_new(gchar
*, g_list_length(entries
) + 1);
1200 PurpleRequestField
*field
= entries
->data
;
1201 const char *id
= purple_request_field_get_id(field
);
1202 const char *value
= purple_request_field_string_get_value(field
);
1204 SIPE_DEBUG_INFO("sipe_search_contact_with_cb: %s = '%s'", id
, value
? value
: "");
1206 if (value
!= NULL
) attrs
[i
++] = g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW
, id
, value
);
1207 } while ((entries
= g_list_next(entries
)) != NULL
);
1211 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
1212 gchar
*query
= g_strjoinv(NULL
, attrs
);
1213 SIPE_DEBUG_INFO("sipe_search_contact_with_cb: rows:\n%s", query
? query
: "");
1214 sip_soap_directory_search(sipe_private
,
1217 process_search_contact_response
,
1225 void sipe_core_reset_status(struct sipe_core_public
*sipe_public
)
1227 struct sipe_core_private
*sipe_private
= SIPE_CORE_PRIVATE
;
1228 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
))
1229 sipe_ocs2007_reset_status(sipe_private
);
1231 sipe_ocs2005_reset_status(sipe_private
);
1234 /** for Access levels menu */
1235 #define INDENT_FMT " %s"
1237 /** Member is directly placed to access level container.
1238 * For example SIP URI of user is in the container.
1240 #define INDENT_MARKED_FMT "* %s"
1242 /** Member is indirectly belong to access level container.
1243 * For example 'sameEnterprise' is in the container and user
1244 * belongs to that same enterprise.
1246 #define INDENT_MARKED_INHERITED_FMT "= %s"
1248 GSList
*sipe_core_buddy_info(struct sipe_core_public
*sipe_public
,
1250 const gchar
*status_name
,
1253 struct sipe_core_private
*sipe_private
= SIPE_CORE_PRIVATE
;
1255 gboolean is_oof_note
= FALSE
;
1256 const gchar
*activity
= NULL
;
1257 gchar
*calendar
= NULL
;
1258 const gchar
*meeting_subject
= NULL
;
1259 const gchar
*meeting_location
= NULL
;
1260 gchar
*access_text
= NULL
;
1261 GSList
*info
= NULL
;
1263 #define SIPE_ADD_BUDDY_INFO_COMMON(l, t) \
1265 struct sipe_buddy_info *sbi = g_malloc(sizeof(struct sipe_buddy_info)); \
1268 info = g_slist_append(info, sbi); \
1270 #define SIPE_ADD_BUDDY_INFO(l, t) SIPE_ADD_BUDDY_INFO_COMMON((l), g_markup_escape_text((t), -1))
1271 #define SIPE_ADD_BUDDY_INFO_NOESCAPE(l, t) SIPE_ADD_BUDDY_INFO_COMMON((l), (t))
1273 if (sipe_public
) { //happens on pidgin exit
1274 struct sipe_buddy
*sbuddy
= g_hash_table_lookup(sipe_private
->buddies
, name
);
1276 note
= sbuddy
->note
;
1277 is_oof_note
= sbuddy
->is_oof_note
;
1278 activity
= sbuddy
->activity
;
1279 calendar
= sipe_cal_get_description(sbuddy
);
1280 meeting_subject
= sbuddy
->meeting_subject
;
1281 meeting_location
= sbuddy
->meeting_location
;
1283 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
1284 gboolean is_group_access
= FALSE
;
1285 const int container_id
= sipe_ocs2007_find_access_level(sipe_private
, "user", sipe_get_no_sip_uri(name
), &is_group_access
);
1286 const char *access_level
= sipe_ocs2007_access_level_name(container_id
);
1287 access_text
= is_group_access
?
1288 g_strdup(access_level
) :
1289 g_strdup_printf(INDENT_MARKED_FMT
, access_level
);
1296 const gchar
*status_str
= activity
? activity
: status_name
;
1298 SIPE_ADD_BUDDY_INFO(_("Status"), status_str
);
1300 if (is_online
&& !is_empty(calendar
))
1302 SIPE_ADD_BUDDY_INFO(_("Calendar"), calendar
);
1305 if (!is_empty(meeting_location
))
1307 SIPE_DEBUG_INFO("sipe_tooltip_text: %s meeting location: '%s'", name
, meeting_location
);
1308 SIPE_ADD_BUDDY_INFO(_("Meeting in"), meeting_location
);
1310 if (!is_empty(meeting_subject
))
1312 SIPE_DEBUG_INFO("sipe_tooltip_text: %s meeting subject: '%s'", name
, meeting_subject
);
1313 SIPE_ADD_BUDDY_INFO(_("Meeting about"), meeting_subject
);
1317 SIPE_DEBUG_INFO("sipe_tooltip_text: %s note: '%s'", name
, note
);
1318 SIPE_ADD_BUDDY_INFO_NOESCAPE(is_oof_note
? _("Out of office note") : _("Note"),
1319 g_strdup_printf("<i>%s</i>", note
));
1322 SIPE_ADD_BUDDY_INFO(_("Access level"), access_text
);
1323 g_free(access_text
);
1329 static PurpleBuddy
*
1330 purple_blist_add_buddy_clone(PurpleGroup
* group
, PurpleBuddy
* buddy
)
1332 struct sipe_core_private
*sipe_private
= PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE
;
1334 gchar
*server_alias
, *email
;
1335 const PurpleStatus
*status
= purple_presence_get_active_status(purple_buddy_get_presence(buddy
));
1337 clone
= sipe_backend_buddy_add(SIPE_CORE_PUBLIC
,
1342 server_alias
= sipe_backend_buddy_get_server_alias(SIPE_CORE_PUBLIC
,
1345 sipe_backend_buddy_set_server_alias(SIPE_CORE_PUBLIC
,
1348 g_free(server_alias
);
1351 email
= sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC
,
1353 SIPE_BUDDY_INFO_EMAIL
);
1355 sipe_backend_buddy_set_string(SIPE_CORE_PUBLIC
,
1357 SIPE_BUDDY_INFO_EMAIL
,
1362 purple_presence_set_status_active(purple_buddy_get_presence(clone
),
1363 purple_status_get_id(status
),
1365 /* for UI to update */
1366 sipe_backend_buddy_set_status(SIPE_CORE_PUBLIC
,
1368 purple_status_get_id(status
));
1374 sipe_buddy_menu_copy_to_cb(PurpleBlistNode
*node
, const char *group_name
)
1376 PurpleBuddy
*buddy
, *b
;
1377 PurpleConnection
*gc
;
1378 PurpleGroup
* group
= purple_find_group(group_name
);
1380 g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node
));
1382 buddy
= (PurpleBuddy
*)node
;
1384 SIPE_DEBUG_INFO("sipe_buddy_menu_copy_to_cb: copying %s to %s", buddy
->name
, group_name
);
1385 gc
= purple_account_get_connection(buddy
->account
);
1387 b
= purple_find_buddy_in_group(buddy
->account
, buddy
->name
, group
);
1389 b
= purple_blist_add_buddy_clone(group
, buddy
);
1392 sipe_add_buddy(gc
, b
, group
);
1396 sipe_buddy_menu_chat_new_cb(PurpleBuddy
*buddy
)
1398 struct sipe_core_private
*sipe_private
= PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE
;
1400 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_new_cb: buddy->name=%s", buddy
->name
);
1402 /* 2007+ conference */
1403 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
))
1405 sipe_conf_add(sipe_private
, buddy
->name
);
1407 else /* 2005- multiparty chat */
1409 gchar
*self
= sip_uri_self(sipe_private
);
1410 struct sip_session
*session
;
1412 session
= sipe_session_add_chat(sipe_private
,
1416 session
->chat_session
->backend
= sipe_backend_chat_create(SIPE_CORE_PUBLIC
,
1417 session
->chat_session
,
1418 session
->chat_session
->title
,
1422 sipe_im_invite(sipe_private
, session
, buddy
->name
, NULL
, NULL
, NULL
, FALSE
);
1427 * For 2007+ conference only.
1430 sipe_buddy_menu_chat_make_leader_cb(PurpleBuddy
*buddy
,
1431 struct sipe_chat_session
*chat_session
)
1433 struct sipe_core_private
*sipe_private
= PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE
;
1434 struct sip_session
*session
;
1436 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_make_leader_cb: buddy->name=%s", buddy
->name
);
1437 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_make_leader_cb: chat_title=%s", chat_session
->title
);
1439 session
= sipe_session_find_chat(sipe_private
, chat_session
);
1441 sipe_conf_modify_user_role(sipe_private
, session
, buddy
->name
);
1445 * For 2007+ conference only.
1448 sipe_buddy_menu_chat_remove_cb(PurpleBuddy
*buddy
,
1449 struct sipe_chat_session
*chat_session
)
1451 struct sipe_core_private
*sipe_private
= PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE
;
1452 struct sip_session
*session
;
1454 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_remove_cb: buddy->name=%s", buddy
->name
);
1455 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_remove_cb: chat_title=%s", chat_session
->title
);
1457 session
= sipe_session_find_chat(sipe_private
, chat_session
);
1459 sipe_conf_delete_user(sipe_private
, session
, buddy
->name
);
1463 sipe_buddy_menu_chat_invite_cb(PurpleBuddy
*buddy
,
1464 struct sipe_chat_session
*chat_session
)
1466 struct sipe_core_private
*sipe_private
= PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE
;
1468 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_invite_cb: buddy->name=%s", buddy
->name
);
1469 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_invite_cb: chat_title=%s", chat_session
->title
);
1471 sipe_core_chat_invite(SIPE_CORE_PUBLIC
, chat_session
, buddy
->name
);
1475 sipe_buddy_menu_make_call_cb(PurpleBuddy
*buddy
, const char *phone
)
1477 struct sipe_core_private
*sipe_private
= PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE
;
1479 SIPE_DEBUG_INFO("sipe_buddy_menu_make_call_cb: buddy->name=%s", buddy
->name
);
1481 char *tel_uri
= sip_to_tel_uri(phone
);
1483 SIPE_DEBUG_INFO("sipe_buddy_menu_make_call_cb: going to call number: %s", tel_uri
? tel_uri
: "");
1484 sip_csta_make_call(sipe_private
, tel_uri
);
1491 sipe_buddy_menu_access_level_help_cb(PurpleBuddy
*buddy
)
1493 /** Translators: replace with URL to localized page
1494 * If it doesn't exist copy the original URL */
1495 purple_notify_uri(buddy
->account
->gc
, _("https://sourceforge.net/apps/mediawiki/sipe/index.php?title=Access_Levels"));
1499 sipe_buddy_menu_send_email_cb(PurpleBuddy
*buddy
)
1501 struct sipe_core_private
*sipe_private
= PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE
;
1503 SIPE_DEBUG_INFO("sipe_buddy_menu_send_email_cb: buddy->name=%s", buddy
->name
);
1505 email
= sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC
,
1507 SIPE_BUDDY_INFO_EMAIL
);
1510 char *command_line
= g_strdup_printf(
1516 " mailto:%s", email
);
1517 SIPE_DEBUG_INFO("sipe_buddy_menu_send_email_cb: going to call email client: %s", command_line
);
1520 g_spawn_command_line_async(command_line
, NULL
);
1521 g_free(command_line
);
1525 SIPE_DEBUG_INFO("sipe_buddy_menu_send_email_cb: no email address stored for buddy=%s", buddy
->name
);
1530 sipe_buddy_menu_access_level_cb(PurpleBuddy
*buddy
,
1531 struct sipe_container
*container
)
1533 struct sipe_core_private
*sipe_private
= PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE
;
1534 sipe_ocs2007_change_access_level_from_container(sipe_private
,
1539 sipe_get_access_control_menu(struct sipe_core_private
*sipe_private
,
1543 * A menu which appear when right-clicking on buddy in contact list.
1546 sipe_buddy_menu(PurpleBuddy
*buddy
)
1548 PurpleBlistNode
*g_node
;
1549 PurpleGroup
*gr_parent
;
1550 PurpleMenuAction
*act
;
1552 GList
*menu_groups
= NULL
;
1553 struct sipe_core_private
*sipe_private
= PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE
;
1554 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
1556 gchar
*self
= sip_uri_self(sipe_private
);
1558 SIPE_SESSION_FOREACH
{
1559 if (!sipe_strcase_equal(self
, buddy
->name
) && session
->chat_session
)
1561 struct sipe_chat_session
*chat_session
= session
->chat_session
;
1562 gboolean is_conf
= (chat_session
->type
== SIPE_CHAT_TYPE_CONFERENCE
);
1564 if (sipe_backend_chat_find(chat_session
->backend
, buddy
->name
))
1566 gboolean conf_op
= sipe_backend_chat_is_operator(chat_session
->backend
, self
);
1569 && !sipe_backend_chat_is_operator(chat_session
->backend
, buddy
->name
) /* Not conf OP */
1570 && conf_op
) /* We are a conf OP */
1572 gchar
*label
= g_strdup_printf(_("Make leader of '%s'"),
1573 chat_session
->title
);
1574 act
= purple_menu_action_new(label
,
1575 PURPLE_CALLBACK(sipe_buddy_menu_chat_make_leader_cb
),
1576 chat_session
, NULL
);
1578 menu
= g_list_prepend(menu
, act
);
1582 && conf_op
) /* We are a conf OP */
1584 gchar
*label
= g_strdup_printf(_("Remove from '%s'"),
1585 chat_session
->title
);
1586 act
= purple_menu_action_new(label
,
1587 PURPLE_CALLBACK(sipe_buddy_menu_chat_remove_cb
),
1588 chat_session
, NULL
);
1590 menu
= g_list_prepend(menu
, act
);
1596 || (is_conf
&& !session
->locked
))
1598 gchar
*label
= g_strdup_printf(_("Invite to '%s'"),
1599 chat_session
->title
);
1600 act
= purple_menu_action_new(label
,
1601 PURPLE_CALLBACK(sipe_buddy_menu_chat_invite_cb
),
1602 chat_session
, NULL
);
1604 menu
= g_list_prepend(menu
, act
);
1608 } SIPE_SESSION_FOREACH_END
;
1610 act
= purple_menu_action_new(_("New chat"),
1611 PURPLE_CALLBACK(sipe_buddy_menu_chat_new_cb
),
1613 menu
= g_list_prepend(menu
, act
);
1615 if (sip
->csta
&& !sip
->csta
->line_status
) {
1617 gchar
*phone_disp_str
;
1620 phone
= sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC
,
1622 SIPE_BUDDY_INFO_WORK_PHONE
);
1623 phone_disp_str
= sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC
,
1625 SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY
);
1627 gchar
*label
= g_strdup_printf(_("Work %s"),
1628 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
1629 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
1633 menu
= g_list_prepend(menu
, act
);
1636 g_free(phone_disp_str
);
1639 phone
= sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC
,
1641 SIPE_BUDDY_INFO_MOBILE_PHONE
);
1642 phone_disp_str
= sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC
,
1644 SIPE_BUDDY_INFO_MOBILE_PHONE_DISPLAY
);
1646 gchar
*label
= g_strdup_printf(_("Mobile %s"),
1647 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
1648 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
1652 menu
= g_list_prepend(menu
, act
);
1655 g_free(phone_disp_str
);
1658 phone
= sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC
,
1660 SIPE_BUDDY_INFO_HOME_PHONE
);
1661 phone_disp_str
= sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC
,
1663 SIPE_BUDDY_INFO_HOME_PHONE_DISPLAY
);
1665 gchar
*label
= g_strdup_printf(_("Home %s"),
1666 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
1667 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
1671 menu
= g_list_prepend(menu
, act
);
1674 g_free(phone_disp_str
);
1677 phone
= sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC
,
1679 SIPE_BUDDY_INFO_OTHER_PHONE
);
1680 phone_disp_str
= sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC
,
1682 SIPE_BUDDY_INFO_OTHER_PHONE_DISPLAY
);
1684 gchar
*label
= g_strdup_printf(_("Other %s"),
1685 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
1686 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
1690 menu
= g_list_prepend(menu
, act
);
1693 g_free(phone_disp_str
);
1696 phone
= sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC
,
1698 SIPE_BUDDY_INFO_CUSTOM1_PHONE
);
1699 phone_disp_str
= sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC
,
1701 SIPE_BUDDY_INFO_CUSTOM1_PHONE_DISPLAY
);
1703 gchar
*label
= g_strdup_printf(_("Custom1 %s"),
1704 phone_disp_str
? phone_disp_str
: (tmp
= sip_tel_uri_denormalize(phone
)));
1705 act
= purple_menu_action_new(label
, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb
), (gpointer
) phone
, NULL
);
1709 menu
= g_list_prepend(menu
, act
);
1712 g_free(phone_disp_str
);
1715 email
= sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC
,
1717 SIPE_BUDDY_INFO_EMAIL
);
1719 act
= purple_menu_action_new(_("Send email..."),
1720 PURPLE_CALLBACK(sipe_buddy_menu_send_email_cb
),
1722 menu
= g_list_prepend(menu
, act
);
1727 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
1728 GList
*menu_access_levels
= sipe_get_access_control_menu(sipe_private
, buddy
->name
);
1730 act
= purple_menu_action_new(_("Access level"),
1732 NULL
, menu_access_levels
);
1733 menu
= g_list_prepend(menu
, act
);
1737 gr_parent
= purple_buddy_get_group(buddy
);
1738 for (g_node
= purple_blist_get_root(); g_node
; g_node
= g_node
->next
) {
1741 if (g_node
->type
!= PURPLE_BLIST_GROUP_NODE
)
1744 group
= (PurpleGroup
*)g_node
;
1745 if (group
== gr_parent
)
1748 if (purple_find_buddy_in_group(buddy
->account
, buddy
->name
, group
))
1751 act
= purple_menu_action_new(purple_group_get_name(group
),
1752 PURPLE_CALLBACK(sipe_buddy_menu_copy_to_cb
),
1754 menu_groups
= g_list_prepend(menu_groups
, act
);
1756 /* Coverity complains about RESOURCE_LEAK here - no idea how to fix it */
1757 menu_groups
= g_list_reverse(menu_groups
);
1759 act
= purple_menu_action_new(_("Copy to"),
1762 menu
= g_list_prepend(menu
, act
);
1764 menu
= g_list_reverse(menu
);
1771 sipe_ask_access_domain_cb(PurpleConnection
*gc
, PurpleRequestFields
*fields
)
1773 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
1774 const char *domain
= purple_request_fields_get_string(fields
, "access_domain");
1775 guint index
= purple_request_fields_get_choice(fields
, "container_id");
1776 sipe_ocs2007_change_access_level_for_domain(sipe_private
,
1782 sipe_ask_access_domain(struct sipe_core_private
*sipe_private
)
1784 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
1785 PurpleAccount
*account
= sip
->account
;
1786 PurpleConnection
*gc
= sip
->gc
;
1787 PurpleRequestFields
*fields
;
1788 PurpleRequestFieldGroup
*g
;
1789 PurpleRequestField
*f
;
1791 fields
= purple_request_fields_new();
1793 g
= purple_request_field_group_new(NULL
);
1794 f
= purple_request_field_string_new("access_domain", _("Domain"), "partner-company.com", FALSE
);
1795 purple_request_field_set_required(f
, TRUE
);
1796 purple_request_field_group_add_field(g
, f
);
1798 f
= purple_request_field_choice_new("container_id", _("Access level"), 0);
1799 purple_request_field_choice_add(f
, _("Personal")); /* index 0 */
1800 purple_request_field_choice_add(f
, _("Team"));
1801 purple_request_field_choice_add(f
, _("Company"));
1802 purple_request_field_choice_add(f
, _("Public"));
1803 purple_request_field_choice_add(f
, _("Blocked")); /* index 4 */
1804 purple_request_field_choice_set_default_value(f
, 3); /* index */
1805 purple_request_field_set_required(f
, TRUE
);
1806 purple_request_field_group_add_field(g
, f
);
1808 purple_request_fields_add_group(fields
, g
);
1810 purple_request_fields(gc
, _("Add new domain"),
1811 _("Add new domain"), NULL
, fields
,
1812 _("Add"), G_CALLBACK(sipe_ask_access_domain_cb
),
1814 account
, NULL
, NULL
, gc
);
1818 sipe_buddy_menu_access_level_add_domain_cb(PurpleBuddy
*buddy
)
1820 sipe_ask_access_domain(PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE
);
1824 * Workaround for missing libpurple API to release resources allocated
1825 * during blist_node_menu() callback. See also:
1827 * <http://developer.pidgin.im/ticket/12597>
1829 * We remember all memory blocks in a list and deallocate them when
1831 * - the next time we enter the callback, or
1832 * - the account is disconnected
1834 * That means that after the buddy menu has been closed we have unused
1835 * resources but at least we don't leak them anymore...
1838 sipe_blist_menu_free_containers(struct sipe_core_private
*sipe_private
)
1840 GSList
*entry
= sipe_private
->blist_menu_containers
;
1842 sipe_ocs2007_free_container(entry
->data
);
1843 entry
= entry
->next
;
1845 g_slist_free(sipe_private
->blist_menu_containers
);
1846 sipe_private
->blist_menu_containers
= NULL
;
1850 sipe_blist_menu_remember_container(struct sipe_core_private
*sipe_private
,
1851 struct sipe_container
*container
)
1853 sipe_private
->blist_menu_containers
= g_slist_prepend(sipe_private
->blist_menu_containers
,
1858 sipe_get_access_levels_menu(struct sipe_core_private
*sipe_private
,
1859 const char* member_type
,
1860 const char* member_value
,
1861 const gboolean extra_menu
)
1863 GList
*menu_access_levels
= NULL
;
1866 PurpleMenuAction
*act
;
1867 struct sipe_container
*container
;
1868 gboolean is_group_access
= FALSE
;
1869 int container_id
= sipe_ocs2007_find_access_level(sipe_private
,
1873 guint container_max
= sipe_ocs2007_containers();
1875 for (i
= 1; i
<= container_max
; i
++) {
1876 /* to put Blocked level last in menu list.
1877 * Blocked should remaim in the first place in the containers[] array.
1879 unsigned int j
= (i
== container_max
) ? 0 : i
;
1880 int container_j
= sipe_ocs2007_container_id(j
);
1881 const gchar
*acc_level_name
= sipe_ocs2007_access_level_name(container_j
);
1883 container
= sipe_ocs2007_create_container(j
,
1888 /* libpurple memory leak workaround */
1889 sipe_blist_menu_remember_container(sipe_private
, container
);
1891 /* current container/access level */
1892 if (container_j
== container_id
) {
1893 menu_name
= is_group_access
?
1894 g_strdup_printf(INDENT_MARKED_INHERITED_FMT
, acc_level_name
) :
1895 g_strdup_printf(INDENT_MARKED_FMT
, acc_level_name
);
1897 menu_name
= g_strdup_printf(INDENT_FMT
, acc_level_name
);
1900 act
= purple_menu_action_new(menu_name
,
1901 PURPLE_CALLBACK(sipe_buddy_menu_access_level_cb
),
1904 menu_access_levels
= g_list_prepend(menu_access_levels
, act
);
1907 if (extra_menu
&& (container_id
>= 0)) {
1909 act
= purple_menu_action_new(" --------------", NULL
, NULL
, NULL
);
1910 menu_access_levels
= g_list_prepend(menu_access_levels
, act
);
1912 if (!is_group_access
) {
1913 container
= sipe_ocs2007_create_container(0,
1918 /* libpurple memory leak workaround */
1919 sipe_blist_menu_remember_container(sipe_private
, container
);
1921 /* Translators: remove (clear) previously assigned access level */
1922 menu_name
= g_strdup_printf(INDENT_FMT
, _("Unspecify"));
1923 act
= purple_menu_action_new(menu_name
,
1924 PURPLE_CALLBACK(sipe_buddy_menu_access_level_cb
),
1927 menu_access_levels
= g_list_prepend(menu_access_levels
, act
);
1931 menu_access_levels
= g_list_reverse(menu_access_levels
);
1932 return menu_access_levels
;
1936 sipe_get_access_groups_menu(struct sipe_core_private
*sipe_private
)
1938 GList
*menu_access_groups
= NULL
;
1939 PurpleMenuAction
*act
;
1940 GSList
*access_domains
;
1943 act
= purple_menu_action_new(_("People in my company"),
1945 NULL
, sipe_get_access_levels_menu(sipe_private
, "sameEnterprise", NULL
, FALSE
));
1946 menu_access_groups
= g_list_prepend(menu_access_groups
, act
);
1948 /* this is original name, don't edit */
1949 act
= purple_menu_action_new(_("People in domains connected with my company"),
1951 NULL
, sipe_get_access_levels_menu(sipe_private
, "federated", NULL
, FALSE
));
1952 menu_access_groups
= g_list_prepend(menu_access_groups
, act
);
1954 act
= purple_menu_action_new(_("People in public domains"),
1956 NULL
, sipe_get_access_levels_menu(sipe_private
, "publicCloud", NULL
, TRUE
));
1957 menu_access_groups
= g_list_prepend(menu_access_groups
, act
);
1959 access_domains
= sipe_ocs2007_get_access_domains(sipe_private
);
1960 entry
= access_domains
;
1962 gchar
*domain
= entry
->data
;
1963 gchar
*menu_name
= g_strdup_printf(_("People at %s"), domain
);
1965 /* takes over ownership of entry->data (= domain) */
1966 act
= purple_menu_action_new(menu_name
,
1968 NULL
, sipe_get_access_levels_menu(sipe_private
, "domain", domain
, TRUE
));
1969 menu_access_groups
= g_list_prepend(menu_access_groups
, act
);
1972 entry
= entry
->next
;
1974 g_slist_free(access_domains
);
1977 /* People in domains connected with my company */
1978 act
= purple_menu_action_new("-------------------------------------------", NULL
, NULL
, NULL
);
1979 menu_access_groups
= g_list_prepend(menu_access_groups
, act
);
1981 act
= purple_menu_action_new(_("Add new domain..."),
1982 PURPLE_CALLBACK(sipe_buddy_menu_access_level_add_domain_cb
),
1984 menu_access_groups
= g_list_prepend(menu_access_groups
, act
);
1986 menu_access_groups
= g_list_reverse(menu_access_groups
);
1988 return menu_access_groups
;
1992 sipe_get_access_control_menu(struct sipe_core_private
*sipe_private
,
1995 GList
*menu_access_levels
= NULL
;
1996 GList
*menu_access_groups
= NULL
;
1998 PurpleMenuAction
*act
;
2000 /* libpurple memory leak workaround */
2001 sipe_blist_menu_free_containers(sipe_private
);
2003 menu_access_levels
= sipe_get_access_levels_menu(sipe_private
, "user", sipe_get_no_sip_uri(uri
), TRUE
);
2005 menu_access_groups
= sipe_get_access_groups_menu(sipe_private
);
2007 menu_name
= g_strdup_printf(INDENT_FMT
, _("Access groups"));
2008 act
= purple_menu_action_new(menu_name
,
2010 NULL
, menu_access_groups
);
2012 menu_access_levels
= g_list_append(menu_access_levels
, act
);
2014 menu_name
= g_strdup_printf(INDENT_FMT
, _("Online help..."));
2015 act
= purple_menu_action_new(menu_name
,
2016 PURPLE_CALLBACK(sipe_buddy_menu_access_level_help_cb
),
2019 menu_access_levels
= g_list_append(menu_access_levels
, act
);
2021 return menu_access_levels
;
2025 process_get_info_response(struct sipe_core_private
*sipe_private
,
2026 struct sipmsg
*msg
, struct transaction
*trans
)
2028 struct sipe_account_data
*sip
= SIPE_ACCOUNT_DATA_PRIVATE
;
2029 char *uri
= trans
->payload
->data
;
2031 PurpleNotifyUserInfo
*info
;
2032 PurpleBuddy
*pbuddy
= NULL
;
2033 struct sipe_buddy
*sbuddy
;
2034 const char *alias
= NULL
;
2035 char *device_name
= NULL
;
2036 char *server_alias
= NULL
;
2037 char *phone_number
= NULL
;
2040 char *first_name
= NULL
;
2041 char *last_name
= NULL
;
2043 if (!sip
) return FALSE
;
2045 SIPE_DEBUG_INFO("Fetching %s's user info for %s", uri
, sipe_private
->username
);
2047 pbuddy
= purple_find_buddy((PurpleAccount
*)sip
->account
, uri
);
2048 alias
= purple_buddy_get_local_alias(pbuddy
);
2050 //will query buddy UA's capabilities and send answer to log
2051 sipe_options_request(sipe_private
, uri
);
2053 sbuddy
= g_hash_table_lookup(sipe_private
->buddies
, uri
);
2055 device_name
= sbuddy
->device_name
? g_strdup(sbuddy
->device_name
) : NULL
;
2058 info
= purple_notify_user_info_new();
2060 if (msg
->response
!= 200) {
2061 SIPE_DEBUG_INFO("process_get_info_response: SERVICE response is %d", msg
->response
);
2063 sipe_xml
*searchResults
;
2064 const sipe_xml
*mrow
;
2066 SIPE_DEBUG_INFO("process_get_info_response: body:\n%s", msg
->body
? msg
->body
: "");
2067 searchResults
= sipe_xml_parse(msg
->body
, msg
->bodylen
);
2068 if (!searchResults
) {
2069 SIPE_DEBUG_INFO_NOFORMAT("process_get_info_response: no parseable searchResults");
2070 } else if ((mrow
= sipe_xml_child(searchResults
, "Body/Array/row"))) {
2072 server_alias
= g_strdup(sipe_xml_attribute(mrow
, "displayName"));
2073 email
= g_strdup(sipe_xml_attribute(mrow
, "email"));
2074 phone_number
= g_strdup(sipe_xml_attribute(mrow
, "phone"));
2076 /* For 2007 system we will take this from ContactCard -
2077 * it has cleaner tel: URIs at least
2079 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007
)) {
2080 char *tel_uri
= sip_to_tel_uri(phone_number
);
2081 /* trims its parameters, so call first */
2082 sipe_buddy_update_property(sipe_private
, uri
, SIPE_BUDDY_INFO_DISPLAY_NAME
, server_alias
);
2083 sipe_buddy_update_property(sipe_private
, uri
, SIPE_BUDDY_INFO_EMAIL
, email
);
2084 sipe_buddy_update_property(sipe_private
, uri
, SIPE_BUDDY_INFO_WORK_PHONE
, tel_uri
);
2085 sipe_buddy_update_property(sipe_private
, uri
, SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY
, phone_number
);
2089 #if PURPLE_VERSION_CHECK(3,0,0)
2090 #define PURPLE_NOTIFY_USER_INFO_ADD_PAIR purple_notify_user_info_add_pair_html
2092 #define PURPLE_NOTIFY_USER_INFO_ADD_PAIR purple_notify_user_info_add_pair
2095 if (server_alias
&& strlen(server_alias
) > 0) {
2096 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info
, _("Display name"), server_alias
);
2098 if ((value
= sipe_xml_attribute(mrow
, "title")) && strlen(value
) > 0) {
2099 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info
, _("Job title"), value
);
2101 if ((value
= sipe_xml_attribute(mrow
, "office")) && strlen(value
) > 0) {
2102 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info
, _("Office"), value
);
2104 if (phone_number
&& strlen(phone_number
) > 0) {
2105 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info
, _("Business phone"), phone_number
);
2107 if ((value
= sipe_xml_attribute(mrow
, "company")) && strlen(value
) > 0) {
2108 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info
, _("Company"), value
);
2110 if ((value
= sipe_xml_attribute(mrow
, "city")) && strlen(value
) > 0) {
2111 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info
, _("City"), value
);
2113 if ((value
= sipe_xml_attribute(mrow
, "state")) && strlen(value
) > 0) {
2114 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info
, _("State"), value
);
2116 if ((value
= sipe_xml_attribute(mrow
, "country")) && strlen(value
) > 0) {
2117 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info
, _("Country"), value
);
2119 if (email
&& strlen(email
) > 0) {
2120 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info
, _("Email address"), email
);
2124 sipe_xml_free(searchResults
);
2127 purple_notify_user_info_add_section_break(info
);
2129 if (is_empty(server_alias
)) {
2130 g_free(server_alias
);
2131 server_alias
= sipe_backend_buddy_get_server_alias(SIPE_CORE_PUBLIC
,
2134 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info
, _("Display name"), server_alias
);
2138 /* present alias if it differs from server alias */
2139 if (alias
&& !sipe_strequal(alias
, server_alias
))
2141 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info
, _("Alias"), alias
);
2144 if (is_empty(email
)) {
2146 email
= sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC
,
2148 SIPE_BUDDY_INFO_EMAIL
);
2150 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info
, _("Email address"), email
);
2154 site
= sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC
,
2156 SIPE_BUDDY_INFO_SITE
);
2158 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info
, _("Site"), site
);
2162 sipe_get_first_last_names(sipe_private
, uri
, &first_name
, &last_name
);
2163 if (first_name
&& last_name
) {
2164 char *link
= g_strconcat("http://www.linkedin.com/pub/dir/", first_name
, "/", last_name
, NULL
);
2166 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info
, _("Find on LinkedIn"), link
);
2173 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info
, _("Device"), device_name
);
2176 /* show a buddy's user info in a nice dialog box */
2177 purple_notify_userinfo(sip
->gc
, /* connection the buddy info came through */
2178 uri
, /* buddy's URI */
2180 NULL
, /* callback called when dialog closed */
2181 NULL
); /* userdata for callback */
2183 g_free(phone_number
);
2184 g_free(server_alias
);
2186 g_free(device_name
);
2192 * AD search first, LDAP based
2194 void sipe_get_info(PurpleConnection
*gc
, const char *username
)
2196 struct sipe_core_private
*sipe_private
= PURPLE_GC_TO_SIPE_CORE_PRIVATE
;
2197 char *row
= g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW
, "msRTCSIP-PrimaryUserAddress", username
);
2198 struct transaction_payload
*payload
= g_new0(struct transaction_payload
, 1);
2200 payload
->destroy
= g_free
;
2201 payload
->data
= g_strdup(username
);
2203 SIPE_DEBUG_INFO("sipe_get_info: row: %s", row
? row
: "");
2204 sip_soap_directory_search(sipe_private
,
2207 process_get_info_response
,