core cleanup: separate code for sipe_connection_cleanup()
[siplcs.git] / src / core / sipe.c
blob649d11277863067b4af7b16b6b6bdfd5ad43c5f5
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-dialog.h"
90 #include "sipe-im.h"
91 #include "sipe-nls.h"
92 #include "sipe-ocs2005.h"
93 #include "sipe-ocs2007.h"
94 #include "sipe-schedule.h"
95 #include "sipe-session.h"
96 #include "sipe-subscriptions.h"
97 #include "sipe-utils.h"
98 #include "sipe-xml.h"
100 #define _SIPE_NEED_ACTIVITIES /* ugly hack :-( */
101 #include "sipe.h"
103 #define SIPE_IDLE_SET_DELAY 1 /* 1 sec */
105 /* Status identifiers (see also: sipe_status_types()) */
106 #define SIPE_STATUS_ID_UNKNOWN purple_primitive_get_id_from_type(PURPLE_STATUS_UNSET) /* Unset (primitive) */
107 #define SIPE_STATUS_ID_OFFLINE purple_primitive_get_id_from_type(PURPLE_STATUS_OFFLINE) /* Offline (primitive) */
108 #define SIPE_STATUS_ID_AVAILABLE purple_primitive_get_id_from_type(PURPLE_STATUS_AVAILABLE) /* Online */
109 /* PURPLE_STATUS_UNAVAILABLE: */
110 #define SIPE_STATUS_ID_BUSY "busy" /* Busy */
111 #define SIPE_STATUS_ID_BUSYIDLE "busyidle" /* BusyIdle */
112 #define SIPE_STATUS_ID_DND "do-not-disturb" /* Do Not Disturb */
113 #define SIPE_STATUS_ID_IN_MEETING "in-a-meeting" /* In a meeting */
114 #define SIPE_STATUS_ID_IN_CONF "in-a-conference" /* In a conference */
115 #define SIPE_STATUS_ID_ON_PHONE "on-the-phone" /* On the phone */
116 #define SIPE_STATUS_ID_INVISIBLE purple_primitive_get_id_from_type(PURPLE_STATUS_INVISIBLE) /* Appear Offline */
117 /* PURPLE_STATUS_AWAY: */
118 #define SIPE_STATUS_ID_IDLE "idle" /* Idle/Inactive */
119 #define SIPE_STATUS_ID_BRB "be-right-back" /* Be Right Back */
120 #define SIPE_STATUS_ID_AWAY purple_primitive_get_id_from_type(PURPLE_STATUS_AWAY) /* Away (primitive) */
121 /** Reuters status (user settable) */
122 #define SIPE_STATUS_ID_LUNCH "out-to-lunch" /* Out To Lunch */
123 /* ??? PURPLE_STATUS_EXTENDED_AWAY */
124 /* ??? PURPLE_STATUS_MOBILE */
125 /* ??? PURPLE_STATUS_TUNE */
127 /* Status attributes (see also sipe_status_types() */
128 #define SIPE_STATUS_ATTR_ID_MESSAGE "message"
130 static struct sipe_activity_map_struct
132 sipe_activity type;
133 const char *token;
134 const char *desc;
135 const char *status_id;
137 } const sipe_activity_map[] =
139 /* This has nothing to do with Availability numbers, like 3500 (online).
140 * Just a mapping of Communicator Activities to Purple statuses to be able display them in Pidgin.
142 { SIPE_ACTIVITY_UNSET, "unset", NULL , NULL },
143 { SIPE_ACTIVITY_ONLINE, "online", NULL , NULL },
144 { SIPE_ACTIVITY_INACTIVE, SIPE_STATUS_ID_IDLE, N_("Inactive") , NULL },
145 { SIPE_ACTIVITY_BUSY, SIPE_STATUS_ID_BUSY, N_("Busy") , SIPE_STATUS_ID_BUSY },
146 { SIPE_ACTIVITY_BUSYIDLE, SIPE_STATUS_ID_BUSYIDLE, N_("Busy-Idle") , NULL },
147 { SIPE_ACTIVITY_DND, SIPE_STATUS_ID_DND, NULL , SIPE_STATUS_ID_DND },
148 { SIPE_ACTIVITY_BRB, SIPE_STATUS_ID_BRB, N_("Be right back") , SIPE_STATUS_ID_BRB },
149 { SIPE_ACTIVITY_AWAY, "away", NULL , NULL },
150 { SIPE_ACTIVITY_LUNCH, SIPE_STATUS_ID_LUNCH, N_("Out to lunch") , NULL },
151 { SIPE_ACTIVITY_OFFLINE, "offline", NULL , NULL },
152 { SIPE_ACTIVITY_ON_PHONE, SIPE_STATUS_ID_ON_PHONE, N_("In a call") , NULL },
153 { SIPE_ACTIVITY_IN_CONF, SIPE_STATUS_ID_IN_CONF, N_("In a conference") , NULL },
154 { SIPE_ACTIVITY_IN_MEETING, SIPE_STATUS_ID_IN_MEETING, N_("In a meeting") , NULL },
155 { SIPE_ACTIVITY_OOF, "out-of-office", N_("Out of office") , NULL },
156 { SIPE_ACTIVITY_URGENT_ONLY, "urgent-interruptions-only", N_("Urgent interruptions only") , NULL }
158 /** @param x is sipe_activity */
159 #define SIPE_ACTIVITY_I18N(x) gettext(sipe_activity_map[x].desc)
161 const gchar *sipe_activity_to_token(sipe_activity type)
163 return(sipe_activity_map[type].token);
166 const gchar *sipe_activity_description(sipe_activity type)
168 return(SIPE_ACTIVITY_I18N(type));
171 void sipe_set_unknown_status(struct sipe_core_private *sipe_private)
173 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
175 g_free(sip->status);
176 sip->status = g_strdup(SIPE_STATUS_ID_UNKNOWN);
179 void sipe_set_initial_status(struct sipe_core_private *sipe_private)
181 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
183 g_free(sip->status);
184 sip->status = g_strdup(SIPE_STATUS_ID_AVAILABLE); /* our initial state */
187 void sipe_set_invisible_status(struct sipe_core_private *sipe_private)
189 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
191 g_free(sip->status);
192 sip->status = g_strdup(SIPE_STATUS_ID_INVISIBLE);
195 static sipe_activity
196 sipe_get_activity_by_token(const char *token)
198 int i;
200 for (i = 0; i < SIPE_ACTIVITY_NUM_TYPES; i++)
202 if (sipe_strequal(token, sipe_activity_to_token(i)))
203 return sipe_activity_map[i].type;
206 return sipe_activity_map[0].type;
209 const gchar *sipe_activity_description_from_token(const gchar *token)
211 if (!token) return NULL;
213 return sipe_activity_description(sipe_get_activity_by_token(token));
216 void
217 sipe_apply_calendar_status(struct sipe_core_private *sipe_private,
218 struct sipe_buddy *sbuddy,
219 const char *status_id)
221 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
222 time_t cal_avail_since;
223 int cal_status = sipe_cal_get_status(sbuddy, time(NULL), &cal_avail_since);
224 int avail;
225 gchar *self_uri;
227 if (!sbuddy) return;
229 if (cal_status < SIPE_CAL_NO_DATA) {
230 SIPE_DEBUG_INFO("sipe_apply_calendar_status: cal_status : %d for %s", cal_status, sbuddy->name);
231 SIPE_DEBUG_INFO("sipe_apply_calendar_status: cal_avail_since : %s", asctime(localtime(&cal_avail_since)));
234 /* scheduled Cal update call */
235 if (!status_id) {
236 status_id = sbuddy->last_non_cal_status_id;
237 g_free(sbuddy->activity);
238 sbuddy->activity = g_strdup(sbuddy->last_non_cal_activity);
241 if (!status_id) {
242 SIPE_DEBUG_INFO("sipe_apply_calendar_status: status_id is NULL for %s, exiting.",
243 sbuddy->name ? sbuddy->name : "" );
244 return;
247 /* adjust to calendar status */
248 if (cal_status != SIPE_CAL_NO_DATA) {
249 SIPE_DEBUG_INFO("sipe_apply_calendar_status: user_avail_since: %s", asctime(localtime(&sbuddy->user_avail_since)));
251 if (cal_status == SIPE_CAL_BUSY
252 && cal_avail_since > sbuddy->user_avail_since
253 && 6500 >= sipe_get_availability_by_status(status_id, NULL))
255 status_id = SIPE_STATUS_ID_BUSY;
256 g_free(sbuddy->activity);
257 sbuddy->activity = g_strdup(sipe_activity_description(SIPE_ACTIVITY_IN_MEETING));
259 avail = sipe_get_availability_by_status(status_id, NULL);
261 SIPE_DEBUG_INFO("sipe_apply_calendar_status: activity_since : %s", asctime(localtime(&sbuddy->activity_since)));
262 if (cal_avail_since > sbuddy->activity_since) {
263 if (cal_status == SIPE_CAL_OOF
264 && avail >= 15000) /* 12000 in 2007 */
266 g_free(sbuddy->activity);
267 sbuddy->activity = g_strdup(sipe_activity_description(SIPE_ACTIVITY_OOF));
272 /* then set status_id actually */
273 SIPE_DEBUG_INFO("sipe_apply_calendar_status: to %s for %s", status_id, sbuddy->name ? sbuddy->name : "" );
274 sipe_backend_buddy_set_status(SIPE_CORE_PUBLIC, sbuddy->name, status_id);
276 /* set our account state to the one in roaming (including calendar info) */
277 self_uri = sip_uri_self(sipe_private);
278 if (sip->initial_state_published && sipe_strcase_equal(sbuddy->name, self_uri)) {
279 if (sipe_strequal(status_id, SIPE_STATUS_ID_OFFLINE)) {
280 status_id = g_strdup(SIPE_STATUS_ID_INVISIBLE); /* not not let offline status switch us off */
283 SIPE_DEBUG_INFO("sipe_apply_calendar_status: switch to '%s' for the account", sip->status);
284 sipe_backend_account_status_and_note(sipe_private, status_id);
286 g_free(self_uri);
289 void
290 sipe_core_buddy_got_status(struct sipe_core_public *sipe_public,
291 const gchar* uri,
292 const gchar *status_id)
294 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
295 struct sipe_buddy *sbuddy = g_hash_table_lookup(sipe_private->buddies, uri);
297 if (!sbuddy) return;
299 /* Check if on 2005 system contact's calendar,
300 * then set/preserve it.
302 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
303 sipe_apply_calendar_status(sipe_private, sbuddy, status_id);
304 } else {
305 sipe_backend_buddy_set_status(sipe_public, uri, status_id);
309 void sipe_set_status(PurpleAccount *account, PurpleStatus *status)
311 SIPE_DEBUG_INFO("sipe_set_status: status=%s", purple_status_get_id(status));
313 if (!purple_status_is_active(status))
314 return;
316 if (account->gc) {
317 struct sipe_core_private *sipe_private = PURPLE_ACCOUNT_TO_SIPE_CORE_PRIVATE;
318 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
320 if (sip) {
321 gchar *action_name;
322 gchar *tmp;
323 time_t now = time(NULL);
324 const char *status_id = purple_status_get_id(status);
325 const char *note = purple_status_get_attr_string(status, SIPE_STATUS_ATTR_ID_MESSAGE);
326 sipe_activity activity = sipe_get_activity_by_token(status_id);
327 gboolean do_not_publish = ((now - sip->do_not_publish[activity]) <= 2);
329 /* when other point of presence clears note, but we are keeping
330 * state if OOF note.
332 if (do_not_publish && !note && sip->cal && sip->cal->oof_note) {
333 SIPE_DEBUG_INFO_NOFORMAT("sipe_set_status: enabling publication as OOF note keepers.");
334 do_not_publish = FALSE;
337 SIPE_DEBUG_INFO("sipe_set_status: was: sip->do_not_publish[%s]=%d [?] now(time)=%d",
338 status_id, (int)sip->do_not_publish[activity], (int)now);
340 sip->do_not_publish[activity] = 0;
341 SIPE_DEBUG_INFO("sipe_set_status: set: sip->do_not_publish[%s]=%d [0]",
342 status_id, (int)sip->do_not_publish[activity]);
344 if (do_not_publish)
346 SIPE_DEBUG_INFO_NOFORMAT("sipe_set_status: publication was switched off, exiting.");
347 return;
350 g_free(sip->status);
351 sip->status = g_strdup(status_id);
353 /* hack to escape apostrof before comparison */
354 tmp = note ? sipe_utils_str_replace(note, "'", "&apos;") : NULL;
356 /* this will preserve OOF flag as well */
357 if (!sipe_strequal(tmp, sip->note)) {
358 sip->is_oof_note = FALSE;
359 g_free(sip->note);
360 sip->note = g_strdup(note);
361 sip->note_since = time(NULL);
363 g_free(tmp);
365 /* schedule 2 sec to capture idle flag */
366 action_name = g_strdup_printf("<%s>", "+set-status");
367 sipe_schedule_seconds(sipe_private,
368 action_name,
369 NULL,
370 SIPE_IDLE_SET_DELAY,
371 send_presence_status,
372 NULL);
373 g_free(action_name);
378 void
379 sipe_set_idle(PurpleConnection * gc,
380 int interval)
382 SIPE_DEBUG_INFO("sipe_set_idle: interval=%d", interval);
384 if (gc) {
385 struct sipe_core_private *sipe_private = PURPLE_GC_TO_SIPE_CORE_PRIVATE;
386 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
388 if (sip) {
389 sip->idle_switch = time(NULL);
390 SIPE_DEBUG_INFO("sipe_set_idle: sip->idle_switch : %s", asctime(localtime(&(sip->idle_switch))));
395 const gchar *sipe_get_buddy_status(struct sipe_core_private *sipe_private,
396 const gchar *uri)
398 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
399 PurpleBuddy *pbuddy = purple_find_buddy((PurpleAccount *)sip->account, uri);
400 const PurplePresence *presence = purple_buddy_get_presence(pbuddy);
401 const PurpleStatus *pstatus = purple_presence_get_active_status(presence);
402 return(purple_status_get_id(pstatus));
405 void sipe_buddy_status_from_activity(struct sipe_core_private *sipe_private,
406 const gchar *uri,
407 const gchar *activity,
408 gboolean is_online)
410 if (is_online) {
411 const gchar *status_id = NULL;
412 if (activity) {
413 if (sipe_strequal(activity, sipe_activity_to_token(SIPE_ACTIVITY_BUSY))) {
414 status_id = SIPE_STATUS_ID_BUSY;
415 } else if (sipe_strequal(activity, sipe_activity_to_token(SIPE_ACTIVITY_AWAY))) {
416 status_id = SIPE_STATUS_ID_AWAY;
420 if (!status_id) {
421 status_id = SIPE_STATUS_ID_AVAILABLE;
424 SIPE_DEBUG_INFO("sipe_buddy_status_from_activity: status_id(%s)", status_id);
425 sipe_core_buddy_got_status(SIPE_CORE_PUBLIC, uri, status_id);
426 } else {
427 sipe_core_buddy_got_status(SIPE_CORE_PUBLIC, uri, SIPE_STATUS_ID_OFFLINE);
432 * Tries to figure out user first and last name
433 * based on Display Name and email properties.
435 * Allocates memory - must be g_free()'d
437 * Examples to parse:
438 * First Last
439 * First Last - Company Name
440 * Last, First
441 * Last, First M.
442 * Last, First (C)(STP) (Company)
443 * first.last@company.com (preprocessed as "first last")
444 * first.last.company.com@reuters.net (preprocessed as "first last company com")
446 * Unusable examples:
447 * user@company.com (preprocessed as "user")
448 * first.m.last@company.com (preprocessed as "first m last")
449 * user.company.com@reuters.net (preprocessed as "user company com")
451 static void
452 sipe_get_first_last_names(struct sipe_core_private *sipe_private,
453 const char *uri,
454 char **first_name,
455 char **last_name)
457 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
458 sipe_backend_buddy p_buddy;
459 char *display_name;
460 gchar *email;
461 const char *first, *last;
462 char *tmp;
463 char **parts;
464 gboolean has_comma = FALSE;
466 if (!sip || !uri) return;
468 p_buddy = sipe_backend_buddy_find(SIPE_CORE_PUBLIC, uri, NULL);
470 if (!p_buddy) return;
472 display_name = sipe_backend_buddy_get_alias(SIPE_CORE_PUBLIC, p_buddy);
473 email = sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC, p_buddy, SIPE_BUDDY_INFO_EMAIL);
475 if (!display_name && !email) return;
477 /* if no display name, make "first last anything_else" out of email */
478 if (email && !display_name) {
479 display_name = g_strndup(email, strstr(email, "@") - email);
480 display_name = sipe_utils_str_replace((tmp = display_name), ".", " ");
481 g_free(tmp);
484 if (display_name) {
485 has_comma = (strstr(display_name, ",") != NULL);
486 display_name = sipe_utils_str_replace((tmp = display_name), ", ", " ");
487 g_free(tmp);
488 display_name = sipe_utils_str_replace((tmp = display_name), ",", " ");
489 g_free(tmp);
492 parts = g_strsplit(display_name, " ", 0);
494 if (!parts[0] || !parts[1]) {
495 g_free(email);
496 g_free(display_name);
497 g_strfreev(parts);
498 return;
501 if (has_comma) {
502 last = parts[0];
503 first = parts[1];
504 } else {
505 first = parts[0];
506 last = parts[1];
509 if (first_name) {
510 *first_name = g_strstrip(g_strdup(first));
513 if (last_name) {
514 *last_name = g_strstrip(g_strdup(last));
517 g_free(email);
518 g_free(display_name);
519 g_strfreev(parts);
523 * This method motivates Purple's Host (e.g. Pidgin) to update its UI
524 * by using standard Purple's means of signals and saved statuses.
526 * Thus all UI elements get updated: Status Button with Note, docklet.
527 * This is ablolutely important as both our status and note can come
528 * inbound (roaming) or be updated programmatically (e.g. based on our
529 * calendar data).
531 void sipe_backend_account_status_and_note(struct sipe_core_private *sipe_private,
532 const gchar *status_id)
534 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
535 PurpleAccount *account = sip->account;
536 PurpleStatus *status = purple_account_get_active_status(account);
537 const gchar *message = sip->note;
538 time_t *do_not_publish = sip->do_not_publish;
539 gboolean changed = TRUE;
541 if (g_str_equal(status_id, purple_status_get_id(status)) &&
542 sipe_strequal(message, purple_status_get_attr_string(status, SIPE_STATUS_ATTR_ID_MESSAGE)))
544 changed = FALSE;
547 if (purple_savedstatus_is_idleaway()) {
548 changed = FALSE;
551 if (changed) {
552 PurpleSavedStatus *saved_status;
553 const PurpleStatusType *acct_status_type =
554 purple_status_type_find_with_id(account->status_types, status_id);
555 PurpleStatusPrimitive primitive = purple_status_type_get_primitive(acct_status_type);
556 sipe_activity activity = sipe_get_activity_by_token(status_id);
558 saved_status = purple_savedstatus_find_transient_by_type_and_message(primitive, message);
559 if (saved_status) {
560 purple_savedstatus_set_substatus(saved_status, account, acct_status_type, message);
563 /* If this type+message is unique then create a new transient saved status
564 * Ref: gtkstatusbox.c
566 if (!saved_status) {
567 GList *tmp;
568 GList *active_accts = purple_accounts_get_all_active();
570 saved_status = purple_savedstatus_new(NULL, primitive);
571 purple_savedstatus_set_message(saved_status, message);
573 for (tmp = active_accts; tmp != NULL; tmp = tmp->next) {
574 purple_savedstatus_set_substatus(saved_status,
575 (PurpleAccount *)tmp->data, acct_status_type, message);
577 g_list_free(active_accts);
580 do_not_publish[activity] = time(NULL);
581 SIPE_DEBUG_INFO("sipe_set_purple_account_status_and_note: do_not_publish[%s]=%d [now]",
582 status_id, (int)do_not_publish[activity]);
584 /* Set the status for each account */
585 purple_savedstatus_activate(saved_status);
589 /* IM Session (INVITE and MESSAGE methods) */
591 static gboolean
592 process_options_response(SIPE_UNUSED_PARAMETER struct sipe_core_private *sipe_private,
593 struct sipmsg *msg,
594 SIPE_UNUSED_PARAMETER struct transaction *trans)
596 gboolean ret = TRUE;
598 if (msg->response != 200) {
599 SIPE_DEBUG_INFO("process_options_response: OPTIONS response is %d", msg->response);
600 return FALSE;
603 SIPE_DEBUG_INFO("process_options_response: body:\n%s", msg->body ? msg->body : "");
605 return ret;
609 * Asks UA/proxy about its capabilities.
611 static void sipe_options_request(struct sipe_core_private *sipe_private,
612 const char *who)
614 gchar *to = sip_uri(who);
615 gchar *contact = get_contact(sipe_private);
616 gchar *request = g_strdup_printf(
617 "Accept: application/sdp\r\n"
618 "Contact: %s\r\n", contact);
619 g_free(contact);
621 sip_transport_request(sipe_private,
622 "OPTIONS",
625 request,
626 NULL,
627 NULL,
628 process_options_response);
630 g_free(to);
631 g_free(request);
634 void
635 sipe_convo_closed(PurpleConnection * gc, const char *who)
637 struct sipe_core_private *sipe_private = PURPLE_GC_TO_SIPE_CORE_PRIVATE;
639 SIPE_DEBUG_INFO("conversation with %s closed", who);
640 sipe_session_close(sipe_private,
641 sipe_session_find_im(sipe_private, who));
645 * Returns 2005-style activity and Availability.
647 * @param status Sipe statis id.
649 void sipe_get_act_avail_by_status_2005(const char *status,
650 int *activity,
651 int *availability)
653 int avail = 300; /* online */
654 int act = 400; /* Available */
656 if (sipe_strequal(status, SIPE_STATUS_ID_AWAY)) {
657 act = 100;
658 //} else if (sipe_strequal(status, SIPE_STATUS_ID_LUNCH)) {
659 // act = 150;
660 } else if (sipe_strequal(status, SIPE_STATUS_ID_BRB)) {
661 act = 300;
662 } else if (sipe_strequal(status, SIPE_STATUS_ID_AVAILABLE)) {
663 act = 400;
664 //} else if (sipe_strequal(status, SIPE_STATUS_ID_ON_PHONE)) {
665 // act = 500;
666 } else if (sipe_strequal(status, SIPE_STATUS_ID_BUSY) ||
667 sipe_strequal(status, SIPE_STATUS_ID_DND)) {
668 act = 600;
669 } else if (sipe_strequal(status, SIPE_STATUS_ID_INVISIBLE) ||
670 sipe_strequal(status, SIPE_STATUS_ID_OFFLINE)) {
671 avail = 0; /* offline */
672 act = 100;
673 } else {
674 act = 400; /* Available */
677 if (activity) *activity = act;
678 if (availability) *availability = avail;
682 * [MS-SIP] 2.2.1
684 * @param activity 2005 aggregated activity. Ex.: 600
685 * @param availablity 2005 aggregated availablity. Ex.: 300
687 const gchar *
688 sipe_get_status_by_act_avail_2005(const int activity,
689 const int availablity,
690 char **activity_desc)
692 const char *status_id = NULL;
693 const char *act = NULL;
695 if (activity < 150) {
696 status_id = SIPE_STATUS_ID_AWAY;
697 } else if (activity < 200) {
698 //status_id = SIPE_STATUS_ID_LUNCH;
699 status_id = SIPE_STATUS_ID_AWAY;
700 act = sipe_activity_description(SIPE_ACTIVITY_LUNCH);
701 } else if (activity < 300) {
702 //status_id = SIPE_STATUS_ID_IDLE;
703 status_id = SIPE_STATUS_ID_AWAY;
704 act = sipe_activity_description(SIPE_ACTIVITY_INACTIVE);
705 } else if (activity < 400) {
706 status_id = SIPE_STATUS_ID_BRB;
707 } else if (activity < 500) {
708 status_id = SIPE_STATUS_ID_AVAILABLE;
709 } else if (activity < 600) {
710 //status_id = SIPE_STATUS_ID_ON_PHONE;
711 status_id = SIPE_STATUS_ID_BUSY;
712 act = sipe_activity_description(SIPE_ACTIVITY_ON_PHONE);
713 } else if (activity < 700) {
714 status_id = SIPE_STATUS_ID_BUSY;
715 } else if (activity < 800) {
716 status_id = SIPE_STATUS_ID_AWAY;
717 } else {
718 status_id = SIPE_STATUS_ID_AVAILABLE;
721 if (availablity < 100)
722 status_id = SIPE_STATUS_ID_OFFLINE;
724 if (activity_desc && act) {
725 g_free(*activity_desc);
726 *activity_desc = g_strdup(act);
729 return status_id;
733 * [MS-PRES] Table 3: Conversion of legacyInterop elements and attributes to MSRTC elements and attributes.
735 const gchar *
736 sipe_get_status_by_availability(int avail,
737 gchar **activity_desc)
739 const char *status;
740 const char *act = NULL;
742 if (avail < 3000) {
743 status = SIPE_STATUS_ID_OFFLINE;
744 } else if (avail < 4500) {
745 status = SIPE_STATUS_ID_AVAILABLE;
746 } else if (avail < 6000) {
747 //status = SIPE_STATUS_ID_IDLE;
748 status = SIPE_STATUS_ID_AVAILABLE;
749 act = sipe_activity_description(SIPE_ACTIVITY_INACTIVE);
750 } else if (avail < 7500) {
751 status = SIPE_STATUS_ID_BUSY;
752 } else if (avail < 9000) {
753 //status = SIPE_STATUS_ID_BUSYIDLE;
754 status = SIPE_STATUS_ID_BUSY;
755 act = sipe_activity_description(SIPE_ACTIVITY_BUSYIDLE);
756 } else if (avail < 12000) {
757 status = SIPE_STATUS_ID_DND;
758 } else if (avail < 15000) {
759 status = SIPE_STATUS_ID_BRB;
760 } else if (avail < 18000) {
761 status = SIPE_STATUS_ID_AWAY;
762 } else {
763 status = SIPE_STATUS_ID_OFFLINE;
766 if (activity_desc && act) {
767 g_free(*activity_desc);
768 *activity_desc = g_strdup(act);
771 return status;
775 * Returns 2007-style availability value
777 * @param sipe_status_id (in)
778 * @param activity_token (out) Must be g_free()'d after use if consumed.
781 sipe_get_availability_by_status(const char* sipe_status_id, char** activity_token)
783 int availability;
784 sipe_activity activity = SIPE_ACTIVITY_UNSET;
786 if (sipe_strequal(sipe_status_id, SIPE_STATUS_ID_AWAY)) {
787 availability = 15500;
788 if (!activity_token || !(*activity_token)) {
789 activity = SIPE_ACTIVITY_AWAY;
791 } else if (sipe_strequal(sipe_status_id, SIPE_STATUS_ID_BRB)) {
792 availability = 12500;
793 activity = SIPE_ACTIVITY_BRB;
794 } else if (sipe_strequal(sipe_status_id, SIPE_STATUS_ID_DND)) {
795 availability = 9500;
796 activity = SIPE_ACTIVITY_DND;
797 } else if (sipe_strequal(sipe_status_id, SIPE_STATUS_ID_BUSY)) {
798 availability = 6500;
799 if (!activity_token || !(*activity_token)) {
800 activity = SIPE_ACTIVITY_BUSY;
802 } else if (sipe_strequal(sipe_status_id, SIPE_STATUS_ID_AVAILABLE)) {
803 availability = 3500;
804 activity = SIPE_ACTIVITY_ONLINE;
805 } else if (sipe_strequal(sipe_status_id, SIPE_STATUS_ID_UNKNOWN)) {
806 availability = 0;
807 } else {
808 // Offline or invisible
809 availability = 18500;
810 activity = SIPE_ACTIVITY_OFFLINE;
813 if (activity_token) {
814 *activity_token = g_strdup(sipe_activity_to_token(activity));
816 return availability;
820 * Whether user manually changed status or
821 * it was changed automatically due to user
822 * became inactive/active again
824 gboolean
825 sipe_is_user_state(struct sipe_core_private *sipe_private)
827 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
828 gboolean res;
829 time_t now = time(NULL);
831 SIPE_DEBUG_INFO("sipe_is_user_state: sip->idle_switch : %s", asctime(localtime(&(sip->idle_switch))));
832 SIPE_DEBUG_INFO("sipe_is_user_state: now : %s", asctime(localtime(&now)));
834 res = ((now - SIPE_IDLE_SET_DELAY * 2) >= sip->idle_switch);
836 SIPE_DEBUG_INFO("sipe_is_user_state: res = %s", res ? "USER" : "MACHINE");
837 return res;
840 gboolean sipe_is_user_available(struct sipe_core_private *sipe_private)
842 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
843 return(sipe_strequal(sip->status, SIPE_STATUS_ID_AVAILABLE));
846 void send_presence_status(struct sipe_core_private *sipe_private,
847 SIPE_UNUSED_PARAMETER gpointer unused)
849 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
850 PurpleStatus * status = purple_account_get_active_status(sip->account);
852 if (!status) return;
854 SIPE_DEBUG_INFO("send_presence_status: status: %s (%s)",
855 purple_status_get_id(status) ? purple_status_get_id(status) : "",
856 sipe_is_user_state(sipe_private) ? "USER" : "MACHINE");
858 sipe_cal_presence_publish(sipe_private, FALSE);
861 /* temporary function */
862 void sipe_purple_setup(struct sipe_core_public *sipe_public,
863 PurpleConnection *gc)
865 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA;
866 sip->gc = gc;
867 sip->account = purple_connection_get_account(gc);
870 void sipe_core_reset_status(struct sipe_core_public *sipe_public)
872 struct sipe_core_private *sipe_private = SIPE_CORE_PRIVATE;
873 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007))
874 sipe_ocs2007_reset_status(sipe_private);
875 else
876 sipe_ocs2005_reset_status(sipe_private);
879 /** for Access levels menu */
880 #define INDENT_FMT " %s"
882 /** Member is indirectly belong to access level container.
883 * For example 'sameEnterprise' is in the container and user
884 * belongs to that same enterprise.
886 #define INDENT_MARKED_INHERITED_FMT "= %s"
888 static PurpleBuddy *
889 purple_blist_add_buddy_clone(PurpleGroup * group, PurpleBuddy * buddy)
891 struct sipe_core_private *sipe_private = PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE;
892 PurpleBuddy *clone;
893 gchar *server_alias, *email;
894 const PurpleStatus *status = purple_presence_get_active_status(purple_buddy_get_presence(buddy));
896 clone = sipe_backend_buddy_add(SIPE_CORE_PUBLIC,
897 buddy->name,
898 buddy->alias,
899 group->name);
901 server_alias = sipe_backend_buddy_get_server_alias(SIPE_CORE_PUBLIC,
902 buddy);
903 if (server_alias) {
904 sipe_backend_buddy_set_server_alias(SIPE_CORE_PUBLIC,
905 clone,
906 server_alias);
907 g_free(server_alias);
910 email = sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC,
911 buddy,
912 SIPE_BUDDY_INFO_EMAIL);
913 if (email) {
914 sipe_backend_buddy_set_string(SIPE_CORE_PUBLIC,
915 clone,
916 SIPE_BUDDY_INFO_EMAIL,
917 email);
918 g_free(email);
921 purple_presence_set_status_active(purple_buddy_get_presence(clone),
922 purple_status_get_id(status),
923 TRUE);
924 /* for UI to update */
925 sipe_backend_buddy_set_status(SIPE_CORE_PUBLIC,
926 buddy->name,
927 purple_status_get_id(status));
929 return clone;
932 static void
933 sipe_buddy_menu_copy_to_cb(PurpleBlistNode *node, const char *group_name)
935 PurpleBuddy *buddy, *b;
936 PurpleGroup * group = purple_find_group(group_name);
938 g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
940 buddy = (PurpleBuddy *)node;
942 SIPE_DEBUG_INFO("sipe_buddy_menu_copy_to_cb: copying %s to %s",
943 buddy->name, group_name);
945 b = purple_find_buddy_in_group(buddy->account, buddy->name, group);
946 if (!b)
947 b = purple_blist_add_buddy_clone(group, buddy);
949 if (b && group) {
950 PurpleConnection *gc = purple_account_get_connection(b->account);
952 sipe_core_buddy_add(PURPLE_GC_TO_SIPE_CORE_PUBLIC,
953 b->name,
954 group->name);
958 static void
959 sipe_buddy_menu_chat_new_cb(PurpleBuddy *buddy)
961 struct sipe_core_private *sipe_private = PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE;
963 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_new_cb: buddy->name=%s", buddy->name);
965 /* 2007+ conference */
966 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007))
968 sipe_conf_add(sipe_private, buddy->name);
970 else /* 2005- multiparty chat */
972 gchar *self = sip_uri_self(sipe_private);
973 struct sip_session *session;
975 session = sipe_session_add_chat(sipe_private,
976 NULL,
977 TRUE,
978 self);
979 session->chat_session->backend = sipe_backend_chat_create(SIPE_CORE_PUBLIC,
980 session->chat_session,
981 session->chat_session->title,
982 self);
983 g_free(self);
985 sipe_im_invite(sipe_private, session, buddy->name, NULL, NULL, NULL, FALSE);
990 * For 2007+ conference only.
992 static void
993 sipe_buddy_menu_chat_make_leader_cb(PurpleBuddy *buddy,
994 struct sipe_chat_session *chat_session)
996 struct sipe_core_private *sipe_private = PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE;
997 struct sip_session *session;
999 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_make_leader_cb: buddy->name=%s", buddy->name);
1000 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_make_leader_cb: chat_title=%s", chat_session->title);
1002 session = sipe_session_find_chat(sipe_private, chat_session);
1004 sipe_conf_modify_user_role(sipe_private, session, buddy->name);
1008 * For 2007+ conference only.
1010 static void
1011 sipe_buddy_menu_chat_remove_cb(PurpleBuddy *buddy,
1012 struct sipe_chat_session *chat_session)
1014 struct sipe_core_private *sipe_private = PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE;
1015 struct sip_session *session;
1017 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_remove_cb: buddy->name=%s", buddy->name);
1018 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_remove_cb: chat_title=%s", chat_session->title);
1020 session = sipe_session_find_chat(sipe_private, chat_session);
1022 sipe_conf_delete_user(sipe_private, session, buddy->name);
1025 static void
1026 sipe_buddy_menu_chat_invite_cb(PurpleBuddy *buddy,
1027 struct sipe_chat_session *chat_session)
1029 struct sipe_core_private *sipe_private = PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE;
1031 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_invite_cb: buddy->name=%s", buddy->name);
1032 SIPE_DEBUG_INFO("sipe_buddy_menu_chat_invite_cb: chat_title=%s", chat_session->title);
1034 sipe_core_chat_invite(SIPE_CORE_PUBLIC, chat_session, buddy->name);
1037 static void
1038 sipe_buddy_menu_make_call_cb(PurpleBuddy *buddy, const char *phone)
1040 struct sipe_core_private *sipe_private = PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE;
1042 SIPE_DEBUG_INFO("sipe_buddy_menu_make_call_cb: buddy->name=%s", buddy->name);
1043 if (phone) {
1044 char *tel_uri = sip_to_tel_uri(phone);
1046 SIPE_DEBUG_INFO("sipe_buddy_menu_make_call_cb: going to call number: %s", tel_uri ? tel_uri : "");
1047 sip_csta_make_call(sipe_private, tel_uri);
1049 g_free(tel_uri);
1053 static void
1054 sipe_buddy_menu_access_level_help_cb(PurpleBuddy *buddy)
1056 /** Translators: replace with URL to localized page
1057 * If it doesn't exist copy the original URL */
1058 purple_notify_uri(buddy->account->gc, _("https://sourceforge.net/apps/mediawiki/sipe/index.php?title=Access_Levels"));
1061 static void
1062 sipe_buddy_menu_send_email_cb(PurpleBuddy *buddy)
1064 struct sipe_core_private *sipe_private = PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE;
1065 gchar *email;
1066 SIPE_DEBUG_INFO("sipe_buddy_menu_send_email_cb: buddy->name=%s", buddy->name);
1068 email = sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC,
1069 buddy,
1070 SIPE_BUDDY_INFO_EMAIL);
1071 if (email)
1073 char *command_line = g_strdup_printf(
1074 #ifdef _WIN32
1075 "cmd /c start"
1076 #else
1077 "xdg-email"
1078 #endif
1079 " mailto:%s", email);
1080 SIPE_DEBUG_INFO("sipe_buddy_menu_send_email_cb: going to call email client: %s", command_line);
1082 g_free(email);
1083 g_spawn_command_line_async(command_line, NULL);
1084 g_free(command_line);
1086 else
1088 SIPE_DEBUG_INFO("sipe_buddy_menu_send_email_cb: no email address stored for buddy=%s", buddy->name);
1092 static void
1093 sipe_buddy_menu_access_level_cb(PurpleBuddy *buddy,
1094 struct sipe_container *container)
1096 struct sipe_core_private *sipe_private = PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE;
1097 sipe_ocs2007_change_access_level_from_container(sipe_private,
1098 container);
1101 static GList *
1102 sipe_get_access_control_menu(struct sipe_core_private *sipe_private,
1103 const char* uri);
1106 * A menu which appear when right-clicking on buddy in contact list.
1108 GList *
1109 sipe_buddy_menu(PurpleBuddy *buddy)
1111 PurpleBlistNode *g_node;
1112 PurpleGroup *gr_parent;
1113 PurpleMenuAction *act;
1114 GList *menu = NULL;
1115 GList *menu_groups = NULL;
1116 struct sipe_core_private *sipe_private = PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE;
1117 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
1118 gchar *email;
1119 gchar *self = sip_uri_self(sipe_private);
1121 SIPE_SESSION_FOREACH {
1122 if (!sipe_strcase_equal(self, buddy->name) && session->chat_session)
1124 struct sipe_chat_session *chat_session = session->chat_session;
1125 gboolean is_conf = (chat_session->type == SIPE_CHAT_TYPE_CONFERENCE);
1127 if (sipe_backend_chat_find(chat_session->backend, buddy->name))
1129 gboolean conf_op = sipe_backend_chat_is_operator(chat_session->backend, self);
1131 if (is_conf
1132 && !sipe_backend_chat_is_operator(chat_session->backend, buddy->name) /* Not conf OP */
1133 && conf_op) /* We are a conf OP */
1135 gchar *label = g_strdup_printf(_("Make leader of '%s'"),
1136 chat_session->title);
1137 act = purple_menu_action_new(label,
1138 PURPLE_CALLBACK(sipe_buddy_menu_chat_make_leader_cb),
1139 chat_session, NULL);
1140 g_free(label);
1141 menu = g_list_prepend(menu, act);
1144 if (is_conf
1145 && conf_op) /* We are a conf OP */
1147 gchar *label = g_strdup_printf(_("Remove from '%s'"),
1148 chat_session->title);
1149 act = purple_menu_action_new(label,
1150 PURPLE_CALLBACK(sipe_buddy_menu_chat_remove_cb),
1151 chat_session, NULL);
1152 g_free(label);
1153 menu = g_list_prepend(menu, act);
1156 else
1158 if (!is_conf
1159 || (is_conf && !session->locked))
1161 gchar *label = g_strdup_printf(_("Invite to '%s'"),
1162 chat_session->title);
1163 act = purple_menu_action_new(label,
1164 PURPLE_CALLBACK(sipe_buddy_menu_chat_invite_cb),
1165 chat_session, NULL);
1166 g_free(label);
1167 menu = g_list_prepend(menu, act);
1171 } SIPE_SESSION_FOREACH_END;
1173 act = purple_menu_action_new(_("New chat"),
1174 PURPLE_CALLBACK(sipe_buddy_menu_chat_new_cb),
1175 NULL, NULL);
1176 menu = g_list_prepend(menu, act);
1178 if (sip->csta && !sip->csta->line_status) {
1179 gchar *phone;
1180 gchar *phone_disp_str;
1181 gchar *tmp = NULL;
1182 /* work phone */
1183 phone = sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC,
1184 buddy,
1185 SIPE_BUDDY_INFO_WORK_PHONE);
1186 phone_disp_str = sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC,
1187 buddy,
1188 SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY);
1189 if (phone) {
1190 gchar *label = g_strdup_printf(_("Work %s"),
1191 phone_disp_str ? phone_disp_str : (tmp = sip_tel_uri_denormalize(phone)));
1192 act = purple_menu_action_new(label, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb), (gpointer) phone, NULL);
1193 g_free(tmp);
1194 tmp = NULL;
1195 g_free(label);
1196 menu = g_list_prepend(menu, act);
1197 g_free(phone);
1199 g_free(phone_disp_str);
1201 /* mobile phone */
1202 phone = sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC,
1203 buddy,
1204 SIPE_BUDDY_INFO_MOBILE_PHONE);
1205 phone_disp_str = sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC,
1206 buddy,
1207 SIPE_BUDDY_INFO_MOBILE_PHONE_DISPLAY);
1208 if (phone) {
1209 gchar *label = g_strdup_printf(_("Mobile %s"),
1210 phone_disp_str ? phone_disp_str : (tmp = sip_tel_uri_denormalize(phone)));
1211 act = purple_menu_action_new(label, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb), (gpointer) phone, NULL);
1212 g_free(tmp);
1213 tmp = NULL;
1214 g_free(label);
1215 menu = g_list_prepend(menu, act);
1216 g_free(phone);
1218 g_free(phone_disp_str);
1220 /* home phone */
1221 phone = sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC,
1222 buddy,
1223 SIPE_BUDDY_INFO_HOME_PHONE);
1224 phone_disp_str = sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC,
1225 buddy,
1226 SIPE_BUDDY_INFO_HOME_PHONE_DISPLAY);
1227 if (phone) {
1228 gchar *label = g_strdup_printf(_("Home %s"),
1229 phone_disp_str ? phone_disp_str : (tmp = sip_tel_uri_denormalize(phone)));
1230 act = purple_menu_action_new(label, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb), (gpointer) phone, NULL);
1231 g_free(tmp);
1232 tmp = NULL;
1233 g_free(label);
1234 menu = g_list_prepend(menu, act);
1235 g_free(phone);
1237 g_free(phone_disp_str);
1239 /* other phone */
1240 phone = sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC,
1241 buddy,
1242 SIPE_BUDDY_INFO_OTHER_PHONE);
1243 phone_disp_str = sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC,
1244 buddy,
1245 SIPE_BUDDY_INFO_OTHER_PHONE_DISPLAY);
1246 if (phone) {
1247 gchar *label = g_strdup_printf(_("Other %s"),
1248 phone_disp_str ? phone_disp_str : (tmp = sip_tel_uri_denormalize(phone)));
1249 act = purple_menu_action_new(label, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb), (gpointer) phone, NULL);
1250 g_free(tmp);
1251 tmp = NULL;
1252 g_free(label);
1253 menu = g_list_prepend(menu, act);
1254 g_free(phone);
1256 g_free(phone_disp_str);
1258 /* custom1 phone */
1259 phone = sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC,
1260 buddy,
1261 SIPE_BUDDY_INFO_CUSTOM1_PHONE);
1262 phone_disp_str = sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC,
1263 buddy,
1264 SIPE_BUDDY_INFO_CUSTOM1_PHONE_DISPLAY);
1265 if (phone) {
1266 gchar *label = g_strdup_printf(_("Custom1 %s"),
1267 phone_disp_str ? phone_disp_str : (tmp = sip_tel_uri_denormalize(phone)));
1268 act = purple_menu_action_new(label, PURPLE_CALLBACK(sipe_buddy_menu_make_call_cb), (gpointer) phone, NULL);
1269 g_free(tmp);
1270 tmp = NULL;
1271 g_free(label);
1272 menu = g_list_prepend(menu, act);
1273 g_free(phone);
1275 g_free(phone_disp_str);
1278 email = sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC,
1279 buddy,
1280 SIPE_BUDDY_INFO_EMAIL);
1281 if (email) {
1282 act = purple_menu_action_new(_("Send email..."),
1283 PURPLE_CALLBACK(sipe_buddy_menu_send_email_cb),
1284 NULL, NULL);
1285 menu = g_list_prepend(menu, act);
1286 g_free(email);
1289 /* Access Level */
1290 if (SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
1291 GList *menu_access_levels = sipe_get_access_control_menu(sipe_private, buddy->name);
1293 act = purple_menu_action_new(_("Access level"),
1294 NULL,
1295 NULL, menu_access_levels);
1296 menu = g_list_prepend(menu, act);
1299 /* Copy to */
1300 gr_parent = purple_buddy_get_group(buddy);
1301 for (g_node = purple_blist_get_root(); g_node; g_node = g_node->next) {
1302 PurpleGroup *group;
1304 if (g_node->type != PURPLE_BLIST_GROUP_NODE)
1305 continue;
1307 group = (PurpleGroup *)g_node;
1308 if (group == gr_parent)
1309 continue;
1311 if (purple_find_buddy_in_group(buddy->account, buddy->name, group))
1312 continue;
1314 act = purple_menu_action_new(purple_group_get_name(group),
1315 PURPLE_CALLBACK(sipe_buddy_menu_copy_to_cb),
1316 group->name, NULL);
1317 menu_groups = g_list_prepend(menu_groups, act);
1319 /* Coverity complains about RESOURCE_LEAK here - no idea how to fix it */
1320 menu_groups = g_list_reverse(menu_groups);
1322 act = purple_menu_action_new(_("Copy to"),
1323 NULL,
1324 NULL, menu_groups);
1325 menu = g_list_prepend(menu, act);
1327 menu = g_list_reverse(menu);
1329 g_free(self);
1330 return menu;
1333 static void
1334 sipe_ask_access_domain_cb(PurpleConnection *gc, PurpleRequestFields *fields)
1336 struct sipe_core_private *sipe_private = PURPLE_GC_TO_SIPE_CORE_PRIVATE;
1337 const char *domain = purple_request_fields_get_string(fields, "access_domain");
1338 guint index = purple_request_fields_get_choice(fields, "container_id");
1339 sipe_ocs2007_change_access_level_for_domain(sipe_private,
1340 domain,
1341 index);
1344 static void
1345 sipe_ask_access_domain(struct sipe_core_private *sipe_private)
1347 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
1348 PurpleAccount *account = sip->account;
1349 PurpleConnection *gc = sip->gc;
1350 PurpleRequestFields *fields;
1351 PurpleRequestFieldGroup *g;
1352 PurpleRequestField *f;
1354 fields = purple_request_fields_new();
1356 g = purple_request_field_group_new(NULL);
1357 f = purple_request_field_string_new("access_domain", _("Domain"), "partner-company.com", FALSE);
1358 purple_request_field_set_required(f, TRUE);
1359 purple_request_field_group_add_field(g, f);
1361 f = purple_request_field_choice_new("container_id", _("Access level"), 0);
1362 purple_request_field_choice_add(f, _("Personal")); /* index 0 */
1363 purple_request_field_choice_add(f, _("Team"));
1364 purple_request_field_choice_add(f, _("Company"));
1365 purple_request_field_choice_add(f, _("Public"));
1366 purple_request_field_choice_add(f, _("Blocked")); /* index 4 */
1367 purple_request_field_choice_set_default_value(f, 3); /* index */
1368 purple_request_field_set_required(f, TRUE);
1369 purple_request_field_group_add_field(g, f);
1371 purple_request_fields_add_group(fields, g);
1373 purple_request_fields(gc, _("Add new domain"),
1374 _("Add new domain"), NULL, fields,
1375 _("Add"), G_CALLBACK(sipe_ask_access_domain_cb),
1376 _("Cancel"), NULL,
1377 account, NULL, NULL, gc);
1380 static void
1381 sipe_buddy_menu_access_level_add_domain_cb(PurpleBuddy *buddy)
1383 sipe_ask_access_domain(PURPLE_BUDDY_TO_SIPE_CORE_PRIVATE);
1387 * Workaround for missing libpurple API to release resources allocated
1388 * during blist_node_menu() callback. See also:
1390 * <http://developer.pidgin.im/ticket/12597>
1392 * We remember all memory blocks in a list and deallocate them when
1394 * - the next time we enter the callback, or
1395 * - the account is disconnected
1397 * That means that after the buddy menu has been closed we have unused
1398 * resources but at least we don't leak them anymore...
1400 void sipe_blist_menu_free_containers(struct sipe_core_private *sipe_private)
1402 GSList *entry = sipe_private->blist_menu_containers;
1403 while (entry) {
1404 sipe_ocs2007_free_container(entry->data);
1405 entry = entry->next;
1407 g_slist_free(sipe_private->blist_menu_containers);
1408 sipe_private->blist_menu_containers = NULL;
1411 static void
1412 sipe_blist_menu_remember_container(struct sipe_core_private *sipe_private,
1413 struct sipe_container *container)
1415 sipe_private->blist_menu_containers = g_slist_prepend(sipe_private->blist_menu_containers,
1416 container);
1419 static GList *
1420 sipe_get_access_levels_menu(struct sipe_core_private *sipe_private,
1421 const char* member_type,
1422 const char* member_value,
1423 const gboolean extra_menu)
1425 GList *menu_access_levels = NULL;
1426 unsigned int i;
1427 char *menu_name;
1428 PurpleMenuAction *act;
1429 struct sipe_container *container;
1430 gboolean is_group_access = FALSE;
1431 int container_id = sipe_ocs2007_find_access_level(sipe_private,
1432 member_type,
1433 member_value,
1434 &is_group_access);
1435 guint container_max = sipe_ocs2007_containers();
1437 for (i = 1; i <= container_max; i++) {
1438 /* to put Blocked level last in menu list.
1439 * Blocked should remaim in the first place in the containers[] array.
1441 unsigned int j = (i == container_max) ? 0 : i;
1442 int container_j = sipe_ocs2007_container_id(j);
1443 const gchar *acc_level_name = sipe_ocs2007_access_level_name(container_j);
1445 container = sipe_ocs2007_create_container(j,
1446 member_type,
1447 member_value,
1448 FALSE);
1450 /* libpurple memory leak workaround */
1451 sipe_blist_menu_remember_container(sipe_private, container);
1453 /* current container/access level */
1454 if (container_j == container_id) {
1455 menu_name = is_group_access ?
1456 g_strdup_printf(INDENT_MARKED_INHERITED_FMT, acc_level_name) :
1457 g_strdup_printf(SIPE_OCS2007_INDENT_MARKED_FMT, acc_level_name);
1458 } else {
1459 menu_name = g_strdup_printf(INDENT_FMT, acc_level_name);
1462 act = purple_menu_action_new(menu_name,
1463 PURPLE_CALLBACK(sipe_buddy_menu_access_level_cb),
1464 container, NULL);
1465 g_free(menu_name);
1466 menu_access_levels = g_list_prepend(menu_access_levels, act);
1469 if (extra_menu && (container_id >= 0)) {
1470 /* separator */
1471 act = purple_menu_action_new(" --------------", NULL, NULL, NULL);
1472 menu_access_levels = g_list_prepend(menu_access_levels, act);
1474 if (!is_group_access) {
1475 container = sipe_ocs2007_create_container(0,
1476 member_type,
1477 member_value,
1478 TRUE);
1480 /* libpurple memory leak workaround */
1481 sipe_blist_menu_remember_container(sipe_private, container);
1483 /* Translators: remove (clear) previously assigned access level */
1484 menu_name = g_strdup_printf(INDENT_FMT, _("Unspecify"));
1485 act = purple_menu_action_new(menu_name,
1486 PURPLE_CALLBACK(sipe_buddy_menu_access_level_cb),
1487 container, NULL);
1488 g_free(menu_name);
1489 menu_access_levels = g_list_prepend(menu_access_levels, act);
1493 menu_access_levels = g_list_reverse(menu_access_levels);
1494 return menu_access_levels;
1497 static GList *
1498 sipe_get_access_groups_menu(struct sipe_core_private *sipe_private)
1500 GList *menu_access_groups = NULL;
1501 PurpleMenuAction *act;
1502 GSList *access_domains;
1503 GSList *entry;
1505 act = purple_menu_action_new(_("People in my company"),
1506 NULL,
1507 NULL, sipe_get_access_levels_menu(sipe_private, "sameEnterprise", NULL, FALSE));
1508 menu_access_groups = g_list_prepend(menu_access_groups, act);
1510 /* this is original name, don't edit */
1511 act = purple_menu_action_new(_("People in domains connected with my company"),
1512 NULL,
1513 NULL, sipe_get_access_levels_menu(sipe_private, "federated", NULL, FALSE));
1514 menu_access_groups = g_list_prepend(menu_access_groups, act);
1516 act = purple_menu_action_new(_("People in public domains"),
1517 NULL,
1518 NULL, sipe_get_access_levels_menu(sipe_private, "publicCloud", NULL, TRUE));
1519 menu_access_groups = g_list_prepend(menu_access_groups, act);
1521 access_domains = sipe_ocs2007_get_access_domains(sipe_private);
1522 entry = access_domains;
1523 while (entry) {
1524 gchar *domain = entry->data;
1525 gchar *menu_name = g_strdup_printf(_("People at %s"), domain);
1527 /* takes over ownership of entry->data (= domain) */
1528 act = purple_menu_action_new(menu_name,
1529 NULL,
1530 NULL, sipe_get_access_levels_menu(sipe_private, "domain", domain, TRUE));
1531 menu_access_groups = g_list_prepend(menu_access_groups, act);
1532 g_free(menu_name);
1534 entry = entry->next;
1536 g_slist_free(access_domains);
1538 /* separator */
1539 /* People in domains connected with my company */
1540 act = purple_menu_action_new("-------------------------------------------", NULL, NULL, NULL);
1541 menu_access_groups = g_list_prepend(menu_access_groups, act);
1543 act = purple_menu_action_new(_("Add new domain..."),
1544 PURPLE_CALLBACK(sipe_buddy_menu_access_level_add_domain_cb),
1545 NULL, NULL);
1546 menu_access_groups = g_list_prepend(menu_access_groups, act);
1548 menu_access_groups = g_list_reverse(menu_access_groups);
1550 return menu_access_groups;
1553 static GList *
1554 sipe_get_access_control_menu(struct sipe_core_private *sipe_private,
1555 const char* uri)
1557 GList *menu_access_levels = NULL;
1558 GList *menu_access_groups = NULL;
1559 char *menu_name;
1560 PurpleMenuAction *act;
1562 /* libpurple memory leak workaround */
1563 sipe_blist_menu_free_containers(sipe_private);
1565 menu_access_levels = sipe_get_access_levels_menu(sipe_private, "user", sipe_get_no_sip_uri(uri), TRUE);
1567 menu_access_groups = sipe_get_access_groups_menu(sipe_private);
1569 menu_name = g_strdup_printf(INDENT_FMT, _("Access groups"));
1570 act = purple_menu_action_new(menu_name,
1571 NULL,
1572 NULL, menu_access_groups);
1573 g_free(menu_name);
1574 menu_access_levels = g_list_append(menu_access_levels, act);
1576 menu_name = g_strdup_printf(INDENT_FMT, _("Online help..."));
1577 act = purple_menu_action_new(menu_name,
1578 PURPLE_CALLBACK(sipe_buddy_menu_access_level_help_cb),
1579 NULL, NULL);
1580 g_free(menu_name);
1581 menu_access_levels = g_list_append(menu_access_levels, act);
1583 return menu_access_levels;
1586 static gboolean
1587 process_get_info_response(struct sipe_core_private *sipe_private,
1588 struct sipmsg *msg, struct transaction *trans)
1590 struct sipe_account_data *sip = SIPE_ACCOUNT_DATA_PRIVATE;
1591 char *uri = trans->payload->data;
1593 PurpleNotifyUserInfo *info;
1594 PurpleBuddy *pbuddy = NULL;
1595 struct sipe_buddy *sbuddy;
1596 const char *alias = NULL;
1597 char *device_name = NULL;
1598 char *server_alias = NULL;
1599 char *phone_number = NULL;
1600 char *email = NULL;
1601 char *site;
1602 char *first_name = NULL;
1603 char *last_name = NULL;
1605 if (!sip) return FALSE;
1607 SIPE_DEBUG_INFO("Fetching %s's user info for %s", uri, sipe_private->username);
1609 pbuddy = purple_find_buddy((PurpleAccount *)sip->account, uri);
1610 alias = purple_buddy_get_local_alias(pbuddy);
1612 //will query buddy UA's capabilities and send answer to log
1613 sipe_options_request(sipe_private, uri);
1615 sbuddy = g_hash_table_lookup(sipe_private->buddies, uri);
1616 if (sbuddy) {
1617 device_name = sbuddy->device_name ? g_strdup(sbuddy->device_name) : NULL;
1620 info = purple_notify_user_info_new();
1622 if (msg->response != 200) {
1623 SIPE_DEBUG_INFO("process_get_info_response: SERVICE response is %d", msg->response);
1624 } else {
1625 sipe_xml *searchResults;
1626 const sipe_xml *mrow;
1628 SIPE_DEBUG_INFO("process_get_info_response: body:\n%s", msg->body ? msg->body : "");
1629 searchResults = sipe_xml_parse(msg->body, msg->bodylen);
1630 if (!searchResults) {
1631 SIPE_DEBUG_INFO_NOFORMAT("process_get_info_response: no parseable searchResults");
1632 } else if ((mrow = sipe_xml_child(searchResults, "Body/Array/row"))) {
1633 const char *value;
1634 server_alias = g_strdup(sipe_xml_attribute(mrow, "displayName"));
1635 email = g_strdup(sipe_xml_attribute(mrow, "email"));
1636 phone_number = g_strdup(sipe_xml_attribute(mrow, "phone"));
1638 /* For 2007 system we will take this from ContactCard -
1639 * it has cleaner tel: URIs at least
1641 if (!SIPE_CORE_PRIVATE_FLAG_IS(OCS2007)) {
1642 char *tel_uri = sip_to_tel_uri(phone_number);
1643 /* trims its parameters, so call first */
1644 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_DISPLAY_NAME, server_alias);
1645 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_EMAIL, email);
1646 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_WORK_PHONE, tel_uri);
1647 sipe_buddy_update_property(sipe_private, uri, SIPE_BUDDY_INFO_WORK_PHONE_DISPLAY, phone_number);
1648 g_free(tel_uri);
1651 #if PURPLE_VERSION_CHECK(3,0,0)
1652 #define PURPLE_NOTIFY_USER_INFO_ADD_PAIR purple_notify_user_info_add_pair_html
1653 #else
1654 #define PURPLE_NOTIFY_USER_INFO_ADD_PAIR purple_notify_user_info_add_pair
1655 #endif
1657 if (server_alias && strlen(server_alias) > 0) {
1658 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info, _("Display name"), server_alias);
1660 if ((value = sipe_xml_attribute(mrow, "title")) && strlen(value) > 0) {
1661 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info, _("Job title"), value);
1663 if ((value = sipe_xml_attribute(mrow, "office")) && strlen(value) > 0) {
1664 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info, _("Office"), value);
1666 if (phone_number && strlen(phone_number) > 0) {
1667 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info, _("Business phone"), phone_number);
1669 if ((value = sipe_xml_attribute(mrow, "company")) && strlen(value) > 0) {
1670 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info, _("Company"), value);
1672 if ((value = sipe_xml_attribute(mrow, "city")) && strlen(value) > 0) {
1673 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info, _("City"), value);
1675 if ((value = sipe_xml_attribute(mrow, "state")) && strlen(value) > 0) {
1676 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info, _("State"), value);
1678 if ((value = sipe_xml_attribute(mrow, "country")) && strlen(value) > 0) {
1679 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info, _("Country"), value);
1681 if (email && strlen(email) > 0) {
1682 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info, _("Email address"), email);
1686 sipe_xml_free(searchResults);
1689 purple_notify_user_info_add_section_break(info);
1691 if (is_empty(server_alias)) {
1692 g_free(server_alias);
1693 server_alias = sipe_backend_buddy_get_server_alias(SIPE_CORE_PUBLIC,
1694 pbuddy);
1695 if (server_alias) {
1696 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info, _("Display name"), server_alias);
1700 /* present alias if it differs from server alias */
1701 if (alias && !sipe_strequal(alias, server_alias))
1703 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info, _("Alias"), alias);
1706 if (is_empty(email)) {
1707 g_free(email);
1708 email = sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC,
1709 pbuddy,
1710 SIPE_BUDDY_INFO_EMAIL);
1711 if (email) {
1712 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info, _("Email address"), email);
1716 site = sipe_backend_buddy_get_string(SIPE_CORE_PUBLIC,
1717 pbuddy,
1718 SIPE_BUDDY_INFO_SITE);
1719 if (site) {
1720 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info, _("Site"), site);
1721 g_free(site);
1724 sipe_get_first_last_names(sipe_private, uri, &first_name, &last_name);
1725 if (first_name && last_name) {
1726 char *link = g_strconcat("http://www.linkedin.com/pub/dir/", first_name, "/", last_name, NULL);
1728 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info, _("Find on LinkedIn"), link);
1729 g_free(link);
1731 g_free(first_name);
1732 g_free(last_name);
1734 if (device_name) {
1735 PURPLE_NOTIFY_USER_INFO_ADD_PAIR(info, _("Device"), device_name);
1738 /* show a buddy's user info in a nice dialog box */
1739 purple_notify_userinfo(sip->gc, /* connection the buddy info came through */
1740 uri, /* buddy's URI */
1741 info, /* body */
1742 NULL, /* callback called when dialog closed */
1743 NULL); /* userdata for callback */
1745 g_free(phone_number);
1746 g_free(server_alias);
1747 g_free(email);
1748 g_free(device_name);
1750 return TRUE;
1753 #define SIPE_SOAP_SEARCH_ROW "<m:row m:attrib=\"%s\" m:value=\"%s\"/>"
1756 * AD search first, LDAP based
1758 void sipe_get_info(PurpleConnection *gc, const char *username)
1760 struct sipe_core_private *sipe_private = PURPLE_GC_TO_SIPE_CORE_PRIVATE;
1761 char *row = g_markup_printf_escaped(SIPE_SOAP_SEARCH_ROW, "msRTCSIP-PrimaryUserAddress", username);
1762 struct transaction_payload *payload = g_new0(struct transaction_payload, 1);
1764 payload->destroy = g_free;
1765 payload->data = g_strdup(username);
1767 SIPE_DEBUG_INFO("sipe_get_info: row: %s", row ? row : "");
1768 sip_soap_directory_search(sipe_private,
1770 row,
1771 process_get_info_response,
1772 payload);
1773 g_free(row);
1777 Local Variables:
1778 mode: c
1779 c-file-style: "bsd"
1780 indent-tabs-mode: t
1781 tab-width: 8
1782 End: