core cleanup: get rid of purple_notify_error()
[siplcs.git] / src / core / sipe.c
blob5fbd4087ab3e9a0147153f5f7b4a97f1114de043
1 /**
2 * @file sipe.c
4 *****************************************************************************
5 *** !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ***
6 *** ***
7 *** THIS MODULE IS DEPECRATED ***
8 *** ***
9 *** DO NOT ADD ANY NEW CODE TO THIS MODULE ***
10 *** ***
11 *** !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ***
12 *****************************************************************************
14 * pidgin-sipe
16 * Copyright (C) 2010-11 SIPE Project <http://sipe.sourceforge.net/>
17 * Copyright (C) 2010 pier11 <pier11@operamail.com>
18 * Copyright (C) 2009 Anibal Avelar <debianmx@gmail.com>
19 * Copyright (C) 2009 pier11 <pier11@operamail.com>
20 * Copyright (C) 2008 Novell, Inc., Anibal Avelar <debianmx@gmail.com>
21 * Copyright (C) 2007 Anibal Avelar <debianmx@gmail.com>
22 * Copyright (C) 2005 Thomas Butter <butter@uni-mannheim.de>
24 * ***
25 * Thanks to Google's Summer of Code Program and the helpful mentors
26 * ***
28 * Session-based SIP MESSAGE documentation:
29 * http://tools.ietf.org/html/draft-ietf-simple-im-session-00
31 * This program is free software; you can redistribute it and/or modify
32 * it under the terms of the GNU General Public License as published by
33 * the Free Software Foundation; either version 2 of the License, or
34 * (at your option) any later version.
36 * This program is distributed in the hope that it will be useful,
37 * but WITHOUT ANY WARRANTY; without even the implied warranty of
38 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
39 * GNU General Public License for more details.
41 * You should have received a copy of the GNU General Public License
42 * along with this program; if not, write to the Free Software
43 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
46 #ifdef HAVE_CONFIG_H
47 #include "config.h"
48 #endif
50 #include <time.h>
51 #include <stdlib.h>
52 #include <stdio.h>
53 #include <errno.h>
54 #include <string.h>
55 #include <unistd.h>
57 #include <glib.h>
59 #include <libintl.h>
61 #include "sipe-common.h"
63 #include "account.h"
64 #include "blist.h"
65 #include "connection.h"
66 #include "conversation.h"
67 #include "ft.h"
68 #include "notify.h"
69 #include "plugin.h"
70 #include "privacy.h"
71 #include "request.h"
72 #include "savedstatuses.h"
73 #include "version.h"
75 #include "core-depurple.h" /* Temporary for the core de-purple transition */
77 #include "http-conn.h"
78 #include "sipmsg.h"
79 #include "sip-csta.h"
80 #include "sip-soap.h"
81 #include "sip-transport.h"
82 #include "sipe-backend.h"
83 #include "sipe-buddy.h"
84 #include "sipe-cal.h"
85 #include "sipe-chat.h"
86 #include "sipe-conf.h"
87 #include "sipe-core.h"
88 #include "sipe-core-private.h"
89 #include "sipe-group.h"
90 #include "sipe-dialog.h"
91 #include "sipe-groupchat.h"
92 #include "sipe-im.h"
93 #include "sipe-nls.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 :-( */
103 #include "sipe.h"
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
134 sipe_activity type;
135 const char *token;
136 const char *desc;
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;
177 g_free(sip->status);
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;
185 g_free(sip->status);
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;
193 g_free(sip->status);
194 sip->status = g_strdup(SIPE_STATUS_ID_INVISIBLE);
197 static sipe_activity
198 sipe_get_activity_by_token(const char *token)
200 int i;
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));
218 void
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);
226 int avail;
227 gchar *self_uri;
229 if (!sbuddy) return;
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 */
237 if (!status_id) {
238 status_id = sbuddy->last_non_cal_status_id;
239 g_free(sbuddy->activity);
240 sbuddy->activity = g_strdup(sbuddy->last_non_cal_activity);
243 if (!status_id) {
244 SIPE_DEBUG_INFO("sipe_apply_calendar_status: status_id is NULL for %s, exiting.",
245 sbuddy->name ? sbuddy->name : "" );
246 return;
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);
288 g_free(self_uri);
291 void
292 sipe_core_buddy_got_status(struct sipe_core_public *sipe_public,
293 const gchar* uri,
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);
299 if (!sbuddy) return;
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);
306 } else {
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))
316 return;
318 if (account->gc) {
319 struct sipe_core_private *sipe_private = PURPLE_ACCOUNT_TO_SIPE_CORE_PRIVATE;
320 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
322 if (sip) {
323 gchar *action_name;
324 gchar *tmp;
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
332 * state if OOF note.
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]);
346 if (do_not_publish)
348 SIPE_DEBUG_INFO_NOFORMAT("sipe_set_status: publication was switched off, exiting.");
349 return;
352 g_free(sip->status);
353 sip->status = g_strdup(status_id);
355 /* hack to escape apostrof before comparison */
356 tmp = note ? sipe_utils_str_replace(note, "'", "&apos;") : NULL;
358 /* this will preserve OOF flag as well */
359 if (!sipe_strequal(tmp, sip->note)) {
360 sip->is_oof_note = FALSE;
361 g_free(sip->note);
362 sip->note = g_strdup(note);
363 sip->note_since = time(NULL);
365 g_free(tmp);
367 /* schedule 2 sec to capture idle flag */
368 action_name = g_strdup_printf("<%s>", "+set-status");
369 sipe_schedule_seconds(sipe_private,
370 action_name,
371 NULL,
372 SIPE_IDLE_SET_DELAY,
373 send_presence_status,
374 NULL);
375 g_free(action_name);
380 void
381 sipe_set_idle(PurpleConnection * gc,
382 int interval)
384 SIPE_DEBUG_INFO("sipe_set_idle: interval=%d", interval);
386 if (gc) {
387 struct sipe_core_private *sipe_private = PURPLE_GC_TO_SIPE_CORE_PRIVATE;
388 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
390 if (sip) {
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,
398 const gchar *uri)
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,
408 const gchar *uri,
409 const gchar *activity,
410 gboolean is_online)
412 if (is_online) {
413 const gchar *status_id = NULL;
414 if (activity) {
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;
422 if (!status_id) {
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);
428 } else {
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);
444 g_free(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);
450 g_free(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,
461 b->name);
462 } else {
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)
472 #ifndef _WIN32
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.
486 g_free(buddy->name);
487 #endif
488 g_free(buddy->activity);
489 g_free(buddy->meeting_subject);
490 g_free(buddy->meeting_location);
491 g_free(buddy->note);
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);
502 g_free(buddy);
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 : "");
517 if (!buddy) return;
519 b = g_hash_table_lookup(sipe_private->buddies, buddy->name);
520 if (!b) return;
522 if (group) {
523 g = sipe_group_find_by_name(sipe_private, group->name);
526 if (g) {
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);
534 g_free(action_name);
536 g_hash_table_remove(sipe_private->buddies, buddy->name);
538 if (b->name) {
539 gchar *request = g_strdup_printf("<m:URI>%s</m:URI>",
540 b->name);
541 sip_soap_request(sipe_private,
542 "deleteContact",
543 request);
544 g_free(request);
547 sipe_free_buddy(b);
548 } else {
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
561 * Examples to parse:
562 * First Last
563 * First Last - Company Name
564 * Last, First
565 * Last, First M.
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")
570 * Unusable examples:
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")
575 static void
576 sipe_get_first_last_names(struct sipe_core_private *sipe_private,
577 const char *uri,
578 char **first_name,
579 char **last_name)
581 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
582 sipe_backend_buddy p_buddy;
583 char *display_name;
584 gchar *email;
585 const char *first, *last;
586 char *tmp;
587 char **parts;
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), ".", " ");
605 g_free(tmp);
608 if (display_name) {
609 has_comma = (strstr(display_name, ",") != NULL);
610 display_name = sipe_utils_str_replace((tmp = display_name), ", ", " ");
611 g_free(tmp);
612 display_name = sipe_utils_str_replace((tmp = display_name), ",", " ");
613 g_free(tmp);
616 parts = g_strsplit(display_name, " ", 0);
618 if (!parts[0] || !parts[1]) {
619 g_free(email);
620 g_free(display_name);
621 g_strfreev(parts);
622 return;
625 if (has_comma) {
626 last = parts[0];
627 first = parts[1];
628 } else {
629 first = parts[0];
630 last = parts[1];
633 if (first_name) {
634 *first_name = g_strstrip(g_strdup(first));
637 if (last_name) {
638 *last_name = g_strstrip(g_strdup(last));
641 g_free(email);
642 g_free(display_name);
643 g_strfreev(parts);
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
653 * calendar data).
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)))
668 changed = FALSE;
671 if (purple_savedstatus_is_idleaway()) {
672 changed = FALSE;
675 if (changed) {
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);
683 if (saved_status) {
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
690 if (!saved_status) {
691 GList *tmp;
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) */
715 static gboolean
716 process_options_response(SIPE_UNUSED_PARAMETER struct sipe_core_private *sipe_private,
717 struct sipmsg *msg,
718 SIPE_UNUSED_PARAMETER struct transaction *trans)
720 gboolean ret = TRUE;
722 if (msg->response != 200) {
723 SIPE_DEBUG_INFO("process_options_response: OPTIONS response is %d", msg->response);
724 return FALSE;
727 SIPE_DEBUG_INFO("process_options_response: body:\n%s", msg->body ? msg->body : "");
729 return ret;
733 * Asks UA/proxy about its capabilities.
735 static void sipe_options_request(struct sipe_core_private *sipe_private,
736 const char *who)
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);
743 g_free(contact);
745 sip_transport_request(sipe_private,
746 "OPTIONS",
749 request,
750 NULL,
751 NULL,
752 process_options_response);
754 g_free(to);
755 g_free(request);
758 void
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,
774 int *activity,
775 int *availability)
777 int avail = 300; /* online */
778 int act = 400; /* Available */
780 if (sipe_strequal(status, SIPE_STATUS_ID_AWAY)) {
781 act = 100;
782 //} else if (sipe_strequal(status, SIPE_STATUS_ID_LUNCH)) {
783 // act = 150;
784 } else if (sipe_strequal(status, SIPE_STATUS_ID_BRB)) {
785 act = 300;
786 } else if (sipe_strequal(status, SIPE_STATUS_ID_AVAILABLE)) {
787 act = 400;
788 //} else if (sipe_strequal(status, SIPE_STATUS_ID_ON_PHONE)) {
789 // act = 500;
790 } else if (sipe_strequal(status, SIPE_STATUS_ID_BUSY) ||
791 sipe_strequal(status, SIPE_STATUS_ID_DND)) {
792 act = 600;
793 } else if (sipe_strequal(status, SIPE_STATUS_ID_INVISIBLE) ||
794 sipe_strequal(status, SIPE_STATUS_ID_OFFLINE)) {
795 avail = 0; /* offline */
796 act = 100;
797 } else {
798 act = 400; /* Available */
801 if (activity) *activity = act;
802 if (availability) *availability = avail;
806 * [MS-SIP] 2.2.1
808 * @param activity 2005 aggregated activity. Ex.: 600
809 * @param availablity 2005 aggregated availablity. Ex.: 300
811 const gchar *
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;
841 } else {
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);
853 return status_id;
857 * [MS-PRES] Table 3: Conversion of legacyInterop elements and attributes to MSRTC elements and attributes.
859 const gchar *
860 sipe_get_status_by_availability(int avail,
861 gchar **activity_desc)
863 const char *status;
864 const char *act = NULL;
866 if (avail < 3000) {
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;
886 } else {
887 status = SIPE_STATUS_ID_OFFLINE;
890 if (activity_desc && act) {
891 g_free(*activity_desc);
892 *activity_desc = g_strdup(act);
895 return status;
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)
907 int availability;
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)) {
919 availability = 9500;
920 activity = SIPE_ACTIVITY_DND;
921 } else if (sipe_strequal(sipe_status_id, SIPE_STATUS_ID_BUSY)) {
922 availability = 6500;
923 if (!activity_token || !(*activity_token)) {
924 activity = SIPE_ACTIVITY_BUSY;
926 } else if (sipe_strequal(sipe_status_id, SIPE_STATUS_ID_AVAILABLE)) {
927 availability = 3500;
928 activity = SIPE_ACTIVITY_ONLINE;
929 } else if (sipe_strequal(sipe_status_id, SIPE_STATUS_ID_UNKNOWN)) {
930 availability = 0;
931 } else {
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));
940 return availability;
944 * Whether user manually changed status or
945 * it was changed automatically due to user
946 * became inactive/active again
948 gboolean
949 sipe_is_user_state(struct sipe_core_private *sipe_private)
951 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
952 gboolean res;
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");
961 return res;
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);
976 if (!status) return;
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;
990 sip->gc = gc;
991 sip->account = purple_connection_get_account(gc);
994 static void
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;
1010 while (entry) {
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;
1025 if (sip->regcallid)
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;
1033 if (sip->cal) {
1034 sipe_cal_calendar_free(sip->cal);
1036 sip->cal = NULL;
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 */
1050 return(TRUE);
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);
1064 if (conv == NULL)
1065 conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, acct, id);
1066 purple_conversation_present(conv);
1067 g_free(id);
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,
1079 struct sipmsg *msg,
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;
1089 gchar *secondary;
1091 /* valid response? */
1092 if (msg->response != 200) {
1093 SIPE_DEBUG_ERROR("process_search_contact_response: request failed (%d)",
1094 msg->response);
1095 sipe_backend_notify_error(SIPE_CORE_PUBLIC,
1096 _("Contact search failed"),
1097 NULL);
1098 return(FALSE);
1101 SIPE_DEBUG_INFO("process_search_contact_response: body:\n%s", msg->body ? msg->body : "");
1103 /* valid XML? */
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"),
1109 NULL);
1110 return FALSE;
1113 /* any matches? */
1114 mrow = sipe_xml_child(searchResults, "Body/Array/row");
1115 if (!mrow) {
1116 SIPE_DEBUG_ERROR_NOFORMAT("process_search_contact_response: no matches");
1117 sipe_backend_notify_error(SIPE_CORE_PUBLIC,
1118 _("No contacts found"),
1119 NULL);
1121 sipe_xml_free(searchResults);
1122 return(FALSE);
1125 /* OK, we found something - show the results to the user */
1126 results = purple_notify_searchresults_new();
1127 if (!results) {
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"),
1131 NULL);
1133 sipe_xml_free(searchResults);
1134 return FALSE;
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)) {
1153 GList *row = NULL;
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);
1165 match_count++;
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);
1171 g_free(data);
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);
1184 g_free(secondary);
1185 sipe_xml_free(searchResults);
1186 return TRUE;
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);
1195 unsigned i = 0;
1197 if (!attrs) return;
1199 do {
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);
1208 attrs[i] = NULL;
1210 if (i > 0) {
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,
1215 100,
1216 query,
1217 process_search_contact_response,
1218 NULL);
1219 g_free(query);
1222 g_strfreev(attrs);
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);
1230 else
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,
1249 const gchar *name,
1250 const gchar *status_name,
1251 gboolean is_online)
1253 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
1254 gchar *note = NULL;
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)); \
1266 sbi->label = (l); \
1267 sbi->text = (t); \
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);
1275 if (sbuddy) {
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);
1293 //Layout
1294 if (is_online)
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);
1304 g_free(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);
1315 if (note)
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));
1321 if (access_text) {
1322 SIPE_ADD_BUDDY_INFO(_("Access level"), access_text);
1323 g_free(access_text);
1326 return(info);
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;
1333 PurpleBuddy *clone;
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,
1338 buddy->name,
1339 buddy->alias,
1340 group->name);
1342 server_alias = sipe_backend_buddy_get_server_alias(SIPE_CORE_PUBLIC,
1343 buddy);
1344 if (server_alias) {
1345 sipe_backend_buddy_set_server_alias(SIPE_CORE_PUBLIC,
1346 clone,
1347 server_alias);
1348 g_free(server_alias);
1351 email = sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC,
1352 buddy,
1353 SIPE_BUDDY_INFO_EMAIL);
1354 if (email) {
1355 sipe_backend_buddy_set_string(SIPE_CORE_PUBLIC,
1356 clone,
1357 SIPE_BUDDY_INFO_EMAIL,
1358 email);
1359 g_free(email);
1362 purple_presence_set_status_active(purple_buddy_get_presence(clone),
1363 purple_status_get_id(status),
1364 TRUE);
1365 /* for UI to update */
1366 sipe_backend_buddy_set_status(SIPE_CORE_PUBLIC,
1367 buddy->name,
1368 purple_status_get_id(status));
1370 return clone;
1373 static void
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);
1388 if (!b){
1389 b = purple_blist_add_buddy_clone(group, buddy);
1392 sipe_add_buddy(gc, b, group);
1395 static void
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,
1413 NULL,
1414 TRUE,
1415 self);
1416 session->chat_session->backend = sipe_backend_chat_create(SIPE_CORE_PUBLIC,
1417 session->chat_session,
1418 session->chat_session->title,
1419 self);
1420 g_free(self);
1422 sipe_im_invite(sipe_private, session, buddy->name, NULL, NULL, NULL, FALSE);
1427 * For 2007+ conference only.
1429 static void
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.
1447 static void
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);
1462 static void
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);
1474 static void
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);
1480 if (phone) {
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);
1486 g_free(tel_uri);
1490 static void
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"));
1498 static void
1499 sipe_buddy_menu_send_email_cb(PurpleBuddy *buddy)
1501 struct sipe_core_private *sipe_private = PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE;
1502 gchar *email;
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,
1506 buddy,
1507 SIPE_BUDDY_INFO_EMAIL);
1508 if (email)
1510 char *command_line = g_strdup_printf(
1511 #ifdef _WIN32
1512 "cmd /c start"
1513 #else
1514 "xdg-email"
1515 #endif
1516 " mailto:%s", email);
1517 SIPE_DEBUG_INFO("sipe_buddy_menu_send_email_cb: going to call email client: %s", command_line);
1519 g_free(email);
1520 g_spawn_command_line_async(command_line, NULL);
1521 g_free(command_line);
1523 else
1525 SIPE_DEBUG_INFO("sipe_buddy_menu_send_email_cb: no email address stored for buddy=%s", buddy->name);
1529 static void
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,
1535 container);
1538 static GList *
1539 sipe_get_access_control_menu(struct sipe_core_private *sipe_private,
1540 const char* uri);
1543 * A menu which appear when right-clicking on buddy in contact list.
1545 GList *
1546 sipe_buddy_menu(PurpleBuddy *buddy)
1548 PurpleBlistNode *g_node;
1549 PurpleGroup *gr_parent;
1550 PurpleMenuAction *act;
1551 GList *menu = NULL;
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;
1555 gchar *email;
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);
1568 if (is_conf
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);
1577 g_free(label);
1578 menu = g_list_prepend(menu, act);
1581 if (is_conf
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);
1589 g_free(label);
1590 menu = g_list_prepend(menu, act);
1593 else
1595 if (!is_conf
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);
1603 g_free(label);
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),
1612 NULL, NULL);
1613 menu = g_list_prepend(menu, act);
1615 if (sip->csta && !sip->csta->line_status) {
1616 gchar *phone;
1617 gchar *phone_disp_str;
1618 gchar *tmp = NULL;
1619 /* work phone */
1620 phone = sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC,
1621 buddy,
1622 SIPE_BUDDY_INFO_WORK_PHONE);
1623 phone_disp_str = sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC,
1624 buddy,
1625 SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY);
1626 if (phone) {
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);
1630 g_free(tmp);
1631 tmp = NULL;
1632 g_free(label);
1633 menu = g_list_prepend(menu, act);
1634 g_free(phone);
1636 g_free(phone_disp_str);
1638 /* mobile phone */
1639 phone = sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC,
1640 buddy,
1641 SIPE_BUDDY_INFO_MOBILE_PHONE);
1642 phone_disp_str = sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC,
1643 buddy,
1644 SIPE_BUDDY_INFO_MOBILE_PHONE_DISPLAY);
1645 if (phone) {
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);
1649 g_free(tmp);
1650 tmp = NULL;
1651 g_free(label);
1652 menu = g_list_prepend(menu, act);
1653 g_free(phone);
1655 g_free(phone_disp_str);
1657 /* home phone */
1658 phone = sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC,
1659 buddy,
1660 SIPE_BUDDY_INFO_HOME_PHONE);
1661 phone_disp_str = sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC,
1662 buddy,
1663 SIPE_BUDDY_INFO_HOME_PHONE_DISPLAY);
1664 if (phone) {
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);
1668 g_free(tmp);
1669 tmp = NULL;
1670 g_free(label);
1671 menu = g_list_prepend(menu, act);
1672 g_free(phone);
1674 g_free(phone_disp_str);
1676 /* other phone */
1677 phone = sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC,
1678 buddy,
1679 SIPE_BUDDY_INFO_OTHER_PHONE);
1680 phone_disp_str = sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC,
1681 buddy,
1682 SIPE_BUDDY_INFO_OTHER_PHONE_DISPLAY);
1683 if (phone) {
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);
1687 g_free(tmp);
1688 tmp = NULL;
1689 g_free(label);
1690 menu = g_list_prepend(menu, act);
1691 g_free(phone);
1693 g_free(phone_disp_str);
1695 /* custom1 phone */
1696 phone = sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC,
1697 buddy,
1698 SIPE_BUDDY_INFO_CUSTOM1_PHONE);
1699 phone_disp_str = sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC,
1700 buddy,
1701 SIPE_BUDDY_INFO_CUSTOM1_PHONE_DISPLAY);
1702 if (phone) {
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);
1706 g_free(tmp);
1707 tmp = NULL;
1708 g_free(label);
1709 menu = g_list_prepend(menu, act);
1710 g_free(phone);
1712 g_free(phone_disp_str);
1715 email = sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC,
1716 buddy,
1717 SIPE_BUDDY_INFO_EMAIL);
1718 if (email) {
1719 act = purple_menu_action_new(_("Send email..."),
1720 PURPLE_CALLBACK(sipe_buddy_menu_send_email_cb),
1721 NULL, NULL);
1722 menu = g_list_prepend(menu, act);
1723 g_free(email);
1726 /* Access Level */
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"),
1731 NULL,
1732 NULL, menu_access_levels);
1733 menu = g_list_prepend(menu, act);
1736 /* Copy to */
1737 gr_parent = purple_buddy_get_group(buddy);
1738 for (g_node = purple_blist_get_root(); g_node; g_node = g_node->next) {
1739 PurpleGroup *group;
1741 if (g_node->type != PURPLE_BLIST_GROUP_NODE)
1742 continue;
1744 group = (PurpleGroup *)g_node;
1745 if (group == gr_parent)
1746 continue;
1748 if (purple_find_buddy_in_group(buddy->account, buddy->name, group))
1749 continue;
1751 act = purple_menu_action_new(purple_group_get_name(group),
1752 PURPLE_CALLBACK(sipe_buddy_menu_copy_to_cb),
1753 group->name, NULL);
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"),
1760 NULL,
1761 NULL, menu_groups);
1762 menu = g_list_prepend(menu, act);
1764 menu = g_list_reverse(menu);
1766 g_free(self);
1767 return menu;
1770 static void
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,
1777 domain,
1778 index);
1781 static void
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),
1813 _("Cancel"), NULL,
1814 account, NULL, NULL, gc);
1817 static void
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...
1837 static void
1838 sipe_blist_menu_free_containers(struct sipe_core_private *sipe_private)
1840 GSList *entry = sipe_private->blist_menu_containers;
1841 while (entry) {
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;
1849 static void
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,
1854 container);
1857 static GList *
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;
1864 unsigned int i;
1865 char *menu_name;
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,
1870 member_type,
1871 member_value,
1872 &is_group_access);
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,
1884 member_type,
1885 member_value,
1886 FALSE);
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);
1896 } else {
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),
1902 container, NULL);
1903 g_free(menu_name);
1904 menu_access_levels = g_list_prepend(menu_access_levels, act);
1907 if (extra_menu && (container_id >= 0)) {
1908 /* separator */
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,
1914 member_type,
1915 member_value,
1916 TRUE);
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),
1925 container, NULL);
1926 g_free(menu_name);
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;
1935 static GList *
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;
1941 GSList *entry;
1943 act = purple_menu_action_new(_("People in my company"),
1944 NULL,
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"),
1950 NULL,
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"),
1955 NULL,
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;
1961 while (entry) {
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,
1967 NULL,
1968 NULL, sipe_get_access_levels_menu(sipe_private, "domain", domain, TRUE));
1969 menu_access_groups = g_list_prepend(menu_access_groups, act);
1970 g_free(menu_name);
1972 entry = entry->next;
1974 g_slist_free(access_domains);
1976 /* separator */
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),
1983 NULL, NULL);
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;
1991 static GList *
1992 sipe_get_access_control_menu(struct sipe_core_private *sipe_private,
1993 const char* uri)
1995 GList *menu_access_levels = NULL;
1996 GList *menu_access_groups = NULL;
1997 char *menu_name;
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,
2009 NULL,
2010 NULL, menu_access_groups);
2011 g_free(menu_name);
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),
2017 NULL, NULL);
2018 g_free(menu_name);
2019 menu_access_levels = g_list_append(menu_access_levels, act);
2021 return menu_access_levels;
2024 static gboolean
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;
2038 char *email = NULL;
2039 char *site;
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);
2054 if (sbuddy) {
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);
2062 } else {
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"))) {
2071 const char *value;
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);
2086 g_free(tel_uri);
2089 #if PURPLE_VERSION_CHECK(3,0,0)
2090 #define PURPLE_NOTIFY_USER_INFO_ADD_PAIR purple_notify_user_info_add_pair_html
2091 #else
2092 #define PURPLE_NOTIFY_USER_INFO_ADD_PAIR purple_notify_user_info_add_pair
2093 #endif
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,
2132 pbuddy);
2133 if (server_alias) {
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)) {
2145 g_free(email);
2146 email = sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC,
2147 pbuddy,
2148 SIPE_BUDDY_INFO_EMAIL);
2149 if (email) {
2150 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info, _("Email address"), email);
2154 site = sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC,
2155 pbuddy,
2156 SIPE_BUDDY_INFO_SITE);
2157 if (site) {
2158 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info, _("Site"), site);
2159 g_free(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);
2167 g_free(link);
2169 g_free(first_name);
2170 g_free(last_name);
2172 if (device_name) {
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 */
2179 info, /* body */
2180 NULL, /* callback called when dialog closed */
2181 NULL); /* userdata for callback */
2183 g_free(phone_number);
2184 g_free(server_alias);
2185 g_free(email);
2186 g_free(device_name);
2188 return TRUE;
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,
2206 row,
2207 process_get_info_response,
2208 payload);
2209 g_free(row);
2213 Local Variables:
2214 mode: c
2215 c-file-style: "bsd"
2216 indent-tabs-mode: t
2217 tab-width: 8
2218 End: