core cleanup: move out ocs2005 code
[siplcs.git] / src / core / sipe.c
blob3cf71d2ed1fd9564ddf424fce0bd8622c582df3c
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);
555 /** Property names to store in blist.xml */
556 #define ALIAS_PROP "alias"
557 #define EMAIL_PROP "email"
558 #define PHONE_PROP "phone"
559 #define PHONE_DISPLAY_PROP "phone-display"
560 #define PHONE_MOBILE_PROP "phone-mobile"
561 #define PHONE_MOBILE_DISPLAY_PROP "phone-mobile-display"
562 #define PHONE_HOME_PROP "phone-home"
563 #define PHONE_HOME_DISPLAY_PROP "phone-home-display"
564 #define PHONE_OTHER_PROP "phone-other"
565 #define PHONE_OTHER_DISPLAY_PROP "phone-other-display"
566 #define PHONE_CUSTOM1_PROP "phone-custom1"
567 #define PHONE_CUSTOM1_DISPLAY_PROP "phone-custom1-display"
568 #define SITE_PROP "site"
569 #define COMPANY_PROP "company"
570 #define DEPARTMENT_PROP "department"
571 #define TITLE_PROP "title"
572 #define OFFICE_PROP "office"
573 /** implies work address */
574 #define ADDRESS_STREET_PROP "address-street"
575 #define ADDRESS_CITY_PROP "address-city"
576 #define ADDRESS_STATE_PROP "address-state"
577 #define ADDRESS_ZIPCODE_PROP "address-zipcode"
578 #define ADDRESS_COUNTRYCODE_PROP "address-country-code"
581 * Tries to figure out user first and last name
582 * based on Display Name and email properties.
584 * Allocates memory - must be g_free()'d
586 * Examples to parse:
587 * First Last
588 * First Last - Company Name
589 * Last, First
590 * Last, First M.
591 * Last, First (C)(STP) (Company)
592 * first.last@company.com (preprocessed as "first last")
593 * first.last.company.com@reuters.net (preprocessed as "first last company com")
595 * Unusable examples:
596 * user@company.com (preprocessed as "user")
597 * first.m.last@company.com (preprocessed as "first m last")
598 * user.company.com@reuters.net (preprocessed as "user company com")
600 static void
601 sipe_get_first_last_names(struct sipe_core_private *sipe_private,
602 const char *uri,
603 char **first_name,
604 char **last_name)
606 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
607 sipe_backend_buddy p_buddy;
608 char *display_name;
609 gchar *email;
610 const char *first, *last;
611 char *tmp;
612 char **parts;
613 gboolean has_comma = FALSE;
615 if (!sip || !uri) return;
617 p_buddy = sipe_backend_buddy_find(SIPE_CORE_PUBLIC, uri, NULL);
619 if (!p_buddy) return;
621 display_name = sipe_backend_buddy_get_alias(SIPE_CORE_PUBLIC, p_buddy);
622 email = sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC, p_buddy, SIPE_BUDDY_INFO_EMAIL);
624 if (!display_name && !email) return;
626 /* if no display name, make "first last anything_else" out of email */
627 if (email && !display_name) {
628 display_name = g_strndup(email, strstr(email, "@") - email);
629 display_name = sipe_utils_str_replace((tmp = display_name), ".", " ");
630 g_free(tmp);
633 if (display_name) {
634 has_comma = (strstr(display_name, ",") != NULL);
635 display_name = sipe_utils_str_replace((tmp = display_name), ", ", " ");
636 g_free(tmp);
637 display_name = sipe_utils_str_replace((tmp = display_name), ",", " ");
638 g_free(tmp);
641 parts = g_strsplit(display_name, " ", 0);
643 if (!parts[0] || !parts[1]) {
644 g_free(email);
645 g_free(display_name);
646 g_strfreev(parts);
647 return;
650 if (has_comma) {
651 last = parts[0];
652 first = parts[1];
653 } else {
654 first = parts[0];
655 last = parts[1];
658 if (first_name) {
659 *first_name = g_strstrip(g_strdup(first));
662 if (last_name) {
663 *last_name = g_strstrip(g_strdup(last));
666 g_free(email);
667 g_free(display_name);
668 g_strfreev(parts);
672 * Update user information
674 * @param uri buddy SIP URI with 'sip:' prefix whose info we want to change.
675 * @param property_name
676 * @param property_value may be modified to strip white space
678 void sipe_update_user_info(struct sipe_core_private *sipe_private,
679 const char *uri,
680 sipe_buddy_info_fields propkey,
681 char *property_value)
683 GSList *buddies, *entry;
685 if (property_value)
686 property_value = g_strstrip(property_value);
688 entry = buddies = sipe_backend_buddy_find_all(SIPE_CORE_PUBLIC, uri, NULL); /* all buddies in different groups */
689 while (entry) {
690 gchar *prop_str;
691 gchar *server_alias;
692 gchar *alias;
693 sipe_backend_buddy p_buddy = entry->data;
695 /* for Display Name */
696 if (propkey == SIPE_BUDDY_INFO_DISPLAY_NAME) {
697 alias = sipe_backend_buddy_get_alias(SIPE_CORE_PUBLIC, p_buddy);
698 if (property_value && sipe_is_bad_alias(uri, alias)) {
699 SIPE_DEBUG_INFO("Replacing alias for %s with %s", uri, property_value);
700 sipe_backend_buddy_set_alias(SIPE_CORE_PUBLIC, p_buddy, property_value);
702 g_free(alias);
704 server_alias = sipe_backend_buddy_get_server_alias(SIPE_CORE_PUBLIC, p_buddy);
705 if (!is_empty(property_value) &&
706 (!sipe_strequal(property_value, server_alias) || is_empty(server_alias)) )
708 SIPE_DEBUG_INFO("Replacing service alias for %s with %s", uri, property_value);
709 sipe_backend_buddy_set_server_alias(SIPE_CORE_PUBLIC, p_buddy, property_value);
711 g_free(server_alias);
713 /* for other properties */
714 else {
715 if (!is_empty(property_value)) {
716 prop_str = sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC, p_buddy, propkey);
717 if (!prop_str || !sipe_strcase_equal(prop_str, property_value)) {
718 sipe_backend_buddy_set_string(SIPE_CORE_PUBLIC, p_buddy, propkey, property_value);
720 g_free(prop_str);
724 entry = entry->next;
726 g_slist_free(buddies);
730 * This method motivates Purple's Host (e.g. Pidgin) to update its UI
731 * by using standard Purple's means of signals and saved statuses.
733 * Thus all UI elements get updated: Status Button with Note, docklet.
734 * This is ablolutely important as both our status and note can come
735 * inbound (roaming) or be updated programmatically (e.g. based on our
736 * calendar data).
738 void sipe_backend_account_status_and_note(struct sipe_core_private *sipe_private,
739 const gchar *status_id)
741 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
742 PurpleAccount *account = sip->account;
743 PurpleStatus *status = purple_account_get_active_status(account);
744 const gchar *message = sip->note;
745 time_t *do_not_publish = sip->do_not_publish;
746 gboolean changed = TRUE;
748 if (g_str_equal(status_id, purple_status_get_id(status)) &&
749 sipe_strequal(message, purple_status_get_attr_string(status, SIPE_STATUS_ATTR_ID_MESSAGE)))
751 changed = FALSE;
754 if (purple_savedstatus_is_idleaway()) {
755 changed = FALSE;
758 if (changed) {
759 PurpleSavedStatus *saved_status;
760 const PurpleStatusType *acct_status_type =
761 purple_status_type_find_with_id(account->status_types, status_id);
762 PurpleStatusPrimitive primitive = purple_status_type_get_primitive(acct_status_type);
763 sipe_activity activity = sipe_get_activity_by_token(status_id);
765 saved_status = purple_savedstatus_find_transient_by_type_and_message(primitive, message);
766 if (saved_status) {
767 purple_savedstatus_set_substatus(saved_status, account, acct_status_type, message);
770 /* If this type+message is unique then create a new transient saved status
771 * Ref: gtkstatusbox.c
773 if (!saved_status) {
774 GList *tmp;
775 GList *active_accts = purple_accounts_get_all_active();
777 saved_status = purple_savedstatus_new(NULL, primitive);
778 purple_savedstatus_set_message(saved_status, message);
780 for (tmp = active_accts; tmp != NULL; tmp = tmp->next) {
781 purple_savedstatus_set_substatus(saved_status,
782 (PurpleAccount *)tmp->data, acct_status_type, message);
784 g_list_free(active_accts);
787 do_not_publish[activity] = time(NULL);
788 SIPE_DEBUG_INFO("sipe_set_purple_account_status_and_note: do_not_publish[%s]=%d [now]",
789 status_id, (int)do_not_publish[activity]);
791 /* Set the status for each account */
792 purple_savedstatus_activate(saved_status);
796 /* IM Session (INVITE and MESSAGE methods) */
798 static gboolean
799 process_options_response(SIPE_UNUSED_PARAMETER struct sipe_core_private *sipe_private,
800 struct sipmsg *msg,
801 SIPE_UNUSED_PARAMETER struct transaction *trans)
803 gboolean ret = TRUE;
805 if (msg->response != 200) {
806 SIPE_DEBUG_INFO("process_options_response: OPTIONS response is %d", msg->response);
807 return FALSE;
810 SIPE_DEBUG_INFO("process_options_response: body:\n%s", msg->body ? msg->body : "");
812 return ret;
816 * Asks UA/proxy about its capabilities.
818 static void sipe_options_request(struct sipe_core_private *sipe_private,
819 const char *who)
821 gchar *to = sip_uri(who);
822 gchar *contact = get_contact(sipe_private);
823 gchar *request = g_strdup_printf(
824 "Accept: application/sdp\r\n"
825 "Contact: %s\r\n", contact);
826 g_free(contact);
828 sip_transport_request(sipe_private,
829 "OPTIONS",
832 request,
833 NULL,
834 NULL,
835 process_options_response);
837 g_free(to);
838 g_free(request);
841 void
842 sipe_convo_closed(PurpleConnection * gc, const char *who)
844 struct sipe_core_private *sipe_private = PURPLE_GC_TO_SIPE_CORE_PRIVATE;
846 SIPE_DEBUG_INFO("conversation with %s closed", who);
847 sipe_session_close(sipe_private,
848 sipe_session_find_im(sipe_private, who));
852 * Returns 2005-style activity and Availability.
854 * @param status Sipe statis id.
856 void sipe_get_act_avail_by_status_2005(const char *status,
857 int *activity,
858 int *availability)
860 int avail = 300; /* online */
861 int act = 400; /* Available */
863 if (sipe_strequal(status, SIPE_STATUS_ID_AWAY)) {
864 act = 100;
865 //} else if (sipe_strequal(status, SIPE_STATUS_ID_LUNCH)) {
866 // act = 150;
867 } else if (sipe_strequal(status, SIPE_STATUS_ID_BRB)) {
868 act = 300;
869 } else if (sipe_strequal(status, SIPE_STATUS_ID_AVAILABLE)) {
870 act = 400;
871 //} else if (sipe_strequal(status, SIPE_STATUS_ID_ON_PHONE)) {
872 // act = 500;
873 } else if (sipe_strequal(status, SIPE_STATUS_ID_BUSY) ||
874 sipe_strequal(status, SIPE_STATUS_ID_DND)) {
875 act = 600;
876 } else if (sipe_strequal(status, SIPE_STATUS_ID_INVISIBLE) ||
877 sipe_strequal(status, SIPE_STATUS_ID_OFFLINE)) {
878 avail = 0; /* offline */
879 act = 100;
880 } else {
881 act = 400; /* Available */
884 if (activity) *activity = act;
885 if (availability) *availability = avail;
889 * [MS-SIP] 2.2.1
891 * @param activity 2005 aggregated activity. Ex.: 600
892 * @param availablity 2005 aggregated availablity. Ex.: 300
894 const gchar *
895 sipe_get_status_by_act_avail_2005(const int activity,
896 const int availablity,
897 char **activity_desc)
899 const char *status_id = NULL;
900 const char *act = NULL;
902 if (activity < 150) {
903 status_id = SIPE_STATUS_ID_AWAY;
904 } else if (activity < 200) {
905 //status_id = SIPE_STATUS_ID_LUNCH;
906 status_id = SIPE_STATUS_ID_AWAY;
907 act = sipe_activity_description(SIPE_ACTIVITY_LUNCH);
908 } else if (activity < 300) {
909 //status_id = SIPE_STATUS_ID_IDLE;
910 status_id = SIPE_STATUS_ID_AWAY;
911 act = sipe_activity_description(SIPE_ACTIVITY_INACTIVE);
912 } else if (activity < 400) {
913 status_id = SIPE_STATUS_ID_BRB;
914 } else if (activity < 500) {
915 status_id = SIPE_STATUS_ID_AVAILABLE;
916 } else if (activity < 600) {
917 //status_id = SIPE_STATUS_ID_ON_PHONE;
918 status_id = SIPE_STATUS_ID_BUSY;
919 act = sipe_activity_description(SIPE_ACTIVITY_ON_PHONE);
920 } else if (activity < 700) {
921 status_id = SIPE_STATUS_ID_BUSY;
922 } else if (activity < 800) {
923 status_id = SIPE_STATUS_ID_AWAY;
924 } else {
925 status_id = SIPE_STATUS_ID_AVAILABLE;
928 if (availablity < 100)
929 status_id = SIPE_STATUS_ID_OFFLINE;
931 if (activity_desc && act) {
932 g_free(*activity_desc);
933 *activity_desc = g_strdup(act);
936 return status_id;
940 * [MS-PRES] Table 3: Conversion of legacyInterop elements and attributes to MSRTC elements and attributes.
942 const gchar *
943 sipe_get_status_by_availability(int avail,
944 gchar **activity_desc)
946 const char *status;
947 const char *act = NULL;
949 if (avail < 3000) {
950 status = SIPE_STATUS_ID_OFFLINE;
951 } else if (avail < 4500) {
952 status = SIPE_STATUS_ID_AVAILABLE;
953 } else if (avail < 6000) {
954 //status = SIPE_STATUS_ID_IDLE;
955 status = SIPE_STATUS_ID_AVAILABLE;
956 act = sipe_activity_description(SIPE_ACTIVITY_INACTIVE);
957 } else if (avail < 7500) {
958 status = SIPE_STATUS_ID_BUSY;
959 } else if (avail < 9000) {
960 //status = SIPE_STATUS_ID_BUSYIDLE;
961 status = SIPE_STATUS_ID_BUSY;
962 act = sipe_activity_description(SIPE_ACTIVITY_BUSYIDLE);
963 } else if (avail < 12000) {
964 status = SIPE_STATUS_ID_DND;
965 } else if (avail < 15000) {
966 status = SIPE_STATUS_ID_BRB;
967 } else if (avail < 18000) {
968 status = SIPE_STATUS_ID_AWAY;
969 } else {
970 status = SIPE_STATUS_ID_OFFLINE;
973 if (activity_desc && act) {
974 g_free(*activity_desc);
975 *activity_desc = g_strdup(act);
978 return status;
982 * Returns 2007-style availability value
984 * @param sipe_status_id (in)
985 * @param activity_token (out) Must be g_free()'d after use if consumed.
988 sipe_get_availability_by_status(const char* sipe_status_id, char** activity_token)
990 int availability;
991 sipe_activity activity = SIPE_ACTIVITY_UNSET;
993 if (sipe_strequal(sipe_status_id, SIPE_STATUS_ID_AWAY)) {
994 availability = 15500;
995 if (!activity_token || !(*activity_token)) {
996 activity = SIPE_ACTIVITY_AWAY;
998 } else if (sipe_strequal(sipe_status_id, SIPE_STATUS_ID_BRB)) {
999 availability = 12500;
1000 activity = SIPE_ACTIVITY_BRB;
1001 } else if (sipe_strequal(sipe_status_id, SIPE_STATUS_ID_DND)) {
1002 availability = 9500;
1003 activity = SIPE_ACTIVITY_DND;
1004 } else if (sipe_strequal(sipe_status_id, SIPE_STATUS_ID_BUSY)) {
1005 availability = 6500;
1006 if (!activity_token || !(*activity_token)) {
1007 activity = SIPE_ACTIVITY_BUSY;
1009 } else if (sipe_strequal(sipe_status_id, SIPE_STATUS_ID_AVAILABLE)) {
1010 availability = 3500;
1011 activity = SIPE_ACTIVITY_ONLINE;
1012 } else if (sipe_strequal(sipe_status_id, SIPE_STATUS_ID_UNKNOWN)) {
1013 availability = 0;
1014 } else {
1015 // Offline or invisible
1016 availability = 18500;
1017 activity = SIPE_ACTIVITY_OFFLINE;
1020 if (activity_token) {
1021 *activity_token = g_strdup(sipe_activity_to_token(activity));
1023 return availability;
1027 * Whether user manually changed status or
1028 * it was changed automatically due to user
1029 * became inactive/active again
1031 gboolean
1032 sipe_is_user_state(struct sipe_core_private *sipe_private)
1034 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
1035 gboolean res;
1036 time_t now = time(NULL);
1038 SIPE_DEBUG_INFO("sipe_is_user_state: sip->idle_switch : %s", asctime(localtime(&(sip->idle_switch))));
1039 SIPE_DEBUG_INFO("sipe_is_user_state: now : %s", asctime(localtime(&now)));
1041 res = ((now - SIPE_IDLE_SET_DELAY * 2) >= sip->idle_switch);
1043 SIPE_DEBUG_INFO("sipe_is_user_state: res = %s", res ? "USER" : "MACHINE");
1044 return res;
1047 gboolean sipe_is_user_available(struct sipe_core_private *sipe_private)
1049 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
1050 return(sipe_strequal(sip->status, SIPE_STATUS_ID_AVAILABLE));
1053 void send_presence_status(struct sipe_core_private *sipe_private,
1054 SIPE_UNUSED_PARAMETER gpointer unused)
1056 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
1057 PurpleStatus * status = purple_account_get_active_status(sip->account);
1059 if (!status) return;
1061 SIPE_DEBUG_INFO("send_presence_status: status: %s (%s)",
1062 purple_status_get_id(status) ? purple_status_get_id(status) : "",
1063 sipe_is_user_state(sipe_private) ? "USER" : "MACHINE");
1065 sipe_cal_presence_publish(sipe_private, FALSE);
1068 /* temporary function */
1069 void sipe_purple_setup(struct sipe_core_public *sipe_public,
1070 PurpleConnection *gc)
1072 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA;
1073 sip->gc = gc;
1074 sip->account = purple_connection_get_account(gc);
1077 static void
1078 sipe_blist_menu_free_containers(struct sipe_core_private *sipe_private);
1080 void sipe_connection_cleanup(struct sipe_core_private *sipe_private)
1082 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
1084 g_free(sipe_private->epid);
1085 sipe_private->epid = NULL;
1087 sip_transport_disconnect(sipe_private);
1089 sipe_schedule_cancel_all(sipe_private);
1091 if (sip->allow_events) {
1092 GSList *entry = sip->allow_events;
1093 while (entry) {
1094 g_free(entry->data);
1095 entry = entry->next;
1098 g_slist_free(sip->allow_events);
1100 sipe_ocs2007_free(sipe_private);
1102 /* libpurple memory leak workaround */
1103 sipe_blist_menu_free_containers(sipe_private);
1105 if (sipe_private->contact)
1106 g_free(sipe_private->contact);
1107 sipe_private->contact = NULL;
1108 if (sip->regcallid)
1109 g_free(sip->regcallid);
1110 sip->regcallid = NULL;
1112 if (sipe_private->focus_factory_uri)
1113 g_free(sipe_private->focus_factory_uri);
1114 sipe_private->focus_factory_uri = NULL;
1116 if (sip->cal) {
1117 sipe_cal_calendar_free(sip->cal);
1119 sip->cal = NULL;
1121 sipe_groupchat_free(sipe_private);
1125 * A callback for g_hash_table_foreach_remove
1127 static gboolean sipe_buddy_remove(SIPE_UNUSED_PARAMETER gpointer key, gpointer buddy,
1128 SIPE_UNUSED_PARAMETER gpointer user_data)
1130 sipe_free_buddy((struct sipe_buddy *) buddy);
1132 /* We must return TRUE as the key/value have already been deleted */
1133 return(TRUE);
1136 void sipe_buddy_free_all(struct sipe_core_private *sipe_private)
1138 g_hash_table_foreach_steal(sipe_private->buddies, sipe_buddy_remove, NULL);
1141 static void sipe_searchresults_im_buddy(PurpleConnection *gc, GList *row,
1142 SIPE_UNUSED_PARAMETER void *user_data)
1144 PurpleAccount *acct = purple_connection_get_account(gc);
1145 char *id = sip_uri_from_name((gchar *)g_list_nth_data(row, 0));
1146 PurpleConversation *conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, id, acct);
1147 if (conv == NULL)
1148 conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, acct, id);
1149 purple_conversation_present(conv);
1150 g_free(id);
1153 static void sipe_searchresults_add_buddy(PurpleConnection *gc, GList *row,
1154 SIPE_UNUSED_PARAMETER void *user_data)
1157 purple_blist_request_add_buddy(purple_connection_get_account(gc),
1158 g_list_nth_data(row, 0), _("Other Contacts"), g_list_nth_data(row, 1));
1161 static gboolean process_search_contact_response(struct sipe_core_private *sipe_private,
1162 struct sipmsg *msg,
1163 SIPE_UNUSED_PARAMETER struct transaction *trans)
1165 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
1166 PurpleNotifySearchResults *results;
1167 PurpleNotifySearchColumn *column;
1168 sipe_xml *searchResults;
1169 const sipe_xml *mrow;
1170 int match_count = 0;
1171 gboolean more = FALSE;
1172 gchar *secondary;
1174 /* valid response? */
1175 if (msg->response != 200) {
1176 SIPE_DEBUG_ERROR("process_search_contact_response: request failed (%d)",
1177 msg->response);
1178 purple_notify_error(sip->gc, NULL,
1179 _("Contact search failed"),
1180 NULL);
1181 return(FALSE);
1184 SIPE_DEBUG_INFO("process_search_contact_response: body:\n%s", msg->body ? msg->body : "");
1186 /* valid XML? */
1187 searchResults = sipe_xml_parse(msg->body, msg->bodylen);
1188 if (!searchResults) {
1189 SIPE_DEBUG_INFO_NOFORMAT("process_search_contact_response: no parseable searchResults");
1190 purple_notify_error(sip->gc, NULL,
1191 _("Contact search failed"),
1192 NULL);
1193 return FALSE;
1196 /* any matches? */
1197 mrow = sipe_xml_child(searchResults, "Body/Array/row");
1198 if (!mrow) {
1199 SIPE_DEBUG_ERROR_NOFORMAT("process_search_contact_response: no matches");
1200 purple_notify_error(sip->gc, NULL,
1201 _("No contacts found"),
1202 NULL);
1204 sipe_xml_free(searchResults);
1205 return(FALSE);
1208 /* OK, we found something - show the results to the user */
1209 results = purple_notify_searchresults_new();
1210 if (!results) {
1211 SIPE_DEBUG_ERROR_NOFORMAT("process_search_contact_response: Unable to display the search results.");
1212 purple_notify_error(sip->gc, NULL, _("Unable to display the search results"), NULL);
1214 sipe_xml_free(searchResults);
1215 return FALSE;
1218 column = purple_notify_searchresults_column_new(_("User name"));
1219 purple_notify_searchresults_column_add(results, column);
1221 column = purple_notify_searchresults_column_new(_("Name"));
1222 purple_notify_searchresults_column_add(results, column);
1224 column = purple_notify_searchresults_column_new(_("Company"));
1225 purple_notify_searchresults_column_add(results, column);
1227 column = purple_notify_searchresults_column_new(_("Country"));
1228 purple_notify_searchresults_column_add(results, column);
1230 column = purple_notify_searchresults_column_new(_("Email"));
1231 purple_notify_searchresults_column_add(results, column);
1233 for (/* initialized above */ ; mrow; mrow = sipe_xml_twin(mrow)) {
1234 GList *row = NULL;
1236 gchar **uri_parts = g_strsplit(sipe_xml_attribute(mrow, "uri"), ":", 2);
1237 row = g_list_append(row, g_strdup(uri_parts[1]));
1238 g_strfreev(uri_parts);
1240 row = g_list_append(row, g_strdup(sipe_xml_attribute(mrow, "displayName")));
1241 row = g_list_append(row, g_strdup(sipe_xml_attribute(mrow, "company")));
1242 row = g_list_append(row, g_strdup(sipe_xml_attribute(mrow, "country")));
1243 row = g_list_append(row, g_strdup(sipe_xml_attribute(mrow, "email")));
1245 purple_notify_searchresults_row_add(results, row);
1246 match_count++;
1249 if ((mrow = sipe_xml_child(searchResults, "Body/directorySearch/moreAvailable")) != NULL) {
1250 char *data = sipe_xml_data(mrow);
1251 more = (g_strcasecmp(data, "true") == 0);
1252 g_free(data);
1255 secondary = g_strdup_printf(
1256 dngettext(PACKAGE_NAME,
1257 "Found %d contact%s:",
1258 "Found %d contacts%s:", match_count),
1259 match_count, more ? _(" (more matched your query)") : "");
1261 purple_notify_searchresults_button_add(results, PURPLE_NOTIFY_BUTTON_IM, sipe_searchresults_im_buddy);
1262 purple_notify_searchresults_button_add(results, PURPLE_NOTIFY_BUTTON_ADD, sipe_searchresults_add_buddy);
1263 purple_notify_searchresults(sip->gc, NULL, NULL, secondary, results, NULL, NULL);
1265 g_free(secondary);
1266 sipe_xml_free(searchResults);
1267 return TRUE;
1270 #define SIPE_SOAP_SEARCH_ROW "<m:row m:attrib=\"%s\" m:value=\"%s\"/>"
1272 void sipe_search_contact_with_cb(PurpleConnection *gc, PurpleRequestFields *fields)
1274 GList *entries = purple_request_field_group_get_fields(purple_request_fields_get_groups(fields)->data);
1275 gchar **attrs = g_new(gchar *, g_list_length(entries) + 1);
1276 unsigned i = 0;
1278 if (!attrs) return;
1280 do {
1281 PurpleRequestField *field = entries->data;
1282 const char *id = purple_request_field_get_id(field);
1283 const char *value = purple_request_field_string_get_value(field);
1285 SIPE_DEBUG_INFO("sipe_search_contact_with_cb: %s = '%s'", id, value ? value : "");
1287 if (value != NULL) attrs[i++] = g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW, id, value);
1288 } while ((entries = g_list_next(entries)) != NULL);
1289 attrs[i] = NULL;
1291 if (i > 0) {
1292 struct sipe_core_private *sipe_private = PURPLE_GC_TO_SIPE_CORE_PRIVATE;
1293 gchar *query = g_strjoinv(NULL, attrs);
1294 SIPE_DEBUG_INFO("sipe_search_contact_with_cb: rows:\n%s", query ? query : "");
1295 sip_soap_directory_search(sipe_private,
1296 100,
1297 query,
1298 process_search_contact_response,
1299 NULL);
1300 g_free(query);
1303 g_strfreev(attrs);
1306 void sipe_core_reset_status(struct sipe_core_public *sipe_public)
1308 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
1309 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007))
1310 sipe_ocs2007_reset_status(sipe_private);
1311 else
1312 sipe_ocs2005_reset_status(sipe_private);
1315 /** for Access levels menu */
1316 #define INDENT_FMT " %s"
1318 /** Member is directly placed to access level container.
1319 * For example SIP URI of user is in the container.
1321 #define INDENT_MARKED_FMT "* %s"
1323 /** Member is indirectly belong to access level container.
1324 * For example 'sameEnterprise' is in the container and user
1325 * belongs to that same enterprise.
1327 #define INDENT_MARKED_INHERITED_FMT "= %s"
1329 GSList *sipe_core_buddy_info(struct sipe_core_public *sipe_public,
1330 const gchar *name,
1331 const gchar *status_name,
1332 gboolean is_online)
1334 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
1335 gchar *note = NULL;
1336 gboolean is_oof_note = FALSE;
1337 const gchar *activity = NULL;
1338 gchar *calendar = NULL;
1339 const gchar *meeting_subject = NULL;
1340 const gchar *meeting_location = NULL;
1341 gchar *access_text = NULL;
1342 GSList *info = NULL;
1344 #define SIPE_ADD_BUDDY_INFO_COMMON(l, t) \
1346 struct sipe_buddy_info *sbi = g_malloc(sizeof(struct sipe_buddy_info)); \
1347 sbi->label = (l); \
1348 sbi->text = (t); \
1349 info = g_slist_append(info, sbi); \
1351 #define SIPE_ADD_BUDDY_INFO(l, t) SIPE_ADD_BUDDY_INFO_COMMON((l), g_markup_escape_text((t), -1))
1352 #define SIPE_ADD_BUDDY_INFO_NOESCAPE(l, t) SIPE_ADD_BUDDY_INFO_COMMON((l), (t))
1354 if (sipe_public) { //happens on pidgin exit
1355 struct sipe_buddy *sbuddy = g_hash_table_lookup(sipe_private->buddies, name);
1356 if (sbuddy) {
1357 note = sbuddy->note;
1358 is_oof_note = sbuddy->is_oof_note;
1359 activity = sbuddy->activity;
1360 calendar = sipe_cal_get_description(sbuddy);
1361 meeting_subject = sbuddy->meeting_subject;
1362 meeting_location = sbuddy->meeting_location;
1364 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
1365 gboolean is_group_access = FALSE;
1366 const int container_id = sipe_ocs2007_find_access_level(sipe_private, "user", sipe_get_no_sip_uri(name), &is_group_access);
1367 const char *access_level = sipe_ocs2007_access_level_name(container_id);
1368 access_text = is_group_access ?
1369 g_strdup(access_level) :
1370 g_strdup_printf(INDENT_MARKED_FMT, access_level);
1374 //Layout
1375 if (is_online)
1377 const gchar *status_str = activity ? activity : status_name;
1379 SIPE_ADD_BUDDY_INFO(_("Status"), status_str);
1381 if (is_online && !is_empty(calendar))
1383 SIPE_ADD_BUDDY_INFO(_("Calendar"), calendar);
1385 g_free(calendar);
1386 if (!is_empty(meeting_location))
1388 SIPE_DEBUG_INFO("sipe_tooltip_text: %s meeting location: '%s'", name, meeting_location);
1389 SIPE_ADD_BUDDY_INFO(_("Meeting in"), meeting_location);
1391 if (!is_empty(meeting_subject))
1393 SIPE_DEBUG_INFO("sipe_tooltip_text: %s meeting subject: '%s'", name, meeting_subject);
1394 SIPE_ADD_BUDDY_INFO(_("Meeting about"), meeting_subject);
1396 if (note)
1398 SIPE_DEBUG_INFO("sipe_tooltip_text: %s note: '%s'", name, note);
1399 SIPE_ADD_BUDDY_INFO_NOESCAPE(is_oof_note ? _("Out of office note") : _("Note"),
1400 g_strdup_printf("<i>%s</i>", note));
1402 if (access_text) {
1403 SIPE_ADD_BUDDY_INFO(_("Access level"), access_text);
1404 g_free(access_text);
1407 return(info);
1410 static PurpleBuddy *
1411 purple_blist_add_buddy_clone(PurpleGroup * group, PurpleBuddy * buddy)
1413 PurpleBuddy *clone;
1414 const gchar *server_alias, *email;
1415 const PurpleStatus *status = purple_presence_get_active_status(purple_buddy_get_presence(buddy));
1417 clone = purple_buddy_new(buddy->account, buddy->name, buddy->alias);
1419 purple_blist_add_buddy(clone, NULL, group, NULL);
1421 server_alias = purple_buddy_get_server_alias(buddy);
1422 if (server_alias) {
1423 purple_blist_server_alias_buddy(clone, server_alias);
1426 email = purple_blist_node_get_string(&buddy->node, EMAIL_PROP);
1427 if (email) {
1428 purple_blist_node_set_string(&clone->node, EMAIL_PROP, email);
1431 purple_presence_set_status_active(purple_buddy_get_presence(clone), purple_status_get_id(status), TRUE);
1432 //for UI to update;
1433 purple_prpl_got_user_status(clone->account, clone->name, purple_status_get_id(status), NULL);
1434 return clone;
1437 static void
1438 sipe_buddy_menu_copy_to_cb(PurpleBlistNode *node, const char *group_name)
1440 PurpleBuddy *buddy, *b;
1441 PurpleConnection *gc;
1442 PurpleGroup * group = purple_find_group(group_name);
1444 g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
1446 buddy = (PurpleBuddy *)node;
1448 SIPE_DEBUG_INFO("sipe_buddy_menu_copy_to_cb: copying %s to %s", buddy->name, group_name);
1449 gc = purple_account_get_connection(buddy->account);
1451 b = purple_find_buddy_in_group(buddy->account, buddy->name, group);
1452 if (!b){
1453 b = purple_blist_add_buddy_clone(group, buddy);
1456 sipe_add_buddy(gc, b, group);
1459 static void
1460 sipe_buddy_menu_chat_new_cb(PurpleBuddy *buddy)
1462 struct sipe_core_private *sipe_private = PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE;
1464 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_new_cb: buddy->name=%s", buddy->name);
1466 /* 2007+ conference */
1467 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007))
1469 sipe_conf_add(sipe_private, buddy->name);
1471 else /* 2005- multiparty chat */
1473 gchar *self = sip_uri_self(sipe_private);
1474 struct sip_session *session;
1476 session = sipe_session_add_chat(sipe_private,
1477 NULL,
1478 TRUE,
1479 self);
1480 session->chat_session->backend = sipe_backend_chat_create(SIPE_CORE_PUBLIC,
1481 session->chat_session,
1482 session->chat_session->title,
1483 self);
1484 g_free(self);
1486 sipe_im_invite(sipe_private, session, buddy->name, NULL, NULL, NULL, FALSE);
1491 * For 2007+ conference only.
1493 static void
1494 sipe_buddy_menu_chat_make_leader_cb(PurpleBuddy *buddy,
1495 struct sipe_chat_session *chat_session)
1497 struct sipe_core_private *sipe_private = PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE;
1498 struct sip_session *session;
1500 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_make_leader_cb: buddy->name=%s", buddy->name);
1501 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_make_leader_cb: chat_title=%s", chat_session->title);
1503 session = sipe_session_find_chat(sipe_private, chat_session);
1505 sipe_conf_modify_user_role(sipe_private, session, buddy->name);
1509 * For 2007+ conference only.
1511 static void
1512 sipe_buddy_menu_chat_remove_cb(PurpleBuddy *buddy,
1513 struct sipe_chat_session *chat_session)
1515 struct sipe_core_private *sipe_private = PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE;
1516 struct sip_session *session;
1518 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_remove_cb: buddy->name=%s", buddy->name);
1519 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_remove_cb: chat_title=%s", chat_session->title);
1521 session = sipe_session_find_chat(sipe_private, chat_session);
1523 sipe_conf_delete_user(sipe_private, session, buddy->name);
1526 static void
1527 sipe_buddy_menu_chat_invite_cb(PurpleBuddy *buddy,
1528 struct sipe_chat_session *chat_session)
1530 struct sipe_core_private *sipe_private = PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE;
1532 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_invite_cb: buddy->name=%s", buddy->name);
1533 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_invite_cb: chat_title=%s", chat_session->title);
1535 sipe_core_chat_invite(SIPE_CORE_PUBLIC, chat_session, buddy->name);
1538 static void
1539 sipe_buddy_menu_make_call_cb(PurpleBuddy *buddy, const char *phone)
1541 struct sipe_core_private *sipe_private = PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE;
1543 SIPE_DEBUG_INFO("sipe_buddy_menu_make_call_cb: buddy->name=%s", buddy->name);
1544 if (phone) {
1545 char *tel_uri = sip_to_tel_uri(phone);
1547 SIPE_DEBUG_INFO("sipe_buddy_menu_make_call_cb: going to call number: %s", tel_uri ? tel_uri : "");
1548 sip_csta_make_call(sipe_private, tel_uri);
1550 g_free(tel_uri);
1554 static void
1555 sipe_buddy_menu_access_level_help_cb(PurpleBuddy *buddy)
1557 /** Translators: replace with URL to localized page
1558 * If it doesn't exist copy the original URL */
1559 purple_notify_uri(buddy->account->gc, _("https://sourceforge.net/apps/mediawiki/sipe/index.php?title=Access_Levels"));
1562 static void
1563 sipe_buddy_menu_send_email_cb(PurpleBuddy *buddy)
1565 const gchar *email;
1566 SIPE_DEBUG_INFO("sipe_buddy_menu_send_email_cb: buddy->name=%s", buddy->name);
1568 email = purple_blist_node_get_string(&buddy->node, EMAIL_PROP);
1569 if (email)
1571 char *command_line = g_strdup_printf(
1572 #ifdef _WIN32
1573 "cmd /c start"
1574 #else
1575 "xdg-email"
1576 #endif
1577 " mailto:%s", email);
1578 SIPE_DEBUG_INFO("sipe_buddy_menu_send_email_cb: going to call email client: %s", command_line);
1580 g_spawn_command_line_async(command_line, NULL);
1581 g_free(command_line);
1583 else
1585 SIPE_DEBUG_INFO("sipe_buddy_menu_send_email_cb: no email address stored for buddy=%s", buddy->name);
1589 static void
1590 sipe_buddy_menu_access_level_cb(PurpleBuddy *buddy,
1591 struct sipe_container *container)
1593 struct sipe_core_private *sipe_private = PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE;
1594 sipe_ocs2007_change_access_level_from_container(sipe_private,
1595 container);
1598 static GList *
1599 sipe_get_access_control_menu(struct sipe_core_private *sipe_private,
1600 const char* uri);
1603 * A menu which appear when right-clicking on buddy in contact list.
1605 GList *
1606 sipe_buddy_menu(PurpleBuddy *buddy)
1608 PurpleBlistNode *g_node;
1609 PurpleGroup *gr_parent;
1610 PurpleMenuAction *act;
1611 GList *menu = NULL;
1612 GList *menu_groups = NULL;
1613 struct sipe_core_private *sipe_private = PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE;
1614 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
1615 const char *email;
1616 gchar *self = sip_uri_self(sipe_private);
1618 SIPE_SESSION_FOREACH {
1619 if (!sipe_strcase_equal(self, buddy->name) && session->chat_session)
1621 struct sipe_chat_session *chat_session = session->chat_session;
1622 gboolean is_conf = (chat_session->type == SIPE_CHAT_TYPE_CONFERENCE);
1624 if (sipe_backend_chat_find(chat_session->backend, buddy->name))
1626 gboolean conf_op = sipe_backend_chat_is_operator(chat_session->backend, self);
1628 if (is_conf
1629 && !sipe_backend_chat_is_operator(chat_session->backend, buddy->name) /* Not conf OP */
1630 && conf_op) /* We are a conf OP */
1632 gchar *label = g_strdup_printf(_("Make leader of '%s'"),
1633 chat_session->title);
1634 act = purple_menu_action_new(label,
1635 PURPLE_CALLBACK(sipe_buddy_menu_chat_make_leader_cb),
1636 chat_session, NULL);
1637 g_free(label);
1638 menu = g_list_prepend(menu, act);
1641 if (is_conf
1642 && conf_op) /* We are a conf OP */
1644 gchar *label = g_strdup_printf(_("Remove from '%s'"),
1645 chat_session->title);
1646 act = purple_menu_action_new(label,
1647 PURPLE_CALLBACK(sipe_buddy_menu_chat_remove_cb),
1648 chat_session, NULL);
1649 g_free(label);
1650 menu = g_list_prepend(menu, act);
1653 else
1655 if (!is_conf
1656 || (is_conf && !session->locked))
1658 gchar *label = g_strdup_printf(_("Invite to '%s'"),
1659 chat_session->title);
1660 act = purple_menu_action_new(label,
1661 PURPLE_CALLBACK(sipe_buddy_menu_chat_invite_cb),
1662 chat_session, NULL);
1663 g_free(label);
1664 menu = g_list_prepend(menu, act);
1668 } SIPE_SESSION_FOREACH_END;
1670 act = purple_menu_action_new(_("New chat"),
1671 PURPLE_CALLBACK(sipe_buddy_menu_chat_new_cb),
1672 NULL, NULL);
1673 menu = g_list_prepend(menu, act);
1675 if (sip->csta && !sip->csta->line_status) {
1676 const char *phone;
1677 const char *phone_disp_str;
1678 gchar *tmp = NULL;
1679 /* work phone */
1680 phone = purple_blist_node_get_string(&buddy->node, PHONE_PROP);
1681 phone_disp_str = purple_blist_node_get_string(&buddy->node, PHONE_DISPLAY_PROP);
1682 if (phone) {
1683 gchar *label = g_strdup_printf(_("Work %s"),
1684 phone_disp_str ? phone_disp_str : (tmp = sip_tel_uri_denormalize(phone)));
1685 act = purple_menu_action_new(label, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb), (gpointer) phone, NULL);
1686 g_free(tmp);
1687 tmp = NULL;
1688 g_free(label);
1689 menu = g_list_prepend(menu, act);
1692 /* mobile phone */
1693 phone = purple_blist_node_get_string(&buddy->node, PHONE_MOBILE_PROP);
1694 phone_disp_str = purple_blist_node_get_string(&buddy->node, PHONE_MOBILE_DISPLAY_PROP);
1695 if (phone) {
1696 gchar *label = g_strdup_printf(_("Mobile %s"),
1697 phone_disp_str ? phone_disp_str : (tmp = sip_tel_uri_denormalize(phone)));
1698 act = purple_menu_action_new(label, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb), (gpointer) phone, NULL);
1699 g_free(tmp);
1700 tmp = NULL;
1701 g_free(label);
1702 menu = g_list_prepend(menu, act);
1705 /* home phone */
1706 phone = purple_blist_node_get_string(&buddy->node, PHONE_HOME_PROP);
1707 phone_disp_str = purple_blist_node_get_string(&buddy->node, PHONE_HOME_DISPLAY_PROP);
1708 if (phone) {
1709 gchar *label = g_strdup_printf(_("Home %s"),
1710 phone_disp_str ? phone_disp_str : (tmp = sip_tel_uri_denormalize(phone)));
1711 act = purple_menu_action_new(label, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb), (gpointer) phone, NULL);
1712 g_free(tmp);
1713 tmp = NULL;
1714 g_free(label);
1715 menu = g_list_prepend(menu, act);
1718 /* other phone */
1719 phone = purple_blist_node_get_string(&buddy->node, PHONE_OTHER_PROP);
1720 phone_disp_str = purple_blist_node_get_string(&buddy->node, PHONE_OTHER_DISPLAY_PROP);
1721 if (phone) {
1722 gchar *label = g_strdup_printf(_("Other %s"),
1723 phone_disp_str ? phone_disp_str : (tmp = sip_tel_uri_denormalize(phone)));
1724 act = purple_menu_action_new(label, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb), (gpointer) phone, NULL);
1725 g_free(tmp);
1726 tmp = NULL;
1727 g_free(label);
1728 menu = g_list_prepend(menu, act);
1731 /* custom1 phone */
1732 phone = purple_blist_node_get_string(&buddy->node, PHONE_CUSTOM1_PROP);
1733 phone_disp_str = purple_blist_node_get_string(&buddy->node, PHONE_CUSTOM1_DISPLAY_PROP);
1734 if (phone) {
1735 gchar *label = g_strdup_printf(_("Custom1 %s"),
1736 phone_disp_str ? phone_disp_str : (tmp = sip_tel_uri_denormalize(phone)));
1737 act = purple_menu_action_new(label, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb), (gpointer) phone, NULL);
1738 g_free(tmp);
1739 tmp = NULL;
1740 g_free(label);
1741 menu = g_list_prepend(menu, act);
1745 email = purple_blist_node_get_string(&buddy->node, EMAIL_PROP);
1746 if (email) {
1747 act = purple_menu_action_new(_("Send email..."),
1748 PURPLE_CALLBACK(sipe_buddy_menu_send_email_cb),
1749 NULL, NULL);
1750 menu = g_list_prepend(menu, act);
1753 /* Access Level */
1754 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
1755 GList *menu_access_levels = sipe_get_access_control_menu(sipe_private, buddy->name);
1757 act = purple_menu_action_new(_("Access level"),
1758 NULL,
1759 NULL, menu_access_levels);
1760 menu = g_list_prepend(menu, act);
1763 /* Copy to */
1764 gr_parent = purple_buddy_get_group(buddy);
1765 for (g_node = purple_blist_get_root(); g_node; g_node = g_node->next) {
1766 PurpleGroup *group;
1768 if (g_node->type != PURPLE_BLIST_GROUP_NODE)
1769 continue;
1771 group = (PurpleGroup *)g_node;
1772 if (group == gr_parent)
1773 continue;
1775 if (purple_find_buddy_in_group(buddy->account, buddy->name, group))
1776 continue;
1778 act = purple_menu_action_new(purple_group_get_name(group),
1779 PURPLE_CALLBACK(sipe_buddy_menu_copy_to_cb),
1780 group->name, NULL);
1781 menu_groups = g_list_prepend(menu_groups, act);
1783 /* Coverity complains about RESOURCE_LEAK here - no idea how to fix it */
1784 menu_groups = g_list_reverse(menu_groups);
1786 act = purple_menu_action_new(_("Copy to"),
1787 NULL,
1788 NULL, menu_groups);
1789 menu = g_list_prepend(menu, act);
1791 menu = g_list_reverse(menu);
1793 g_free(self);
1794 return menu;
1797 static void
1798 sipe_ask_access_domain_cb(PurpleConnection *gc, PurpleRequestFields *fields)
1800 struct sipe_core_private *sipe_private = PURPLE_GC_TO_SIPE_CORE_PRIVATE;
1801 const char *domain = purple_request_fields_get_string(fields, "access_domain");
1802 guint index = purple_request_fields_get_choice(fields, "container_id");
1803 sipe_ocs2007_change_access_level_for_domain(sipe_private,
1804 domain,
1805 index);
1808 static void
1809 sipe_ask_access_domain(struct sipe_core_private *sipe_private)
1811 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
1812 PurpleAccount *account = sip->account;
1813 PurpleConnection *gc = sip->gc;
1814 PurpleRequestFields *fields;
1815 PurpleRequestFieldGroup *g;
1816 PurpleRequestField *f;
1818 fields = purple_request_fields_new();
1820 g = purple_request_field_group_new(NULL);
1821 f = purple_request_field_string_new("access_domain", _("Domain"), "partner-company.com", FALSE);
1822 purple_request_field_set_required(f, TRUE);
1823 purple_request_field_group_add_field(g, f);
1825 f = purple_request_field_choice_new("container_id", _("Access level"), 0);
1826 purple_request_field_choice_add(f, _("Personal")); /* index 0 */
1827 purple_request_field_choice_add(f, _("Team"));
1828 purple_request_field_choice_add(f, _("Company"));
1829 purple_request_field_choice_add(f, _("Public"));
1830 purple_request_field_choice_add(f, _("Blocked")); /* index 4 */
1831 purple_request_field_choice_set_default_value(f, 3); /* index */
1832 purple_request_field_set_required(f, TRUE);
1833 purple_request_field_group_add_field(g, f);
1835 purple_request_fields_add_group(fields, g);
1837 purple_request_fields(gc, _("Add new domain"),
1838 _("Add new domain"), NULL, fields,
1839 _("Add"), G_CALLBACK(sipe_ask_access_domain_cb),
1840 _("Cancel"), NULL,
1841 account, NULL, NULL, gc);
1844 static void
1845 sipe_buddy_menu_access_level_add_domain_cb(PurpleBuddy *buddy)
1847 sipe_ask_access_domain(PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE);
1851 * Workaround for missing libpurple API to release resources allocated
1852 * during blist_node_menu() callback. See also:
1854 * <http://developer.pidgin.im/ticket/12597>
1856 * We remember all memory blocks in a list and deallocate them when
1858 * - the next time we enter the callback, or
1859 * - the account is disconnected
1861 * That means that after the buddy menu has been closed we have unused
1862 * resources but at least we don't leak them anymore...
1864 static void
1865 sipe_blist_menu_free_containers(struct sipe_core_private *sipe_private)
1867 GSList *entry = sipe_private->blist_menu_containers;
1868 while (entry) {
1869 sipe_ocs2007_free_container(entry->data);
1870 entry = entry->next;
1872 g_slist_free(sipe_private->blist_menu_containers);
1873 sipe_private->blist_menu_containers = NULL;
1876 static void
1877 sipe_blist_menu_remember_container(struct sipe_core_private *sipe_private,
1878 struct sipe_container *container)
1880 sipe_private->blist_menu_containers = g_slist_prepend(sipe_private->blist_menu_containers,
1881 container);
1884 static GList *
1885 sipe_get_access_levels_menu(struct sipe_core_private *sipe_private,
1886 const char* member_type,
1887 const char* member_value,
1888 const gboolean extra_menu)
1890 GList *menu_access_levels = NULL;
1891 unsigned int i;
1892 char *menu_name;
1893 PurpleMenuAction *act;
1894 struct sipe_container *container;
1895 gboolean is_group_access = FALSE;
1896 int container_id = sipe_ocs2007_find_access_level(sipe_private,
1897 member_type,
1898 member_value,
1899 &is_group_access);
1900 guint container_max = sipe_ocs2007_containers();
1902 for (i = 1; i <= container_max; i++) {
1903 /* to put Blocked level last in menu list.
1904 * Blocked should remaim in the first place in the containers[] array.
1906 unsigned int j = (i == container_max) ? 0 : i;
1907 int container_j = sipe_ocs2007_container_id(j);
1908 const gchar *acc_level_name = sipe_ocs2007_access_level_name(container_j);
1910 container = sipe_ocs2007_create_container(j,
1911 member_type,
1912 member_value,
1913 FALSE);
1915 /* libpurple memory leak workaround */
1916 sipe_blist_menu_remember_container(sipe_private, container);
1918 /* current container/access level */
1919 if (container_j == container_id) {
1920 menu_name = is_group_access ?
1921 g_strdup_printf(INDENT_MARKED_INHERITED_FMT, acc_level_name) :
1922 g_strdup_printf(INDENT_MARKED_FMT, acc_level_name);
1923 } else {
1924 menu_name = g_strdup_printf(INDENT_FMT, acc_level_name);
1927 act = purple_menu_action_new(menu_name,
1928 PURPLE_CALLBACK(sipe_buddy_menu_access_level_cb),
1929 container, NULL);
1930 g_free(menu_name);
1931 menu_access_levels = g_list_prepend(menu_access_levels, act);
1934 if (extra_menu && (container_id >= 0)) {
1935 /* separator */
1936 act = purple_menu_action_new(" --------------", NULL, NULL, NULL);
1937 menu_access_levels = g_list_prepend(menu_access_levels, act);
1939 if (!is_group_access) {
1940 container = sipe_ocs2007_create_container(0,
1941 member_type,
1942 member_value,
1943 TRUE);
1945 /* libpurple memory leak workaround */
1946 sipe_blist_menu_remember_container(sipe_private, container);
1948 /* Translators: remove (clear) previously assigned access level */
1949 menu_name = g_strdup_printf(INDENT_FMT, _("Unspecify"));
1950 act = purple_menu_action_new(menu_name,
1951 PURPLE_CALLBACK(sipe_buddy_menu_access_level_cb),
1952 container, NULL);
1953 g_free(menu_name);
1954 menu_access_levels = g_list_prepend(menu_access_levels, act);
1958 menu_access_levels = g_list_reverse(menu_access_levels);
1959 return menu_access_levels;
1962 static GList *
1963 sipe_get_access_groups_menu(struct sipe_core_private *sipe_private)
1965 GList *menu_access_groups = NULL;
1966 PurpleMenuAction *act;
1967 GSList *access_domains;
1968 GSList *entry;
1970 act = purple_menu_action_new(_("People in my company"),
1971 NULL,
1972 NULL, sipe_get_access_levels_menu(sipe_private, "sameEnterprise", NULL, FALSE));
1973 menu_access_groups = g_list_prepend(menu_access_groups, act);
1975 /* this is original name, don't edit */
1976 act = purple_menu_action_new(_("People in domains connected with my company"),
1977 NULL,
1978 NULL, sipe_get_access_levels_menu(sipe_private, "federated", NULL, FALSE));
1979 menu_access_groups = g_list_prepend(menu_access_groups, act);
1981 act = purple_menu_action_new(_("People in public domains"),
1982 NULL,
1983 NULL, sipe_get_access_levels_menu(sipe_private, "publicCloud", NULL, TRUE));
1984 menu_access_groups = g_list_prepend(menu_access_groups, act);
1986 access_domains = sipe_ocs2007_get_access_domains(sipe_private);
1987 entry = access_domains;
1988 while (entry) {
1989 gchar *domain = entry->data;
1990 gchar *menu_name = g_strdup_printf(_("People at %s"), domain);
1992 /* takes over ownership of entry->data (= domain) */
1993 act = purple_menu_action_new(menu_name,
1994 NULL,
1995 NULL, sipe_get_access_levels_menu(sipe_private, "domain", domain, TRUE));
1996 menu_access_groups = g_list_prepend(menu_access_groups, act);
1997 g_free(menu_name);
1999 entry = entry->next;
2001 g_slist_free(access_domains);
2003 /* separator */
2004 /* People in domains connected with my company */
2005 act = purple_menu_action_new("-------------------------------------------", NULL, NULL, NULL);
2006 menu_access_groups = g_list_prepend(menu_access_groups, act);
2008 act = purple_menu_action_new(_("Add new domain..."),
2009 PURPLE_CALLBACK(sipe_buddy_menu_access_level_add_domain_cb),
2010 NULL, NULL);
2011 menu_access_groups = g_list_prepend(menu_access_groups, act);
2013 menu_access_groups = g_list_reverse(menu_access_groups);
2015 return menu_access_groups;
2018 static GList *
2019 sipe_get_access_control_menu(struct sipe_core_private *sipe_private,
2020 const char* uri)
2022 GList *menu_access_levels = NULL;
2023 GList *menu_access_groups = NULL;
2024 char *menu_name;
2025 PurpleMenuAction *act;
2027 /* libpurple memory leak workaround */
2028 sipe_blist_menu_free_containers(sipe_private);
2030 menu_access_levels = sipe_get_access_levels_menu(sipe_private, "user", sipe_get_no_sip_uri(uri), TRUE);
2032 menu_access_groups = sipe_get_access_groups_menu(sipe_private);
2034 menu_name = g_strdup_printf(INDENT_FMT, _("Access groups"));
2035 act = purple_menu_action_new(menu_name,
2036 NULL,
2037 NULL, menu_access_groups);
2038 g_free(menu_name);
2039 menu_access_levels = g_list_append(menu_access_levels, act);
2041 menu_name = g_strdup_printf(INDENT_FMT, _("Online help..."));
2042 act = purple_menu_action_new(menu_name,
2043 PURPLE_CALLBACK(sipe_buddy_menu_access_level_help_cb),
2044 NULL, NULL);
2045 g_free(menu_name);
2046 menu_access_levels = g_list_append(menu_access_levels, act);
2048 return menu_access_levels;
2051 static gboolean
2052 process_get_info_response(struct sipe_core_private *sipe_private,
2053 struct sipmsg *msg, struct transaction *trans)
2055 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
2056 char *uri = trans->payload->data;
2058 PurpleNotifyUserInfo *info;
2059 PurpleBuddy *pbuddy = NULL;
2060 struct sipe_buddy *sbuddy;
2061 const char *alias = NULL;
2062 char *device_name = NULL;
2063 char *server_alias = NULL;
2064 char *phone_number = NULL;
2065 char *email = NULL;
2066 const char *site;
2067 char *first_name = NULL;
2068 char *last_name = NULL;
2070 if (!sip) return FALSE;
2072 SIPE_DEBUG_INFO("Fetching %s's user info for %s", uri, sipe_private->username);
2074 pbuddy = purple_find_buddy((PurpleAccount *)sip->account, uri);
2075 alias = purple_buddy_get_local_alias(pbuddy);
2077 //will query buddy UA's capabilities and send answer to log
2078 sipe_options_request(sipe_private, uri);
2080 sbuddy = g_hash_table_lookup(sipe_private->buddies, uri);
2081 if (sbuddy) {
2082 device_name = sbuddy->device_name ? g_strdup(sbuddy->device_name) : NULL;
2085 info = purple_notify_user_info_new();
2087 if (msg->response != 200) {
2088 SIPE_DEBUG_INFO("process_get_info_response: SERVICE response is %d", msg->response);
2089 } else {
2090 sipe_xml *searchResults;
2091 const sipe_xml *mrow;
2093 SIPE_DEBUG_INFO("process_get_info_response: body:\n%s", msg->body ? msg->body : "");
2094 searchResults = sipe_xml_parse(msg->body, msg->bodylen);
2095 if (!searchResults) {
2096 SIPE_DEBUG_INFO_NOFORMAT("process_get_info_response: no parseable searchResults");
2097 } else if ((mrow = sipe_xml_child(searchResults, "Body/Array/row"))) {
2098 const char *value;
2099 server_alias = g_strdup(sipe_xml_attribute(mrow, "displayName"));
2100 email = g_strdup(sipe_xml_attribute(mrow, "email"));
2101 phone_number = g_strdup(sipe_xml_attribute(mrow, "phone"));
2103 /* For 2007 system we will take this from ContactCard -
2104 * it has cleaner tel: URIs at least
2106 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
2107 char *tel_uri = sip_to_tel_uri(phone_number);
2108 /* trims its parameters, so call first */
2109 sipe_update_user_info(sipe_private, uri, SIPE_BUDDY_INFO_DISPLAY_NAME, server_alias);
2110 sipe_update_user_info(sipe_private, uri, SIPE_BUDDY_INFO_EMAIL, email);
2111 sipe_update_user_info(sipe_private, uri, SIPE_BUDDY_INFO_WORK_PHONE, tel_uri);
2112 sipe_update_user_info(sipe_private, uri, SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY, phone_number);
2113 g_free(tel_uri);
2116 #if PURPLE_VERSION_CHECK(3,0,0)
2117 #define PURPLE_NOTIFY_USER_INFO_ADD_PAIR purple_notify_user_info_add_pair_html
2118 #else
2119 #define PURPLE_NOTIFY_USER_INFO_ADD_PAIR purple_notify_user_info_add_pair
2120 #endif
2122 if (server_alias && strlen(server_alias) > 0) {
2123 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info, _("Display name"), server_alias);
2125 if ((value = sipe_xml_attribute(mrow, "title")) && strlen(value) > 0) {
2126 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info, _("Job title"), value);
2128 if ((value = sipe_xml_attribute(mrow, "office")) && strlen(value) > 0) {
2129 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info, _("Office"), value);
2131 if (phone_number && strlen(phone_number) > 0) {
2132 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info, _("Business phone"), phone_number);
2134 if ((value = sipe_xml_attribute(mrow, "company")) && strlen(value) > 0) {
2135 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info, _("Company"), value);
2137 if ((value = sipe_xml_attribute(mrow, "city")) && strlen(value) > 0) {
2138 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info, _("City"), value);
2140 if ((value = sipe_xml_attribute(mrow, "state")) && strlen(value) > 0) {
2141 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info, _("State"), value);
2143 if ((value = sipe_xml_attribute(mrow, "country")) && strlen(value) > 0) {
2144 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info, _("Country"), value);
2146 if (email && strlen(email) > 0) {
2147 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info, _("Email address"), email);
2151 sipe_xml_free(searchResults);
2154 purple_notify_user_info_add_section_break(info);
2156 if (is_empty(server_alias)) {
2157 g_free(server_alias);
2158 server_alias = g_strdup(purple_buddy_get_server_alias(pbuddy));
2159 if (server_alias) {
2160 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info, _("Display name"), server_alias);
2164 /* present alias if it differs from server alias */
2165 if (alias && !sipe_strequal(alias, server_alias))
2167 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info, _("Alias"), alias);
2170 if (is_empty(email)) {
2171 g_free(email);
2172 email = g_strdup(purple_blist_node_get_string(&pbuddy->node, EMAIL_PROP));
2173 if (email) {
2174 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info, _("Email address"), email);
2178 site = purple_blist_node_get_string(&pbuddy->node, SITE_PROP);
2179 if (site) {
2180 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info, _("Site"), site);
2183 sipe_get_first_last_names(sipe_private, uri, &first_name, &last_name);
2184 if (first_name && last_name) {
2185 char *link = g_strconcat("http://www.linkedin.com/pub/dir/", first_name, "/", last_name, NULL);
2187 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info, _("Find on LinkedIn"), link);
2188 g_free(link);
2190 g_free(first_name);
2191 g_free(last_name);
2193 if (device_name) {
2194 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info, _("Device"), device_name);
2197 /* show a buddy's user info in a nice dialog box */
2198 purple_notify_userinfo(sip->gc, /* connection the buddy info came through */
2199 uri, /* buddy's URI */
2200 info, /* body */
2201 NULL, /* callback called when dialog closed */
2202 NULL); /* userdata for callback */
2204 g_free(phone_number);
2205 g_free(server_alias);
2206 g_free(email);
2207 g_free(device_name);
2209 return TRUE;
2213 * AD search first, LDAP based
2215 void sipe_get_info(PurpleConnection *gc, const char *username)
2217 struct sipe_core_private *sipe_private = PURPLE_GC_TO_SIPE_CORE_PRIVATE;
2218 char *row = g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW, "msRTCSIP-PrimaryUserAddress", username);
2219 struct transaction_payload *payload = g_new0(struct transaction_payload, 1);
2221 payload->destroy = g_free;
2222 payload->data = g_strdup(username);
2224 SIPE_DEBUG_INFO("sipe_get_info: row: %s", row ? row : "");
2225 sip_soap_directory_search(sipe_private,
2227 row,
2228 process_get_info_response,
2229 payload);
2230 g_free(row);
2234 Local Variables:
2235 mode: c
2236 c-file-style: "bsd"
2237 indent-tabs-mode: t
2238 tab-width: 8
2239 End: