pidgin/pidgin

closing merged branch
port-changes-from-branch-2.x.y-to-default
2020-02-03, Gary Kramlich
2f836435c33c
closing merged branch
/*
* novell.c
*
* Copyright (c) 2004 Novell, Inc. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*
*/
#include "internal.h"
#include "action.h"
#include "debug.h"
#include "plugins.h"
#include "server.h"
#include "nmuser.h"
#include "notify.h"
#include "novell.h"
#include "purple-gio.h"
#include "purpleaccountoption.h"
#include "util.h"
#include "request.h"
#include "network.h"
#include "status.h"
#include "version.h"
#define DEFAULT_PORT 8300
#define NOVELL_CONNECT_STEPS 4
#define NM_ROOT_FOLDER_NAME "GroupWise Messenger"
#define NOVELL_STATUS_TYPE_AVAILABLE "available"
#define NOVELL_STATUS_TYPE_AWAY "away"
#define NOVELL_STATUS_TYPE_BUSY "busy"
#define NOVELL_STATUS_TYPE_OFFLINE "offline"
#define NOVELL_STATUS_TYPE_IDLE "idle"
#define NOVELL_STATUS_TYPE_APPEAR_OFFLINE "appearoffline"
static PurpleProtocol *my_protocol = NULL;
static gboolean
_is_disconnect_error(NMERR_T err);
static gboolean
_check_for_disconnect(NMUser * user, NMERR_T err);
static void
_send_message(NMUser * user, NMMessage * message);
static void
_update_buddy_status(NMUser *user, PurpleBuddy * buddy, int status, int gmt);
static void
_remove_purple_buddies(NMUser * user);
static void
_add_contacts_to_purple_blist(NMUser * user, NMFolder * folder);
static void
_add_purple_buddies(NMUser * user);
static void
_sync_contact_list(NMUser *user);
static void
_sync_privacy_lists(NMUser *user);
static void
_show_info(PurpleConnection * gc, NMUserRecord * user_record, char * name);
const char *
_get_conference_name(int id);
/*******************************************************************************
* Response callbacks
*******************************************************************************/
/* Handle login response */
static void
_login_resp_cb(NMUser * user, NMERR_T ret_code,
gpointer resp_data, gpointer user_data)
{
PurpleConnection *gc;
const char *alias;
NMERR_T rc;
if (user == NULL)
return;
gc = purple_account_get_connection(user->client_data);
if (gc == NULL)
return;
if (ret_code == NM_OK) {
/* Set alias for user if not set (use Full Name) */
alias = purple_account_get_private_alias(user->client_data);
if (alias == NULL || *alias == '\0') {
alias = nm_user_record_get_full_name(user->user_record);
if (alias)
purple_account_set_private_alias(user->client_data, alias);
}
/* Tell Purple that we are connected */
purple_connection_set_state(gc, PURPLE_CONNECTION_CONNECTED);
_sync_contact_list(user);
rc = nm_send_set_status(user, NM_STATUS_AVAILABLE, NULL, NULL, NULL,
NULL);
_check_for_disconnect(user, rc);
} else {
PurpleConnectionError reason;
char *err = g_strdup_printf(_("Unable to login: %s"),
nm_error_to_string (ret_code));
switch (ret_code) {
case NMERR_AUTHENTICATION_FAILED:
case NMERR_CREDENTIALS_MISSING:
case NMERR_PASSWORD_INVALID:
/* Don't attempt to auto-reconnect if our
* password was invalid.
*/
if (!purple_account_get_remember_password(purple_connection_get_account(gc)))
purple_account_set_password(purple_connection_get_account(gc), NULL, NULL, NULL);
reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED;
break;
default:
/* FIXME: There are other reasons login could fail */
reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
}
purple_connection_error(gc, reason, err);
g_free(err);
}
}
/* Handle getstatus response*/
static void
_get_status_resp_cb(NMUser * user, NMERR_T ret_code,
gpointer resp_data, gpointer user_data)
{
PurpleBuddy *buddy;
GSList *buddies;
GSList *bnode;
NMUserRecord *user_record = (NMUserRecord *) resp_data;
int status;
if (user == NULL || user_record == NULL)
return;
if (ret_code == NM_OK) {
/* Find all Purple buddies and update their statuses */
const char *name = nm_user_record_get_display_id(user_record);
if (name) {
buddies = purple_blist_find_buddies((PurpleAccount *) user->client_data, name);
for (bnode = buddies; bnode; bnode = bnode->next) {
buddy = (PurpleBuddy *) bnode->data;
if (buddy) {
status = nm_user_record_get_status(user_record);
_update_buddy_status(user, buddy, status, time(0));
}
}
g_slist_free(buddies);
}
} else {
purple_debug(PURPLE_DEBUG_INFO, "novell",
"_get_status_resp_cb(): rc = 0x%X\n", ret_code);
}
}
/* Show an error if the rename failed */
static void
_rename_contact_resp_cb(NMUser * user, NMERR_T ret_code,
gpointer resp_data, gpointer user_data)
{
if (ret_code != NM_OK) {
purple_debug(PURPLE_DEBUG_INFO, "novell",
"_rename_contact_resp_cb(): rc = 0x%X\n", ret_code);
}
}
/* Handle the getdetails response and send the message */
static void
_get_details_resp_send_msg(NMUser * user, NMERR_T ret_code,
gpointer resp_data, gpointer user_data)
{
PurpleConversation *gconv;
PurpleConnection *gc;
NMUserRecord *user_record = NULL;
NMContact *cntct = NULL;
NMConference *conf;
NMMessage *msg = user_data;
const char *dn = NULL;
const char *name;
if (user == NULL || msg == NULL)
return;
if (ret_code == NM_OK) {
user_record = (NMUserRecord *) resp_data;
if (user_record) {
/* Set the title for the conversation */
/* XXX - Should this be find_im_with_account? */
gconv = purple_conversations_find_with_account(nm_user_record_get_display_id(user_record),
(PurpleAccount *) user->client_data);
if (gconv) {
dn = nm_user_record_get_dn(user_record);
if (dn) {
cntct = nm_find_contact(user, dn);
}
if (cntct) {
purple_conversation_set_title(gconv,
nm_contact_get_display_name(cntct));
} else {
/* Not in the contact list, try to user full name */
name = (char *) nm_user_record_get_full_name(user_record);
if (name)
purple_conversation_set_title(gconv, name);
}
}
/* Add the user record to particpant list */
conf = nm_message_get_conference(msg);
if (conf) {
nm_conference_add_participant(conf, user_record);
_send_message(user, msg);
}
}
} else {
gc = purple_account_get_connection(user->client_data);
if (gc != NULL) {
char *err = g_strdup_printf(_("Unable to send message."
" Could not get details for user (%s)."),
nm_error_to_string (ret_code));
purple_notify_error(gc, NULL, err, NULL,
purple_request_cpar_from_connection(gc));
g_free(err);
}
nm_release_message(msg);
}
}
/* Set up the new PurpleBuddy based on the response from getdetails */
static void
_get_details_resp_setup_buddy(NMUser * user, NMERR_T ret_code,
gpointer resp_data, gpointer user_data)
{
NMUserRecord *user_record;
NMContact *contact;
PurpleBuddy *buddy;
const char *alias;
NMERR_T rc = NM_OK;
if (user == NULL || resp_data == NULL || user_data == NULL)
return;
contact = user_data;
if (ret_code == NM_OK) {
user_record = resp_data;
buddy = nm_contact_get_data(contact);
nm_contact_set_user_record(contact, user_record);
/* Set the display id */
purple_buddy_set_name(buddy,
nm_user_record_get_display_id(user_record));
alias = purple_buddy_get_alias(buddy);
if (alias == NULL || *alias == '\0' || purple_strequal(alias, purple_buddy_get_name(buddy))) {
purple_buddy_set_local_alias(buddy,
nm_user_record_get_full_name(user_record));
/* Tell the server about the new display name */
rc = nm_send_rename_contact(user, contact,
nm_user_record_get_full_name(user_record),
NULL, NULL);
_check_for_disconnect(user, rc);
}
/* Get initial status for the buddy */
rc = nm_send_get_status(user, resp_data, _get_status_resp_cb, NULL);
_check_for_disconnect(user, rc);
/* nm_release_contact(contact);*/
}
nm_release_contact(contact);
}
/* Add the new contact into the PurpleBuddy list */
static void
_create_contact_resp_cb(NMUser * user, NMERR_T ret_code,
gpointer resp_data, gpointer user_data)
{
NMContact *tmp_contact = (NMContact *) user_data;
NMContact *new_contact = NULL;
NMFolder *folder = NULL;
PurpleGroup *group;
PurpleBuddy *buddy;
const char *folder_name = NULL;
NMERR_T rc = NM_OK;
if (user == NULL)
return;
if (ret_code == NM_OK) {
new_contact = (NMContact *) resp_data;
if (new_contact == NULL || tmp_contact == NULL)
return;
/* Get the userid and folder name for the new contact */
folder = nm_find_folder_by_id(user,
nm_contact_get_parent_id(new_contact));
if (folder) {
folder_name = nm_folder_get_name(folder);
}
if (folder_name == NULL || *folder_name == '\0')
folder_name = NM_ROOT_FOLDER_NAME;
/* Re-add the buddy now that we got the okay from the server */
group = purple_blist_find_group(folder_name);
if (group) {
const char *alias = nm_contact_get_display_name(tmp_contact);
const char *display_id = nm_contact_get_display_id(new_contact);
if (display_id == NULL)
display_id = nm_contact_get_dn(new_contact);
if (alias && !purple_strequal(alias, display_id)) {
/* The user requested an alias, tell the server about it. */
rc = nm_send_rename_contact(user, new_contact, alias,
_rename_contact_resp_cb, NULL);
_check_for_disconnect(user, rc);
} else {
alias = "";
}
/* Add it to the purple buddy list if it is not there */
buddy = purple_blist_find_buddy_in_group(user->client_data, display_id, group);
if (buddy == NULL) {
buddy = purple_buddy_new(user->client_data, display_id, alias);
purple_blist_add_buddy(buddy, NULL, group, NULL);
}
/* Save the new buddy as part of the contact object */
nm_contact_set_data(new_contact, (gpointer) buddy);
/* We need details for the user before we can setup the
* new Purple buddy. We always call this because the
* 'createcontact' response fields do not always contain
* everything that we need.
*/
nm_contact_add_ref(new_contact);
rc = nm_send_get_details(user, nm_contact_get_dn(new_contact),
_get_details_resp_setup_buddy, new_contact);
_check_for_disconnect(user, rc);
}
} else {
PurpleConnection *gc = purple_account_get_connection(user->client_data);
const char *name = nm_contact_get_dn(tmp_contact);
char *err;
err =
g_strdup_printf(_("Unable to add %s to your buddy list (%s)."),
name, nm_error_to_string (ret_code));
purple_notify_error(gc, NULL, err, NULL,
purple_request_cpar_from_connection(gc));
g_free(err);
}
if (tmp_contact)
nm_release_contact(tmp_contact);
}
/* Show an error if we failed to send the message */
static void
_send_message_resp_cb(NMUser * user, NMERR_T ret_code,
gpointer resp_data, gpointer user_data)
{
PurpleConnection *gc;
char *err = NULL;
if (user == NULL)
return;
if (ret_code != NM_OK) {
gc = purple_account_get_connection(user->client_data);
/* TODO: Improve this! message to who or for what conference? */
err = g_strdup_printf(_("Unable to send message (%s)."),
nm_error_to_string (ret_code));
purple_notify_error(gc, NULL, err, NULL,
purple_request_cpar_from_connection(gc));
g_free(err);
}
}
/* Show an error if the remove failed */
static void
_remove_contact_resp_cb(NMUser * user, NMERR_T ret_code,
gpointer resp_data, gpointer user_data)
{
if (ret_code != NM_OK) {
/* TODO: Display an error? */
purple_debug(PURPLE_DEBUG_INFO, "novell",
"_remove_contact_resp_cb(): rc = 0x%x\n", ret_code);
}
}
/* Show an error if the remove failed */
static void
_remove_folder_resp_cb(NMUser * user, NMERR_T ret_code,
gpointer resp_data, gpointer user_data)
{
if (ret_code != NM_OK) {
/* TODO: Display an error? */
purple_debug(PURPLE_DEBUG_INFO, "novell",
"_remove_folder_resp_cb(): rc = 0x%x\n", ret_code);
}
}
/* Show an error if the move failed */
static void
_move_contact_resp_cb(NMUser * user, NMERR_T ret_code,
gpointer resp_data, gpointer user_data)
{
if (ret_code != NM_OK) {
/* TODO: Display an error? */
purple_debug(PURPLE_DEBUG_INFO, "novell",
"_move_contact_resp_cb(): rc = 0x%x\n", ret_code);
}
}
/* Show an error if the rename failed */
static void
_rename_folder_resp_cb(NMUser * user, NMERR_T ret_code,
gpointer resp_data, gpointer user_data)
{
if (ret_code != NM_OK) {
/* TODO: Display an error? */
purple_debug(PURPLE_DEBUG_INFO, "novell",
"_rename_folder_resp_cb(): rc = 0x%x\n", ret_code);
}
}
static void
_sendinvite_resp_cb(NMUser *user, NMERR_T ret_code,
gpointer resp_data, gpointer user_data)
{
char *err;
PurpleConnection *gc;
if (user == NULL)
return;
if (ret_code != NM_OK) {
gc = purple_account_get_connection(user->client_data);
err = g_strdup_printf(_("Unable to invite user (%s)."), nm_error_to_string(ret_code));
purple_notify_error(gc, NULL, err, NULL,
purple_request_cpar_from_connection(gc));
g_free(err);
purple_debug(PURPLE_DEBUG_INFO, "novell",
"_sendinvite_resp_cb(): rc = 0x%x\n", ret_code);
}
}
/* If the createconf was successful attempt to send the message,
* otherwise display an error message to the user.
*/
static void
_createconf_resp_send_msg(NMUser * user, NMERR_T ret_code,
gpointer resp_data, gpointer user_data)
{
NMConference *conf;
NMMessage *msg = user_data;
if (user == NULL || msg == NULL)
return;
if (ret_code == NM_OK) {
_send_message(user, msg);
} else {
if ((conf = nm_message_get_conference(msg))) {
PurpleConnection *gc = purple_account_get_connection(user->client_data);
const char *name = NULL;
char *err;
NMUserRecord *ur;
ur = nm_conference_get_participant(conf, 0);
if (ur)
name = nm_user_record_get_userid(ur);
if (name)
err = g_strdup_printf(_("Unable to send message to %s."
" Could not create the conference (%s)."),
name,
nm_error_to_string (ret_code));
else
err = g_strdup_printf(_("Unable to send message."
" Could not create the conference (%s)."),
nm_error_to_string (ret_code));
purple_notify_error(gc, NULL, err, NULL,
purple_request_cpar_from_connection(gc));
g_free(err);
}
nm_release_message(msg);
}
}
/* Move contact to newly created folder */
static void
_create_folder_resp_move_contact(NMUser * user, NMERR_T ret_code,
gpointer resp_data, gpointer user_data)
{
NMContact *contact = user_data;
NMFolder *new_folder;
char *folder_name = resp_data;
NMERR_T rc = NM_OK;
if (user == NULL || folder_name == NULL || contact == NULL) {
g_free(folder_name);
return;
}
if (ret_code == NM_OK || ret_code == NMERR_DUPLICATE_FOLDER) {
new_folder = nm_find_folder(user, folder_name);
if (new_folder) {
/* Tell the server to move the contact to the new folder */
/* rc = nm_send_move_contact(user, contact, new_folder,
_move_contact_resp_cb, NULL); */
rc = nm_send_create_contact(user, new_folder, contact,
NULL, NULL);
_check_for_disconnect(user, rc);
}
} else {
PurpleConnection *gc = purple_account_get_connection(user->client_data);
char *err = g_strdup_printf(_("Unable to move user %s"
" to folder %s in the server side list."
" Error while creating folder (%s)."),
nm_contact_get_dn(contact),
folder_name,
nm_error_to_string (ret_code));
purple_notify_error(gc, NULL, err, NULL,
purple_request_cpar_from_connection(gc));
g_free(err);
}
g_free(folder_name);
}
/* Add contact to newly create folder */
static void
_create_folder_resp_add_contact(NMUser * user, NMERR_T ret_code,
gpointer resp_data, gpointer user_data)
{
NMContact *contact = (NMContact *) user_data;
NMFolder *folder;
char *folder_name = (char *) resp_data;
NMERR_T rc = NM_OK;
if (user == NULL || folder_name == NULL || contact == NULL) {
if (contact)
nm_release_contact(contact);
g_free(folder_name);
return;
}
if (ret_code == NM_OK || ret_code == NMERR_DUPLICATE_FOLDER) {
folder = nm_find_folder(user, folder_name);
if (folder) {
rc = nm_send_create_contact(user, folder, contact,
_create_contact_resp_cb, contact);
_check_for_disconnect(user, rc);
}
} else {
PurpleConnection *gc = purple_account_get_connection(user->client_data);
const char *name = nm_contact_get_dn(contact);
char *err =
g_strdup_printf(_("Unable to add %s to your buddy list."
" Error creating folder in server side list (%s)."),
name, nm_error_to_string (ret_code));
purple_notify_error(gc, NULL, err, NULL,
purple_request_cpar_from_connection(gc));
nm_release_contact(contact);
g_free(err);
}
g_free(folder_name);
}
static void
_join_conf_resp_cb(NMUser * user, NMERR_T ret_code,
gpointer resp_data, gpointer user_data)
{
PurpleChatConversation *chat;
PurpleConnection *gc;
NMUserRecord *ur;
NMConference *conference = user_data;
const char *name, *conf_name;
int i, count;
if (user == NULL || conference == NULL)
return;
gc = purple_account_get_connection(user->client_data);
if (ret_code == NM_OK) {
conf_name = _get_conference_name(++user->conference_count);
chat = purple_serv_got_joined_chat(gc, user->conference_count, conf_name);
if (chat) {
nm_conference_set_data(conference, (gpointer) chat);
count = nm_conference_get_participant_count(conference);
for (i = 0; i < count; i++) {
ur = nm_conference_get_participant(conference, i);
if (ur) {
name = nm_user_record_get_display_id(ur);
purple_chat_conversation_add_user(chat, name, NULL,
PURPLE_CHAT_USER_NONE, TRUE);
}
}
}
}
}
/* Show info returned by getdetails */
static void
_get_details_resp_show_info(NMUser * user, NMERR_T ret_code,
gpointer resp_data, gpointer user_data)
{
PurpleConnection *gc;
NMUserRecord *user_record;
char *name;
char *err;
if (user == NULL)
return;
name = user_data;
if (ret_code == NM_OK) {
user_record = (NMUserRecord *) resp_data;
if (user_record) {
_show_info(purple_account_get_connection(user->client_data),
user_record, g_strdup(name));
}
} else {
gc = purple_account_get_connection(user->client_data);
err =
g_strdup_printf(_("Could not get details for user %s (%s)."),
name, nm_error_to_string (ret_code));
purple_notify_error(gc, NULL, err, NULL,
purple_request_cpar_from_connection(gc));
g_free(err);
}
g_free(name);
}
/* Handle get details response add to privacy list */
static void
_get_details_resp_add_privacy_item(NMUser *user, NMERR_T ret_code,
gpointer resp_data, gpointer user_data)
{
PurpleConnection *gc;
PurpleAccount *account;
NMUserRecord *user_record = resp_data;
char *err;
gboolean allowed = GPOINTER_TO_INT(user_data);
const char *display_id;
if (user == NULL)
return;
gc = purple_account_get_connection(user->client_data);
display_id = nm_user_record_get_display_id(user_record);
account = purple_connection_get_account(gc);
if (ret_code == NM_OK) {
if (allowed) {
if (!g_slist_find_custom(purple_account_privacy_get_denied(account),
display_id, (GCompareFunc)purple_utf8_strcasecmp)) {
purple_account_privacy_permit_add(account, display_id, TRUE);
}
} else {
if (!g_slist_find_custom(purple_account_privacy_get_denied(account),
display_id, (GCompareFunc)purple_utf8_strcasecmp)) {
purple_account_privacy_deny_add(account, display_id, TRUE);
}
}
} else {
err = g_strdup_printf(_("Unable to add user to privacy list (%s)."),
nm_error_to_string(ret_code));
purple_notify_error(gc, NULL, err, NULL,
purple_request_cpar_from_connection(gc));
g_free(err);
}
}
/* Handle response to create privacy item request */
static void
_create_privacy_item_deny_resp_cb(NMUser *user, NMERR_T ret_code,
gpointer resp_data, gpointer user_data)
{
PurpleConnection *gc;
PurpleAccount *account;
NMUserRecord *user_record;
char *who = user_data;
char *err;
NMERR_T rc = NM_OK;
const char *display_id = NULL;
if (user == NULL)
return;
gc = purple_account_get_connection(user->client_data);
account = purple_connection_get_account(gc);
if (ret_code == NM_OK) {
user_record = nm_find_user_record(user, who);
if (user_record)
display_id = nm_user_record_get_display_id(user_record);
if (display_id) {
if (!g_slist_find_custom(purple_account_privacy_get_denied(account),
display_id, (GCompareFunc)purple_utf8_strcasecmp)) {
purple_account_privacy_deny_add(account, display_id, TRUE);
}
} else {
rc = nm_send_get_details(user, who,
_get_details_resp_add_privacy_item,
GINT_TO_POINTER(FALSE));
_check_for_disconnect(user, rc);
}
} else {
err = g_strdup_printf(_("Unable to add %s to deny list (%s)."),
who, nm_error_to_string(ret_code));
purple_notify_error(gc, NULL, err, NULL,
purple_request_cpar_from_connection(gc));
g_free(err);
}
g_free(who);
}
/* Handle response to create privacy item request */
static void
_create_privacy_item_permit_resp_cb(NMUser *user, NMERR_T ret_code,
gpointer resp_data, gpointer user_data)
{
PurpleConnection *gc;
PurpleAccount *account;
NMUserRecord *user_record;
char *who = user_data;
char *err;
NMERR_T rc = NM_OK;
const char *display_id = NULL;
if (user == NULL)
return;
gc = purple_account_get_connection(user->client_data);
account = purple_connection_get_account(gc);
if (ret_code == NM_OK) {
user_record = nm_find_user_record(user, who);
if (user_record)
display_id = nm_user_record_get_display_id(user_record);
if (display_id) {
if (!g_slist_find_custom(purple_account_privacy_get_permitted(account),
display_id,
(GCompareFunc)purple_utf8_strcasecmp)) {
purple_account_privacy_permit_add(account, display_id, TRUE);
}
} else {
rc = nm_send_get_details(user, who,
_get_details_resp_add_privacy_item,
GINT_TO_POINTER(TRUE));
_check_for_disconnect(user, rc);
}
} else {
err = g_strdup_printf(_("Unable to add %s to permit list (%s)."), who,
nm_error_to_string(ret_code));
purple_notify_error(gc, NULL, err, NULL,
purple_request_cpar_from_connection(gc));
g_free(err);
}
g_free(who);
}
static void
_get_details_send_privacy_create(NMUser *user, NMERR_T ret_code,
gpointer resp_data, gpointer user_data)
{
NMERR_T rc = NM_OK;
PurpleConnection *gc;
NMUserRecord *user_record = resp_data;
char *err;
gboolean allowed = GPOINTER_TO_INT(user_data);
const char *dn, *display_id;
if (user == NULL)
return;
gc = purple_account_get_connection(user->client_data);
dn = nm_user_record_get_dn(user_record);
display_id = nm_user_record_get_display_id(user_record);
if (ret_code == NM_OK) {
if (allowed) {
rc = nm_send_create_privacy_item(user, dn, TRUE,
_create_privacy_item_permit_resp_cb,
g_strdup(display_id));
_check_for_disconnect(user, rc);
} else {
rc = nm_send_create_privacy_item(user, dn, FALSE,
_create_privacy_item_deny_resp_cb,
g_strdup(display_id));
_check_for_disconnect(user, rc);
}
} else {
err = g_strdup_printf(_("Unable to add user to privacy list (%s)."),
nm_error_to_string(ret_code));
purple_notify_error(gc, NULL, err, NULL,
purple_request_cpar_from_connection(gc));
g_free(err);
}
}
static void
_remove_privacy_item_resp_cb(NMUser *user, NMERR_T ret_code,
gpointer resp_data, gpointer user_data)
{
PurpleConnection *gc;
char *who = user_data;
char *err;
if (user == NULL)
return;
if (ret_code != NM_OK) {
gc = purple_account_get_connection(user->client_data);
err = g_strdup_printf(_("Unable to remove %s from privacy list (%s)."), who,
nm_error_to_string(ret_code));
purple_notify_error(gc, NULL, err, NULL,
purple_request_cpar_from_connection(gc));
g_free(err);
}
g_free(who);
}
static void
_set_privacy_default_resp_cb(NMUser *user, NMERR_T ret_code,
gpointer resp_data, gpointer user_data)
{
PurpleConnection *gc;
char *err;
if (user == NULL)
return;
if (ret_code != NM_OK) {
gc = purple_account_get_connection(user->client_data);
err = g_strdup_printf(_("Unable to change server side privacy settings (%s)."),
nm_error_to_string(ret_code));
purple_notify_error(gc, NULL, err, NULL,
purple_request_cpar_from_connection(gc));
g_free(err);
}
}
/* Handle get details response add to privacy list */
static void
_get_details_resp_send_invite(NMUser *user, NMERR_T ret_code,
gpointer resp_data, gpointer user_data)
{
NMERR_T rc = NM_OK;
PurpleConnection *gc;
NMUserRecord *user_record = resp_data;
char *err;
GSList *cnode;
NMConference *conference;
gpointer chat;
int id = GPOINTER_TO_INT(user_data);
if (user == NULL)
return;
gc = purple_account_get_connection(user->client_data);
if (ret_code == NM_OK) {
for (cnode = user->conferences; cnode != NULL; cnode = cnode->next) {
conference = cnode->data;
if (conference && (chat = nm_conference_get_data(conference))) {
if (purple_chat_conversation_get_id(PURPLE_CHAT_CONVERSATION(chat)) == id) {
rc = nm_send_conference_invite(user, conference, user_record,
NULL, _sendinvite_resp_cb, NULL);
_check_for_disconnect(user, rc);
break;
}
}
}
} else {
err = g_strdup_printf(_("Unable to invite user (%s)."), nm_error_to_string(ret_code));
purple_notify_error(gc, NULL, err, NULL,
purple_request_cpar_from_connection(gc));
g_free(err);
}
}
static void
_createconf_resp_send_invite(NMUser * user, NMERR_T ret_code,
gpointer resp_data, gpointer user_data)
{
NMERR_T rc = NM_OK;
NMConference *conference = resp_data;
NMUserRecord *user_record = user_data;
PurpleConnection *gc;
char *err;
if (user == NULL)
return;
if (ret_code == NM_OK) {
rc = nm_send_conference_invite(user, conference, user_record,
NULL, _sendinvite_resp_cb, NULL);
_check_for_disconnect(user, rc);
} else {
err = g_strdup_printf(_("Unable to create conference (%s)."), nm_error_to_string(ret_code));
gc = purple_account_get_connection(user->client_data);
purple_notify_error(gc, NULL, err, NULL,
purple_request_cpar_from_connection(gc));
g_free(err);
}
}
/*******************************************************************************
* Helper functions
******************************************************************************/
static char *
_user_agent_string(void)
{
#if !defined(_WIN32)
const char *sysname = "";
const char *release = "";
struct utsname u;
if (uname(&u) == 0) {
sysname = u.sysname;
release = u.release;
} else {
sysname = "Linux";
release = "Unknown";
}
return g_strdup_printf("Purple/%s (%s; %s)", VERSION, sysname, release);
#else
const char *sysname = "";
OSVERSIONINFO os_info;
SYSTEM_INFO sys_info;
os_info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx(&os_info);
GetSystemInfo(&sys_info);
if (os_info.dwPlatformId == VER_PLATFORM_WIN32_NT) {
switch (os_info.dwMajorVersion) {
case 3:
case 4:
sysname = "Windows NT";
break;
case 5:
switch (os_info.dwMinorVersion) {
case 0:
sysname = "Windows 2000";
break;
case 1:
sysname = "Windows XP";
break;
case 2:
sysname = "Windows Server 2003";
break;
default:
sysname = "Windows";
break;
}
break;
default:
sysname = "Windows";
break;
}
} else if (os_info.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
switch (os_info.dwMinorVersion) {
case 0:
sysname = "Windows 95";
break;
case 10:
sysname = "Windows 98";
break;
case 90:
sysname = "Windows ME";
break;
default:
sysname = "Windows";
break;
}
} else {
sysname = "Windows";
}
return g_strdup_printf("Purple/%s (%s; %ld.%ld)", VERSION, sysname,
os_info.dwMajorVersion, os_info.dwMinorVersion);
#endif
}
static gboolean
_is_disconnect_error(NMERR_T err)
{
return (err == NMERR_TCP_WRITE ||
err == NMERR_TCP_READ || err == NMERR_PROTOCOL);
}
static gboolean
_check_for_disconnect(NMUser * user, NMERR_T err)
{
PurpleConnection *gc = purple_account_get_connection(user->client_data);
if (_is_disconnect_error(err)) {
purple_connection_error(gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
_("Error communicating with server. Closing connection."));
return TRUE;
}
return FALSE;
}
/* Check to see if the conference is instantiated, if so send the message.
* If not send the create conference -- the response handler for the createconf
* will call this function again.
*/
static void
_send_message(NMUser * user, NMMessage * message)
{
NMConference *conf;
NMERR_T rc = NM_OK;
conf = nm_message_get_conference(message);
if (conf) {
/* We have a conference make sure that the
server knows about it already. */
if (nm_conference_is_instantiated(conf)) {
/* We have everything that we need...finally! */
rc = nm_send_message(user, message, _send_message_resp_cb);
_check_for_disconnect(user, rc);
nm_release_message(message);
} else {
rc = nm_send_create_conference(user, conf, _createconf_resp_send_msg, message);
_check_for_disconnect(user, rc);
}
}
}
/*
* Update the status of the given buddy in the Purple buddy list
*/
static void
_update_buddy_status(NMUser *user, PurpleBuddy * buddy, int novellstatus, int gmt)
{
PurpleAccount *account;
const char *status_id;
const char *text = NULL;
const char *dn;
const char *name;
int idle = 0;
account = purple_buddy_get_account(buddy);
name = purple_buddy_get_name(buddy);
switch (novellstatus) {
case NM_STATUS_AVAILABLE:
status_id = NOVELL_STATUS_TYPE_AVAILABLE;
break;
case NM_STATUS_AWAY:
status_id = NOVELL_STATUS_TYPE_AWAY;
break;
case NM_STATUS_BUSY:
status_id = NOVELL_STATUS_TYPE_BUSY;
break;
case NM_STATUS_OFFLINE:
status_id = NOVELL_STATUS_TYPE_OFFLINE;
break;
case NM_STATUS_AWAY_IDLE:
status_id = NOVELL_STATUS_TYPE_AWAY;
idle = gmt;
break;
default:
status_id = NOVELL_STATUS_TYPE_OFFLINE;
break;
}
/* Get status text for the user */
dn = nm_lookup_dn(user, name);
if (dn) {
NMUserRecord *user_record = nm_find_user_record(user, dn);
if (user_record) {
text = nm_user_record_get_status_text(user_record);
}
}
purple_protocol_got_user_status(account, name, status_id,
"message", text, NULL);
purple_protocol_got_user_idle(account, name,
(novellstatus == NM_STATUS_AWAY_IDLE), idle);
}
/* Iterate through the cached Purple buddy list and remove buddies
* that are not in the server side list.
*/
static void
_remove_purple_buddies(NMUser *user)
{
PurpleBlistNode *gnode;
PurpleBlistNode *cnode;
PurpleBlistNode *bnode;
PurpleGroup *group;
PurpleBuddy *buddy;
GSList *rem_list = NULL;
NMFolder *folder = NULL;
const char *gname = NULL;
for (gnode = purple_blist_get_default_root(); gnode;
gnode = purple_blist_node_get_sibling_next(gnode)) {
if (!PURPLE_IS_GROUP(gnode))
continue;
group = (PurpleGroup *) gnode;
gname = purple_group_get_name(group);
for (cnode = purple_blist_node_get_first_child(gnode);
cnode;
cnode = purple_blist_node_get_sibling_next(cnode)) {
if (!PURPLE_IS_CONTACT(cnode))
continue;
for (bnode = purple_blist_node_get_first_child(cnode);
bnode;
bnode = purple_blist_node_get_sibling_next(bnode)) {
if (!PURPLE_IS_BUDDY(bnode))
continue;
buddy = (PurpleBuddy *) bnode;
if (purple_buddy_get_account(buddy) == user->client_data) {
if (purple_strequal(gname, NM_ROOT_FOLDER_NAME))
gname = "";
folder = nm_find_folder(user, gname);
if (folder == NULL ||
!nm_folder_find_contact_by_display_id(folder, purple_buddy_get_name(buddy))) {
rem_list = g_slist_append(rem_list, buddy);
}
}
}
}
}
g_slist_free_full(rem_list, (GDestroyNotify)purple_blist_remove_buddy);
}
/* Add all of the contacts in the given folder to the Purple buddy list */
static void
_add_contacts_to_purple_blist(NMUser * user, NMFolder * folder)
{
NMUserRecord *user_record = NULL;
NMContact *contact = NULL;
PurpleBuddy *buddy = NULL;
PurpleGroup *group;
NMERR_T cnt = 0, i;
const char *name = NULL;
const char *fname = NULL;
int status = 0;
/* If this is the root folder give it a name. Purple does not have the concept of
* a root folder.
*/
fname = nm_folder_get_name(folder);
if (fname == NULL || *fname == '\0') {
fname = NM_ROOT_FOLDER_NAME;
}
/* Does the Purple group exist already? */
group = purple_blist_find_group(fname);
if (group == NULL) {
group = purple_group_new(fname);
purple_blist_add_group(group, NULL);
}
/* Get each contact for this folder */
cnt = nm_folder_get_contact_count(folder);
for (i = 0; i < cnt; i++) {
contact = nm_folder_get_contact(folder, i);
if (contact) {
name = nm_contact_get_display_id(contact);
if (name) {
buddy = purple_blist_find_buddy_in_group(user->client_data, name, group);
if (buddy == NULL) {
/* Add it to the purple buddy list */
buddy = purple_buddy_new(user->client_data,
name,
nm_contact_get_display_name(contact));
purple_blist_add_buddy(buddy, NULL, group, NULL);
}
/* Set the initial status for the buddy */
user_record = nm_contact_get_user_record(contact);
if (user_record) {
status = nm_user_record_get_status(user_record);
}
_update_buddy_status(user, buddy, status, time(0));
/* Save the new buddy as part of the contact object */
nm_contact_set_data(contact, (gpointer) buddy);
}
} else {
/* NULL contact. This should not happen, but
* let's break out of the loop.
*/
break;
}
}
}
/* Add all of the server side contacts to the Purple buddy list. */
static void
_add_purple_buddies(NMUser * user)
{
int cnt = 0, i;
NMFolder *root_folder = NULL;
NMFolder *folder = NULL;
root_folder = nm_get_root_folder(user);
if (root_folder) {
/* Add sub-folders and contacts to sub-folders...
* iterate throught the sub-folders in reverse order
* because Purple adds the folders to the front -- so we
* want to add the first folder last
*/
cnt = nm_folder_get_subfolder_count(root_folder);
for (i = cnt-1; i >= 0; i--) {
folder = nm_folder_get_subfolder(root_folder, i);
if (folder) {
_add_contacts_to_purple_blist(user, folder);
}
}
/* Add contacts for the root folder */
_add_contacts_to_purple_blist(user, root_folder);
}
}
static void
_sync_contact_list(NMUser *user)
{
/* Remove all buddies from the local list that are
* not in the server side list and add all buddies
* from the server side list that are not in
* the local list
*/
_remove_purple_buddies(user);
_add_purple_buddies(user);
user->clist_synched = TRUE;
}
static void
_sync_privacy_lists(NMUser *user)
{
GSList *node = NULL, *rem_list = NULL;
PurpleConnection *gc;
PurpleAccount *account;
const char *name, *dn;
NMUserRecord *user_record;
if (user == NULL)
return;
gc = purple_account_get_connection(user->client_data);
if (gc == NULL)
return;
account = purple_connection_get_account(gc);
/* Set the Purple privacy setting */
if (user->default_deny) {
if (user->allow_list == NULL) {
purple_account_set_privacy_type(account, PURPLE_ACCOUNT_PRIVACY_DENY_ALL);
} else {
purple_account_set_privacy_type(account, PURPLE_ACCOUNT_PRIVACY_ALLOW_USERS);
}
} else {
if (user->deny_list == NULL) {
purple_account_set_privacy_type(account, PURPLE_ACCOUNT_PRIVACY_ALLOW_ALL);
} else {
purple_account_set_privacy_type(account, PURPLE_ACCOUNT_PRIVACY_DENY_USERS);
}
}
/* Add stuff */
for (node = user->allow_list; node; node = node->next) {
user_record = nm_find_user_record(user, (char *)node->data);
if (user_record)
name = nm_user_record_get_display_id(user_record);
else
name =(char *)node->data;
if (!g_slist_find_custom(purple_account_privacy_get_permitted(account),
name, (GCompareFunc)purple_utf8_strcasecmp)) {
purple_account_privacy_permit_add(account, name , TRUE);
}
}
for (node = user->deny_list; node; node = node->next) {
user_record = nm_find_user_record(user, (char *)node->data);
if (user_record)
name = nm_user_record_get_display_id(user_record);
else
name =(char *)node->data;
if (!g_slist_find_custom(purple_account_privacy_get_denied(account),
name, (GCompareFunc)purple_utf8_strcasecmp)) {
purple_account_privacy_deny_add(account, name, TRUE);
}
}
/* Remove stuff */
for (node = purple_account_privacy_get_permitted(account); node; node = node->next) {
dn = nm_lookup_dn(user, (char *)node->data);
if (dn != NULL &&
!g_slist_find_custom(user->allow_list,
dn, (GCompareFunc)purple_utf8_strcasecmp)) {
rem_list = g_slist_append(rem_list, node->data);
}
}
if (rem_list) {
for (node = rem_list; node; node = node->next) {
purple_account_privacy_permit_remove(account, (char *)node->data, TRUE);
}
g_slist_free(rem_list);
rem_list = NULL;
}
for (node = purple_account_privacy_get_denied(account); node; node = node->next) {
dn = nm_lookup_dn(user, (char *)node->data);
if (dn != NULL &&
!g_slist_find_custom(user->deny_list,
dn, (GCompareFunc)purple_utf8_strcasecmp)) {
rem_list = g_slist_append(rem_list, node->data);
}
}
if (rem_list) {
for (node = rem_list; node; node = node->next) {
purple_account_privacy_deny_remove(account, (char *)node->data, TRUE);
}
g_slist_free(rem_list);
}
}
/* Map known property tags to user-friendly strings */
static const char *
_map_property_tag(const char *tag)
{
if (tag == NULL) return NULL;
if (purple_strequal(tag, "telephoneNumber"))
return _("Telephone Number");
else if (purple_strequal(tag, "L"))
return _("Location");
else if (purple_strequal(tag, "OU"))
return _("Department");
else if (purple_strequal(tag, "personalTitle"))
return _("Personal Title");
else if (purple_strequal(tag, "Title"))
return _("Job Title");
else if (purple_strequal(tag, "mailstop"))
return _("Mailstop");
else if (purple_strequal(tag, "Internet EMail Address"))
return _("Email Address");
else
return tag;
}
/* Display a dialog box showing the properties for the given user record */
static void
_show_info(PurpleConnection * gc, NMUserRecord * user_record, char * name)
{
PurpleNotifyUserInfo *user_info = purple_notify_user_info_new();
int count, i;
NMProperty *property;
const char *tag, *value;
tag = _("User ID");
value = nm_user_record_get_userid(user_record);
if (value) {
/* TODO: Check whether it's correct to call add_pair_html,
or if we should be using add_pair_plaintext */
purple_notify_user_info_add_pair_html(user_info, tag, value);
}
tag = _("Full name");
value = nm_user_record_get_full_name(user_record);
if (value) {
/* TODO: Check whether it's correct to call add_pair_html,
or if we should be using add_pair_plaintext */
purple_notify_user_info_add_pair_html(user_info, tag, value);
}
count = nm_user_record_get_property_count(user_record);
for (i = 0; i < count; i++) {
property = nm_user_record_get_property(user_record, i);
if (property) {
tag = _map_property_tag(nm_property_get_tag(property));
value = nm_property_get_value(property);
if (tag && value) {
/* TODO: Check whether it's correct to call add_pair_html,
or if we should be using add_pair_plaintext */
purple_notify_user_info_add_pair_html(user_info, tag, value);
}
nm_release_property(property);
}
}
purple_notify_userinfo(gc, name, user_info, NULL, NULL);
purple_notify_user_info_destroy(user_info);
g_free(name);
}
/* Send a join conference, the first item in the parms list is the
* NMUser object and the second item is the conference to join.
* This callback is passed to purple_request_action when we ask the
* user if they want to join the conference.
*/
static void
_join_conference_cb(GSList * parms)
{
NMUser *user;
NMConference *conference;
NMERR_T rc = NM_OK;
if (parms == NULL || g_slist_length(parms) != 2)
return;
user = g_slist_nth_data(parms, 0);
conference = g_slist_nth_data(parms, 1);
if (user && conference) {
rc = nm_send_join_conference(user, conference,
_join_conf_resp_cb, conference);
_check_for_disconnect(user, rc);
}
g_slist_free(parms);
}
/* Send a reject conference, the first item in the parms list is the
* NMUser object and the second item is the conference to reject.
* This callback is passed to purple_request_action when we ask the
* user if they want to joing the conference.
*/
static void
_reject_conference_cb(GSList * parms)
{
NMUser *user;
NMConference *conference;
NMERR_T rc = NM_OK;
if (parms == NULL || g_slist_length(parms) != 2)
return;
user = g_slist_nth_data(parms, 0);
conference = g_slist_nth_data(parms, 1);
if (user && conference) {
rc = nm_send_reject_conference(user, conference, NULL, NULL);
_check_for_disconnect(user, rc);
}
g_slist_free(parms);
}
static void
_initiate_conference_cb(PurpleBlistNode *node, gpointer ignored)
{
PurpleBuddy *buddy;
PurpleConnection *gc;
NMUser *user;
const char *conf_name;
PurpleChatConversation *chat = NULL;
NMUserRecord *user_record;
NMConference *conference;
g_return_if_fail(PURPLE_IS_BUDDY(node));
buddy = (PurpleBuddy *) node;
gc = purple_account_get_connection(purple_buddy_get_account(buddy));
user = purple_connection_get_protocol_data(gc);
if (user == NULL)
return;
/* We should already have a userrecord for the buddy */
user_record = nm_find_user_record(user, purple_buddy_get_name(buddy));
if (user_record == NULL)
return;
conf_name = _get_conference_name(++user->conference_count);
chat = purple_serv_got_joined_chat(gc, user->conference_count, conf_name);
if (chat) {
conference = nm_create_conference(NULL);
nm_conference_set_data(conference, (gpointer) chat);
nm_send_create_conference(user, conference, _createconf_resp_send_invite, user_record);
nm_release_conference(conference);
}
}
const char *
_get_conference_name(int id)
{
static char *name = NULL;
g_free(name);
name = g_strdup_printf(_("GroupWise Conference %d"), id);
return name;
}
static void
_show_privacy_locked_error(PurpleConnection *gc, NMUser *user)
{
char *err;
err = g_strdup_printf(_("Unable to change server side privacy settings (%s)."),
nm_error_to_string(NMERR_ADMIN_LOCKED));
purple_notify_error(gc, NULL, err, NULL,
purple_request_cpar_from_connection(gc));
g_free(err);
}
/*******************************************************************************
* Connect and recv callbacks
******************************************************************************/
static void
novell_ssl_recv_cb(GObject *stream, gpointer data)
{
PurpleConnection *gc = data;
NMUser *user;
NMERR_T rc;
if (gc == NULL)
return;
user = purple_connection_get_protocol_data(gc);
if (user == NULL)
return;
rc = nm_process_new_data(user);
if (rc != NM_OK) {
if (_is_disconnect_error(rc)) {
purple_connection_error(gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
_("Error communicating with server. Closing connection."));
} else {
purple_debug(PURPLE_DEBUG_INFO, "novell",
"Error processing event or response (%d).\n", rc);
}
}
}
static void
novell_login_callback(GObject *source_object, GAsyncResult *res, gpointer data)
{
GSocketClient *client = G_SOCKET_CLIENT(source_object);
PurpleConnection *gc = data;
GSocketConnection *sockconn;
NMUser *user;
NMConn *conn;
NMERR_T rc = 0;
const char *pwd = NULL;
gchar *my_addr = NULL;
char *ua = NULL;
GError *error = NULL;
sockconn = g_socket_client_connect_to_host_finish(client, res, &error);
if (sockconn == NULL) {
if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
g_error_free(error);
} else {
purple_connection_take_error(gc, error);
}
return;
}
user = purple_connection_get_protocol_data(gc);
if ((user == NULL) || (conn = user->conn) == NULL)
return;
purple_connection_update_progress(gc, _("Authenticating..."),
2, NOVELL_CONNECT_STEPS);
conn->stream = G_IO_STREAM(sockconn);
conn->input =
g_data_input_stream_new(g_io_stream_get_input_stream(conn->stream));
conn->output = g_io_stream_get_output_stream(conn->stream);
g_data_input_stream_set_byte_order(conn->input,
G_DATA_STREAM_BYTE_ORDER_LITTLE_ENDIAN);
g_data_input_stream_set_newline_type(conn->input,
G_DATA_STREAM_NEWLINE_TYPE_LF);
my_addr = purple_network_get_my_ip_from_gio(sockconn);
pwd = purple_connection_get_password(gc);
ua = _user_agent_string();
rc = nm_send_login(user, pwd, my_addr, ua, _login_resp_cb, NULL);
if (rc == NM_OK) {
GSource *source;
source = g_pollable_input_stream_create_source(
G_POLLABLE_INPUT_STREAM(conn->input), user->cancellable);
g_source_set_callback(source, (GSourceFunc)novell_ssl_recv_cb, gc,
NULL);
} else {
purple_connection_error(gc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
_("Unable to connect"));
}
purple_connection_update_progress(gc, _("Waiting for response..."),
3, NOVELL_CONNECT_STEPS);
g_free(ua);
g_free(my_addr);
}
/*******************************************************************************
* Event callback and event handlers
******************************************************************************/
static void
_evt_receive_message(NMUser * user, NMEvent * event)
{
NMUserRecord *user_record = NULL;
NMContact *contact = NULL;
PurpleIMConversation *im;
NMConference *conference;
PurpleMessageFlags flags;
char *text = NULL;
text = g_markup_escape_text(nm_event_get_text(event), -1);
conference = nm_event_get_conference(event);
if (conference) {
PurpleChatConversation *chat = nm_conference_get_data(conference);
/* Is this a single person 'conversation' or a conference? */
if (chat == NULL && nm_conference_get_participant_count(conference) == 1) {
user_record = nm_find_user_record(user, nm_event_get_source(event));
if (user_record) {
flags = 0;
if (nm_event_get_type(event) == NMEVT_RECEIVE_AUTOREPLY)
flags |= PURPLE_MESSAGE_AUTO_RESP;
purple_serv_got_im(purple_account_get_connection(user->client_data),
nm_user_record_get_display_id(user_record),
text, flags,
nm_event_get_gmt(event));
im = purple_conversations_find_im_with_account(
nm_user_record_get_display_id(user_record),
(PurpleAccount *) user->client_data);
if (im) {
contact = nm_find_contact(user, nm_event_get_source(event));
if (contact) {
purple_conversation_set_title(PURPLE_CONVERSATION(im),
nm_contact_get_display_name(contact));
} else {
const char *name =
nm_user_record_get_full_name(user_record);
if (name == NULL)
name = nm_user_record_get_userid(user_record);
purple_conversation_set_title(PURPLE_CONVERSATION(im), name);
}
}
} else {
/* this should not happen, see the event code.
* the event code will get the contact details from
* the server if it does not have them before calling
* the event callback.
*/
}
} else if (chat) {
/* get the contact for send if we have one */
NMContact *contact = nm_find_contact(user,
nm_event_get_source(event));
/* get the user record for the sender */
user_record = nm_find_user_record(user, nm_event_get_source(event));
if (user_record) {
const char *name = nm_contact_get_display_name(contact);
if (name == NULL) {
name = nm_user_record_get_full_name(user_record);
if (name == NULL)
name = nm_user_record_get_display_id(user_record);
}
purple_serv_got_chat_in(purple_account_get_connection(user->client_data),
purple_chat_conversation_get_id(chat),
name, PURPLE_MESSAGE_RECV, text, nm_event_get_gmt(event));
}
}
}
g_free(text);
}
static void
_evt_conference_left(NMUser * user, NMEvent * event)
{
PurpleChatConversation *chat;
NMConference *conference;
conference = nm_event_get_conference(event);
if (conference) {
chat = nm_conference_get_data(conference);
if (chat) {
NMUserRecord *ur = nm_find_user_record(user,
nm_event_get_source(event));
if (ur)
purple_chat_conversation_remove_user(chat,
nm_user_record_get_display_id(ur),
NULL);
}
}
}
static void
_evt_conference_invite_notify(NMUser * user, NMEvent * event)
{
PurpleConversation *gconv;
NMConference *conference;
NMUserRecord *user_record = NULL;
char *str = NULL;
user_record = nm_find_user_record(user, nm_event_get_source(event));
conference = nm_event_get_conference(event);
if (user_record && conference) {
gconv = nm_conference_get_data(conference);
str = g_strdup_printf(_("%s has been invited to this conversation."),
nm_user_record_get_display_id(user_record));
purple_conversation_write_system_message(gconv, str, 0);
g_free(str);
}
}
static void
_evt_conference_invite(NMUser * user, NMEvent * event)
{
NMUserRecord *ur;
PurpleConnection *gc;
GSList *parms = NULL;
const char *title = NULL;
const char *secondary = NULL;
const char *name = NULL;
char *primary = NULL;
time_t gmt;
ur = nm_find_user_record(user, nm_event_get_source(event));
if (ur)
name = nm_user_record_get_full_name(ur);
if (name == NULL)
name = nm_event_get_source(event);
gmt = nm_event_get_gmt(event);
title = _("Invitation to Conversation");
primary = g_strdup_printf(_("Invitation from: %s\n\nSent: %s"),
name, purple_date_format_full(localtime(&gmt)));
secondary = _("Would you like to join the conversation?");
/* Set up parms list for the callbacks
* We need to send the NMUser object and
* the NMConference object to the callbacks
*/
parms = NULL;
parms = g_slist_append(parms, user);
parms = g_slist_append(parms, nm_event_get_conference(event));
/* Prompt the user */
/* TODO: Would it be better to use purple_serv_got_chat_invite() here? */
gc = purple_account_get_connection(user->client_data);
purple_request_action(gc, title, primary, secondary,
PURPLE_DEFAULT_ACTION_NONE,
purple_request_cpar_from_connection(gc),
parms, 2,
_("Yes"), G_CALLBACK(_join_conference_cb),
_("No"), G_CALLBACK(_reject_conference_cb));
g_free(primary);
}
static void
_evt_conference_joined(NMUser * user, NMEvent * event)
{
PurpleChatConversation *chat = NULL;
PurpleConnection *gc;
NMConference *conference = NULL;
NMUserRecord *ur = NULL;
const char *name;
const char *conf_name;
gc = purple_account_get_connection(user->client_data);
if (gc == NULL)
return;
conference = nm_event_get_conference(event);
if (conference) {
chat = nm_conference_get_data(conference);
if (nm_conference_get_participant_count(conference) == 2 && chat == NULL) {
ur = nm_conference_get_participant(conference, 0);
if (ur) {
conf_name = _get_conference_name(++user->conference_count);
chat =
purple_serv_got_joined_chat(gc, user->conference_count, conf_name);
if (chat) {
nm_conference_set_data(conference, (gpointer) chat);
name = nm_user_record_get_display_id(ur);
purple_chat_conversation_add_user(chat, name, NULL,
PURPLE_CHAT_USER_NONE, TRUE);
}
}
}
if (chat != NULL) {
ur = nm_find_user_record(user, nm_event_get_source(event));
if (ur) {
name = nm_user_record_get_display_id(ur);
if (!purple_chat_conversation_has_user(chat, name)) {
purple_chat_conversation_add_user(chat, name, NULL,
PURPLE_CHAT_USER_NONE, TRUE);
}
}
}
}
}
static void
_evt_status_change(NMUser * user, NMEvent * event)
{
PurpleBuddy *buddy = NULL;
GSList *buddies;
GSList *bnode;
NMUserRecord *user_record;
const char *display_id;
int status;
user_record = nm_event_get_user_record(event);
if (user_record) {
/* Retrieve new status */
status = nm_user_record_get_status(user_record);
/* Update status for buddy in all folders */
display_id = nm_user_record_get_display_id(user_record);
buddies = purple_blist_find_buddies(user->client_data, display_id);
for (bnode = buddies; bnode; bnode = bnode->next) {
buddy = (PurpleBuddy *) bnode->data;
if (buddy) {
_update_buddy_status(user, buddy, status, nm_event_get_gmt(event));
}
}
g_slist_free(buddies);
}
}
static void
_evt_user_disconnect(NMUser * user, NMEvent * event)
{
PurpleConnection *gc;
PurpleAccount *account = user->client_data;
gc = purple_account_get_connection(account);
if (gc)
{
if (!purple_account_get_remember_password(account))
purple_account_set_password(account, NULL, NULL, NULL);
purple_connection_error(gc,
PURPLE_CONNECTION_ERROR_NAME_IN_USE,
_("You have signed on from another location"));
}
}
static void
_evt_user_typing(NMUser * user, NMEvent * event)
{
PurpleConnection *gc;
NMUserRecord *user_record = NULL;
gc = purple_account_get_connection((PurpleAccount *) user->client_data);
if (gc) {
user_record = nm_find_user_record(user, nm_event_get_source(event));
if (user_record) {
purple_serv_got_typing(gc, nm_user_record_get_display_id(user_record),
30, PURPLE_IM_TYPING);
}
}
}
static void
_evt_user_not_typing(NMUser * user, NMEvent * event)
{
PurpleConnection *gc;
NMUserRecord *user_record;
gc = purple_account_get_connection((PurpleAccount *) user->client_data);
if (gc) {
user_record = nm_find_user_record(user, nm_event_get_source(event));
if (user_record) {
purple_serv_got_typing_stopped(gc,
nm_user_record_get_display_id(user_record));
}
}
}
static void
_evt_undeliverable_status(NMUser * user, NMEvent * event)
{
NMUserRecord *ur;
PurpleConversation *gconv;
char *str;
ur = nm_find_user_record(user, nm_event_get_source(event));
if (ur) {
/* XXX - Should this be PURPLE_CONV_TYPE_IM? */
gconv =
purple_conversations_find_with_account(nm_user_record_get_display_id(ur),
user->client_data);
if (gconv) {
const char *name = nm_user_record_get_full_name(ur);
if (name == NULL) {
name = nm_user_record_get_display_id(ur);
}
str = g_strdup_printf(_("%s appears to be offline and did not receive"
" the message that you just sent."), name);
purple_conversation_write_system_message(gconv, str, 0);
g_free(str);
}
}
}
static void
_event_callback(NMUser * user, NMEvent * event)
{
if (user == NULL || event == NULL)
return;
switch (nm_event_get_type(event)) {
case NMEVT_STATUS_CHANGE:
_evt_status_change(user, event);
break;
case NMEVT_RECEIVE_AUTOREPLY:
case NMEVT_RECEIVE_MESSAGE:
_evt_receive_message(user, event);
break;
case NMEVT_USER_DISCONNECT:
_evt_user_disconnect(user, event);
break;
case NMEVT_USER_TYPING:
_evt_user_typing(user, event);
break;
case NMEVT_USER_NOT_TYPING:
_evt_user_not_typing(user, event);
break;
case NMEVT_SERVER_DISCONNECT:
/* Nothing to do? */
break;
case NMEVT_INVALID_RECIPIENT:
break;
case NMEVT_UNDELIVERABLE_STATUS:
_evt_undeliverable_status(user, event);
break;
case NMEVT_CONFERENCE_INVITE_NOTIFY:
/* Someone else has been invited to join a
* conference that we are currently a part of
*/
_evt_conference_invite_notify(user, event);
break;
case NMEVT_CONFERENCE_INVITE:
/* We have been invited to join a conference */
_evt_conference_invite(user, event);
break;
case NMEVT_CONFERENCE_JOINED:
/* Some one has joined a conference that we
* are a part of
*/
_evt_conference_joined(user, event);
break;
case NMEVT_CONFERENCE_LEFT:
/* Someone else has left a conference that we
* are currently a part of
*/
_evt_conference_left(user, event);
break;
default:
purple_debug(PURPLE_DEBUG_INFO, "novell",
"_event_callback(): unhandled event, %d\n",
nm_event_get_type(event));
break;
}
}
/*******************************************************************************
* Protocol Ops
******************************************************************************/
static void
novell_login(PurpleAccount * account)
{
PurpleConnection *gc;
NMUser *user = NULL;
const char *server;
const char *name;
int port;
GError *error = NULL;
if (account == NULL)
return;
gc = purple_account_get_connection(account);
if (gc == NULL)
return;
purple_connection_set_flags(gc, PURPLE_CONNECTION_FLAG_NO_IMAGES);
server = purple_account_get_string(account, "server", NULL);
if (server == NULL || *server == '\0') {
/* TODO: Would be nice to prompt if not set!
* purple_request_fields(gc, _("Server Address"),...);
*/
/* ...but for now just error out with a nice message. */
purple_connection_error(gc,
PURPLE_CONNECTION_ERROR_INVALID_SETTINGS,
_("Unable to connect to server. Please enter the "
"address of the server to which you wish to connect."));
return;
}
port = purple_account_get_int(account, "port", DEFAULT_PORT);
name = purple_account_get_username(account);
user = nm_initialize_user(name, server, port, account, _event_callback);
if (user && user->conn) {
/* save user */
purple_connection_set_protocol_data(gc, user);
/* connect to the server */
purple_connection_update_progress(gc, _("Connecting"),
1, NOVELL_CONNECT_STEPS);
user->conn->client = purple_gio_socket_client_new(account, &error);
if (user->conn->client == NULL) {
purple_connection_take_error(gc, error);
return;
}
g_socket_client_set_tls(user->conn->client, TRUE);
g_socket_client_connect_to_host_async(
user->conn->client, user->conn->addr, user->conn->port,
user->cancellable, novell_login_callback, gc);
}
}
static void
novell_close(PurpleConnection * gc)
{
NMUser *user;
NMConn *conn;
if (gc == NULL)
return;
user = purple_connection_get_protocol_data(gc);
if (user) {
conn = user->conn;
if (conn) {
nm_release_conn(conn);
}
nm_deinitialize_user(user);
}
purple_connection_set_protocol_data(gc, NULL);
}
static int
novell_send_im(PurpleConnection *gc, PurpleMessage *msg)
{
NMUserRecord *user_record = NULL;
NMConference *conf = NULL;
NMMessage *message;
NMUser *user;
const char *dn = NULL;
char *plain;
gboolean done = TRUE, created_conf = FALSE;
NMERR_T rc = NM_OK;
const gchar *name = purple_message_get_recipient(msg);
if (gc == NULL || name == NULL || purple_message_is_empty(msg))
return 0;
user = purple_connection_get_protocol_data(gc);
if (user == NULL)
return 0;
/* Create a new message */
plain = purple_unescape_html(purple_message_get_contents(msg));
message = nm_create_message(plain);
g_free(plain);
/* Need to get the DN for the buddy so we can look up the convo */
dn = nm_lookup_dn(user, name);
/* Do we already know about the sender? */
user_record = nm_find_user_record(user, dn);
if (user_record) {
/* Do we already have an instantiated conference? */
conf = nm_find_conversation(user, dn);
if (conf == NULL) {
/* If not, create a blank conference */
conf = nm_create_conference(NULL);
created_conf = TRUE;
nm_conference_add_participant(conf, user_record);
}
nm_message_set_conference(message, conf);
/* Make sure conference is instantiated */
if (!nm_conference_is_instantiated(conf)) {
/* It is not, so send the createconf. We will
* have to finish sending the message when we
* get the response with the new conference guid.
*/
rc = nm_send_create_conference(user, conf, _createconf_resp_send_msg, message);
_check_for_disconnect(user, rc);
done = FALSE;
}
} else {
/* If we don't have details for the user, then we don't have
* a conference yet. So create one and send the getdetails
* to the server. We will have to finish sending the message
* when we get the response from the server.
*/
conf = nm_create_conference(NULL);
created_conf = TRUE;
nm_message_set_conference(message, conf);
rc = nm_send_get_details(user, name, _get_details_resp_send_msg, message);
_check_for_disconnect(user, rc);
done = FALSE;
}
if (done) {
/* Did we find everything we needed? */
rc = nm_send_message(user, message, _send_message_resp_cb);
_check_for_disconnect(user, rc);
nm_release_message(message);
}
if (created_conf && conf)
nm_release_conference(conf);
return 1;
}
static unsigned int
novell_send_typing(PurpleConnection * gc, const char *name, PurpleIMTypingState state)
{
NMConference *conf = NULL;
NMUser *user;
const char *dn = NULL;
NMERR_T rc = NM_OK;
if (gc == NULL || name == NULL)
return 0;
user = purple_connection_get_protocol_data(gc);
if (user == NULL)
return 0;
/* Need to get the DN for the buddy so we can look up the convo */
dn = nm_lookup_dn(user, name);
if (dn) {
/* Now find the conference in our list */
conf = nm_find_conversation(user, dn);
if (conf) {
rc = nm_send_typing(user, conf,
((state == PURPLE_IM_TYPING) ? TRUE : FALSE), NULL);
_check_for_disconnect(user, rc);
}
}
return 0;
}
static void
novell_convo_closed(PurpleConnection * gc, const char *who)
{
NMUser *user;
NMConference *conf;
const char *dn;
NMERR_T rc = NM_OK;
if (gc == NULL || who == NULL)
return;
user = purple_connection_get_protocol_data(gc);
if (user && (dn = nm_lookup_dn(user, who))) {
conf = nm_find_conversation(user, dn);
if (conf) {
rc = nm_send_leave_conference(user, conf, NULL, NULL);
_check_for_disconnect(user, rc);
}
}
}
static void
novell_chat_leave(PurpleConnection * gc, int id)
{
NMConference *conference;
NMUser *user;
PurpleChatConversation *chat;
GSList *cnode;
NMERR_T rc = NM_OK;
if (gc == NULL)
return;
user = purple_connection_get_protocol_data(gc);
if (user == NULL)
return;
for (cnode = user->conferences; cnode != NULL; cnode = cnode->next) {
conference = cnode->data;
if (conference && (chat = nm_conference_get_data(conference))) {
if (purple_chat_conversation_get_id(chat) == id) {
rc = nm_send_leave_conference(user, conference, NULL, NULL);
_check_for_disconnect(user, rc);
break;
}
}
}
purple_serv_got_chat_left(gc, id);
}
static void
novell_chat_invite(PurpleConnection *gc, int id,
const char *message, const char *who)
{
NMConference *conference;
NMUser *user;
PurpleChatConversation *chat;
GSList *cnode;
NMERR_T rc = NM_OK;
NMUserRecord *user_record = NULL;
if (gc == NULL)
return;
user = purple_connection_get_protocol_data(gc);
if (user == NULL)
return;
user_record = nm_find_user_record(user, who);
if (user_record == NULL) {
rc = nm_send_get_details(user, who, _get_details_resp_send_invite, GINT_TO_POINTER(id));
_check_for_disconnect(user, rc);
return;
}
for (cnode = user->conferences; cnode != NULL; cnode = cnode->next) {
conference = cnode->data;
if (conference && (chat = nm_conference_get_data(conference))) {
if (purple_chat_conversation_get_id(chat) == id) {
rc = nm_send_conference_invite(user, conference, user_record,
message, _sendinvite_resp_cb, NULL);
_check_for_disconnect(user, rc);
break;
}
}
}
}
static int
novell_chat_send(PurpleConnection * gc, int id, PurpleMessage *msg)
{
NMConference *conference;
PurpleChatConversation *chat;
GSList *cnode;
NMMessage *message;
NMUser *user;
NMERR_T rc = NM_OK;
const char *name;
char *str, *plain;
if (gc == NULL || purple_message_is_empty(msg))
return -1;
user = purple_connection_get_protocol_data(gc);
if (user == NULL)
return -1;
plain = purple_unescape_html(purple_message_get_contents(msg));
message = nm_create_message(plain);
g_free(plain);
for (cnode = user->conferences; cnode != NULL; cnode = cnode->next) {
conference = cnode->data;
if (conference && (chat = nm_conference_get_data(conference))) {
if (purple_chat_conversation_get_id(chat) == id) {
nm_message_set_conference(message, conference);
/* check to see if the conference is instatiated yet */
if (!nm_conference_is_instantiated(conference)) {
nm_message_add_ref(message);
nm_send_create_conference(user, conference, _createconf_resp_send_msg, message);
} else {
rc = nm_send_message(user, message, _send_message_resp_cb);
}
nm_release_message(message);
if (!_check_for_disconnect(user, rc)) {
/* Use the account alias if it is set */
name = purple_account_get_private_alias(user->client_data);
if (name == NULL || *name == '\0') {
/* If there is no account alias, try full name */
name = nm_user_record_get_full_name(user->user_record);
if (name == NULL || *name == '\0') {
/* Fall back to the username that we are signed in with */
name = purple_account_get_username(user->client_data);
}
}
purple_serv_got_chat_in(gc, id, name,
purple_message_get_flags(msg),
purple_message_get_contents(msg), time(NULL));
return 0;
} else
return -1;
}
}
}
/* The conference was not found, must be closed */
chat = purple_conversations_find_chat(gc, id);
if (chat) {
str = g_strdup(_("This conference has been closed."
" No more messages can be sent."));
purple_conversation_write_system_message(PURPLE_CONVERSATION(chat), str, 0);
g_free(str);
}
if (message)
nm_release_message(message);
return -1;
}
static void
novell_add_buddy(PurpleConnection * gc, PurpleBuddy *buddy, PurpleGroup * group, const char *message)
{
NMFolder *folder = NULL;
NMContact *contact;
NMUser *user;
NMERR_T rc = NM_OK;
const char *alias, *gname, *bname;
if (gc == NULL || buddy == NULL || group == NULL)
return;
user = (NMUser *) purple_connection_get_protocol_data(gc);
if (user == NULL)
return;
/* If we haven't synched the contact list yet, ignore
* the add_buddy calls. Server side list is the master.
*/
if (!user->clist_synched)
return;
/* Don't re-add a buddy that is already on our contact list */
if (nm_find_user_record(user, purple_buddy_get_name(buddy)) != NULL)
return;
contact = nm_create_contact();
nm_contact_set_dn(contact, purple_buddy_get_name(buddy));
/* Remove the PurpleBuddy (we will add it back after adding it
* to the server side list). Save the alias if there is one.
*/
alias = purple_buddy_get_alias(buddy);
bname = purple_buddy_get_name(buddy);
if (alias && !purple_strequal(alias, bname))
nm_contact_set_display_name(contact, alias);
purple_blist_remove_buddy(buddy);
buddy = NULL;
gname = purple_group_get_name(group);
if (purple_strequal(gname, NM_ROOT_FOLDER_NAME)) {
gname = "";
}
folder = nm_find_folder(user, gname);
if (folder) {
/* We have everything that we need, so send the createcontact */
rc = nm_send_create_contact(user, folder, contact,
_create_contact_resp_cb, contact);
} else {
/* Need to create the folder before we can add the contact */
rc = nm_send_create_folder(user, gname,
_create_folder_resp_add_contact, contact);
}
_check_for_disconnect(user, rc);
}
static void
novell_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
{
NMContact *contact;
NMFolder *folder;
NMUser *user;
const char *dn, *gname;
NMERR_T rc = NM_OK;
if (gc == NULL || buddy == NULL || group == NULL)
return;
user = purple_connection_get_protocol_data(gc);
if (user && (dn = nm_lookup_dn(user, purple_buddy_get_name(buddy)))) {
gname = purple_group_get_name(group);
if (purple_strequal(gname, NM_ROOT_FOLDER_NAME)) {
gname = "";
}
folder = nm_find_folder(user, gname);
if (folder) {
contact = nm_folder_find_contact(folder, dn);
if (contact) {
/* Remove the buddy from the contact */
nm_contact_set_data(contact, NULL);
/* Tell the server to remove the contact */
rc = nm_send_remove_contact(user, folder, contact,
_remove_contact_resp_cb, NULL);
_check_for_disconnect(user, rc);
}
}
}
}
static void
novell_remove_group(PurpleConnection * gc, PurpleGroup *group)
{
NMUser *user;
NMERR_T rc = NM_OK;
if (gc == NULL || group == NULL)
return;
user = purple_connection_get_protocol_data(gc);
if (user) {
NMFolder *folder = nm_find_folder(user, purple_group_get_name(group));
if (folder) {
rc = nm_send_remove_folder(user, folder,
_remove_folder_resp_cb, NULL);
_check_for_disconnect(user, rc);
}
}
}
static void
novell_alias_buddy(PurpleConnection * gc, const char *name, const char *alias)
{
NMContact *contact;
NMUser *user;
GList *contacts = NULL;
GList *cnode = NULL;
const char *dn = NULL, *fname = NULL;
NMERR_T rc = NM_OK;
if (gc == NULL || name == NULL || alias == NULL)
return;
user = purple_connection_get_protocol_data(gc);
if (user && (dn = nm_lookup_dn(user, name))) {
/* Alias all of instances of the contact */
contacts = nm_find_contacts(user, dn);
for (cnode = contacts; cnode != NULL; cnode = cnode->next) {
contact = (NMContact *) cnode->data;
if (contact) {
PurpleGroup *group = NULL;
PurpleBuddy *buddy;
NMFolder *folder;
/* Alias the Purple buddy? */
folder = nm_find_folder_by_id(user,
nm_contact_get_parent_id(contact));
if (folder) {
fname = nm_folder_get_name(folder);
if (*fname == '\0') {
fname = NM_ROOT_FOLDER_NAME;
}
group = purple_blist_find_group(fname);
}
if (group) {
const char *balias;
buddy = purple_blist_find_buddy_in_group(user->client_data,
name, group);
balias = buddy ? purple_buddy_get_local_alias(buddy) : NULL;
if (balias && !purple_strequal(balias, alias))
purple_buddy_set_local_alias(buddy, alias);
}
/* Tell the server to alias the contact */
rc = nm_send_rename_contact(user, contact, alias,
_rename_contact_resp_cb, NULL);
_check_for_disconnect(user, rc);
}
}
if (contacts)
g_list_free(contacts);
}
}
static void
novell_group_buddy(PurpleConnection * gc,
const char *name, const char *old_group_name,
const char *new_group_name)
{
NMFolder *old_folder;
NMFolder *new_folder;
NMContact *contact;
NMUser *user;
const char *dn;
NMERR_T rc = NM_OK;
if (gc == NULL || name == NULL ||
old_group_name == NULL || new_group_name == NULL)
return;
user = purple_connection_get_protocol_data(gc);
if (user && (dn = nm_lookup_dn(user, name))) {
/* Find the old folder */
if (purple_strequal(old_group_name, NM_ROOT_FOLDER_NAME)) {
old_folder = nm_get_root_folder(user);
if (nm_folder_find_contact(old_folder, dn) == NULL)
old_folder = nm_find_folder(user, old_group_name);
} else {
old_folder = nm_find_folder(user, old_group_name);
}
if (old_folder && (contact = nm_folder_find_contact(old_folder, dn))) {
/* Find the new folder */
new_folder = nm_find_folder(user, new_group_name);
if (new_folder == NULL) {
if (purple_strequal(new_group_name, NM_ROOT_FOLDER_NAME))
new_folder = nm_get_root_folder(user);
}
if (new_folder) {
/* Tell the server to move the contact to the new folder */
rc = nm_send_move_contact(user, contact, new_folder,
_move_contact_resp_cb, NULL);
} else {
nm_contact_add_ref(contact);
/* Remove the old contact first */
nm_send_remove_contact(user, old_folder, contact,
_remove_contact_resp_cb, NULL);
/* New folder does not exist yet, so create it */
rc = nm_send_create_folder(user, new_group_name,
_create_folder_resp_move_contact,
contact);
}
_check_for_disconnect(user, rc);
}
}
}
static void
novell_rename_group(PurpleConnection * gc, const char *old_name,
PurpleGroup *group, GList *moved_buddies)
{
NMERR_T rc = NM_OK;
NMFolder *folder;
NMUser *user;
if (gc == NULL || old_name == NULL || group == NULL || moved_buddies == NULL) {
return;
}
user = purple_connection_get_protocol_data(gc);
if (user) {
const char *gname = purple_group_get_name(group);
/* Does new folder exist already? */
if (nm_find_folder(user, gname)) {
/* purple_group_set_name() adds the buddies
* to the new group and removes the old group...
* so there is nothing more to do here.
*/
return;
}
if (purple_strequal(old_name, NM_ROOT_FOLDER_NAME)) {
/* Can't rename the root folder ... need to revisit this */
return;
}
folder = nm_find_folder(user, old_name);
if (folder) {
rc = nm_send_rename_folder(user, folder, gname,
_rename_folder_resp_cb, NULL);
_check_for_disconnect(user, rc);
}
}
}
static const char *
novell_list_icon(PurpleAccount * account, PurpleBuddy * buddy)
{
return "novell";
}
static void
novell_tooltip_text(PurpleBuddy * buddy, PurpleNotifyUserInfo * user_info, gboolean full)
{
NMUserRecord *user_record = NULL;
PurpleConnection *gc;
NMUser *user;
int status = 0;
const char *status_str = NULL;
const char *text = NULL;
if (buddy == NULL)
return;
gc = purple_account_get_connection(purple_buddy_get_account(buddy));
if (gc == NULL || (user = purple_connection_get_protocol_data(gc)) == NULL)
return;
if (PURPLE_BUDDY_IS_ONLINE(buddy)) {
user_record = nm_find_user_record(user, purple_buddy_get_name(buddy));
if (user_record) {
status = nm_user_record_get_status(user_record);
text = nm_user_record_get_status_text(user_record);
/* No custom text, so default it ... */
switch (status) {
case NM_STATUS_AVAILABLE:
status_str = _("Available");
break;
case NM_STATUS_AWAY:
status_str = _("Away");
break;
case NM_STATUS_BUSY:
status_str = _("Busy");
break;
case NM_STATUS_AWAY_IDLE:
status_str = _("Idle");
break;
case NM_STATUS_OFFLINE:
status_str = _("Offline");
break;
default:
status_str = _("Unknown");
break;
}
purple_notify_user_info_add_pair_plaintext(user_info, _("Status"), status_str);
if (text) {
/* TODO: Check whether it's correct to call add_pair_html,
or if we should be using add_pair_plaintext */
purple_notify_user_info_add_pair_html(user_info, _("Message"), text);
}
}
}
}
static void
novell_set_idle(PurpleConnection * gc, int time)
{
NMUser *user;
NMERR_T rc = NM_OK;
const char *id = NULL;
PurpleStatus *status = NULL;
if (gc == NULL)
return;
user = purple_connection_get_protocol_data(gc);
if (user == NULL)
return;
status = purple_account_get_active_status(purple_connection_get_account(gc));
id = purple_status_get_id(status);
/* Only go idle if active status is available */
if (purple_strequal(id, NOVELL_STATUS_TYPE_AVAILABLE)) {
if (time > 0) {
rc = nm_send_set_status(user, NM_STATUS_AWAY_IDLE, NULL, NULL, NULL, NULL);
} else {
rc = nm_send_set_status(user, NM_STATUS_AVAILABLE, NULL, NULL, NULL, NULL);
}
}
_check_for_disconnect(user, rc);
}
static void
novell_get_info(PurpleConnection * gc, const char *name)
{
NMUserRecord *user_record;
NMUser *user;
NMERR_T rc;
if (gc == NULL || name == NULL)
return;
user = purple_connection_get_protocol_data(gc);
if (user) {
user_record = nm_find_user_record(user, name);
if (user_record) {
_show_info(gc, user_record, g_strdup(name));
} else {
rc = nm_send_get_details(user, name,
_get_details_resp_show_info, g_strdup(name));
_check_for_disconnect(user, rc);
}
}
}
static char *
novell_status_text(PurpleBuddy * buddy)
{
const char *text = NULL;
const char *dn = NULL;
PurpleAccount *account;
account = buddy ? purple_buddy_get_account(buddy) : NULL;
if (buddy && account) {
PurpleConnection *gc = purple_account_get_connection(account);
if (gc) {
NMUser *user = purple_connection_get_protocol_data(gc);
if (user) {
dn = nm_lookup_dn(user, purple_buddy_get_name(buddy));
if (dn) {
NMUserRecord *user_record = nm_find_user_record(user, dn);
if (user_record) {
text = nm_user_record_get_status_text(user_record);
if (text)
return g_strdup(text);
}
}
}
}
}
return NULL;
}
static GList *
novell_status_types(PurpleAccount *account)
{
GList *status_types = NULL;
PurpleStatusType *type;
g_return_val_if_fail(account != NULL, NULL);
type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE, NOVELL_STATUS_TYPE_AVAILABLE,
NULL, TRUE, TRUE, FALSE,
"message", _("Message"), purple_value_new(G_TYPE_STRING),
NULL);
status_types = g_list_append(status_types, type);
type = purple_status_type_new_with_attrs(PURPLE_STATUS_AWAY, NOVELL_STATUS_TYPE_AWAY,
NULL, TRUE, TRUE, FALSE,
"message", _("Message"), purple_value_new(G_TYPE_STRING),
NULL);
status_types = g_list_append(status_types, type);
type = purple_status_type_new_with_attrs(PURPLE_STATUS_UNAVAILABLE, NOVELL_STATUS_TYPE_BUSY,
_("Busy"), TRUE, TRUE, FALSE,
"message", _("Message"), purple_value_new(G_TYPE_STRING),
NULL);
status_types = g_list_append(status_types, type);
type = purple_status_type_new_full(PURPLE_STATUS_INVISIBLE, NOVELL_STATUS_TYPE_APPEAR_OFFLINE,
NULL, TRUE, TRUE, FALSE);
status_types = g_list_append(status_types, type);
type = purple_status_type_new_full(PURPLE_STATUS_OFFLINE, NULL, NULL, TRUE, TRUE, FALSE);
status_types = g_list_append(status_types, type);
return status_types;
}
static void
novell_set_status(PurpleAccount *account, PurpleStatus *status)
{
PurpleConnection *gc;
gboolean connected;
PurplePresence *presence;
PurpleStatusType *type;
PurpleStatusPrimitive primitive;
NMUser *user;
NMSTATUS_T novellstatus = NM_STATUS_AVAILABLE;
NMERR_T rc = NM_OK;
const char *msg = NULL;
char *text = NULL;
connected = purple_account_is_connected(account);
presence = purple_status_get_presence(status);
type = purple_status_get_status_type(status);
primitive = purple_status_type_get_primitive(type);
/*
* We don't have any independent statuses, so we don't need to
* do anything when a status is deactivated (because another
* status is about to be activated).
*/
if (!purple_status_is_active(status))
return;
if (!connected)
return;
gc = purple_account_get_connection(account);
user = purple_connection_get_protocol_data(gc);
if (user == NULL)
return;
if (primitive == PURPLE_STATUS_AVAILABLE) {
novellstatus = NM_STATUS_AVAILABLE;
} else if (primitive == PURPLE_STATUS_AWAY) {
novellstatus = NM_STATUS_AWAY;
} else if (primitive == PURPLE_STATUS_UNAVAILABLE) {
novellstatus = NM_STATUS_BUSY;
} else if (primitive == PURPLE_STATUS_INVISIBLE) {
novellstatus = NM_STATUS_OFFLINE;
} else if (purple_presence_is_idle(presence)) {
novellstatus = NM_STATUS_AWAY_IDLE;
} else {
novellstatus = NM_STATUS_AVAILABLE;
}
if (primitive == PURPLE_STATUS_AWAY || primitive == PURPLE_STATUS_AVAILABLE ||
primitive == PURPLE_STATUS_UNAVAILABLE) {
msg = purple_status_get_attr_string(status, "message");
text = g_strdup(msg);
if (primitive == PURPLE_STATUS_AVAILABLE)
msg = NULL; /* no auto replies for online status */
/* Don't want newlines in status text */
purple_util_chrreplace(text, '\n', ' ');
}
rc = nm_send_set_status(user, novellstatus, text, msg, NULL, NULL);
_check_for_disconnect(user, rc);
g_free(text);
}
static void
novell_add_permit(PurpleConnection *gc, const char *who)
{
NMUser *user;
NMERR_T rc = NM_OK;
const char *name = who;
if (gc == NULL || who == NULL)
return;
user = purple_connection_get_protocol_data(gc);
if (user == NULL)
return;
/* Remove first -- we will add it back in when we get
* the okay from the server
*/
purple_account_privacy_permit_remove(purple_connection_get_account(gc), who, TRUE);
if (nm_user_is_privacy_locked(user)) {
_show_privacy_locked_error(gc, user);
_sync_privacy_lists(user);
return;
}
/* Work around for problem with un-typed, dotted contexts */
if (strchr(who, '.')) {
const char *dn = nm_lookup_dn(user, who);
if (dn == NULL) {
rc = nm_send_get_details(user, who,
_get_details_send_privacy_create,
GINT_TO_POINTER(TRUE));
_check_for_disconnect(user, rc);
return;
} else {
name = dn;
}
}
rc = nm_send_create_privacy_item(user, name, TRUE,
_create_privacy_item_permit_resp_cb,
g_strdup(who));
_check_for_disconnect(user, rc);
}
static void
novell_add_deny(PurpleConnection *gc, const char *who)
{
NMUser *user;
NMERR_T rc = NM_OK;
const char *name = who;
if (gc == NULL || who == NULL)
return;
user = purple_connection_get_protocol_data(gc);
if (user == NULL)
return;
/* Remove first -- we will add it back in when we get
* the okay from the server
*/
purple_account_privacy_deny_remove(purple_connection_get_account(gc), who, TRUE);
if (nm_user_is_privacy_locked(user)) {
_show_privacy_locked_error(gc, user);
_sync_privacy_lists(user);
return;
}
/* Work around for problem with un-typed, dotted contexts */
if (strchr(who, '.')) {
const char *dn = nm_lookup_dn(user, who);
if (dn == NULL) {
rc = nm_send_get_details(user, who,
_get_details_send_privacy_create,
GINT_TO_POINTER(FALSE));
_check_for_disconnect(user, rc);
return;
} else {
name = dn;
}
}
rc = nm_send_create_privacy_item(user, name, FALSE,
_create_privacy_item_deny_resp_cb,
g_strdup(who));
_check_for_disconnect(user, rc);
}
static void
novell_rem_permit(PurpleConnection *gc, const char *who)
{
NMUser *user;
NMERR_T rc = NM_OK;
const char *dn = NULL;
if (gc == NULL || who == NULL)
return;
user = purple_connection_get_protocol_data(gc);
if (user == NULL)
return;
if (nm_user_is_privacy_locked(user)) {
_show_privacy_locked_error(gc, user);
_sync_privacy_lists(user);
return;
}
dn = nm_lookup_dn(user, who);
if (dn == NULL)
dn = who;
rc = nm_send_remove_privacy_item(user, dn, TRUE,
_remove_privacy_item_resp_cb,
g_strdup(who));
_check_for_disconnect(user, rc);
}
static void
novell_rem_deny(PurpleConnection *gc, const char *who)
{
NMUser *user;
NMERR_T rc = NM_OK;
const char *dn = NULL;
if (gc == NULL || who == NULL)
return;
user = purple_connection_get_protocol_data(gc);
if (user == NULL)
return;
if (nm_user_is_privacy_locked(user)) {
_show_privacy_locked_error(gc, user);
_sync_privacy_lists(user);
return;
}
dn = nm_lookup_dn(user, who);
if (dn == NULL)
dn = who;
rc = nm_send_remove_privacy_item(user, dn, FALSE,
_remove_privacy_item_resp_cb,
g_strdup(who));
_check_for_disconnect(user, rc);
}
static void
novell_set_permit_deny(PurpleConnection *gc)
{
NMERR_T rc = NM_OK;
const char *dn, *name = NULL;
NMUserRecord *user_record = NULL;
GSList *node = NULL, *copy = NULL;
NMUser *user;
int i, j, num_contacts, num_folders;
NMContact *contact;
NMFolder *folder = NULL;
PurpleAccount *account;
if (gc == NULL)
return;
account = purple_connection_get_account(gc);
user = purple_connection_get_protocol_data(gc);
if (user == NULL)
return;
if (user->privacy_synched == FALSE) {
_sync_privacy_lists(user);
user->privacy_synched = TRUE;
return;
}
if (nm_user_is_privacy_locked(user)) {
_show_privacy_locked_error(gc, user);
_sync_privacy_lists(user);
return;
}
switch (purple_account_get_privacy_type(account)) {
case PURPLE_ACCOUNT_PRIVACY_ALLOW_ALL:
rc = nm_send_set_privacy_default(user, FALSE,
_set_privacy_default_resp_cb, NULL);
_check_for_disconnect(user, rc);
/* clear server side deny list */
if (rc == NM_OK) {
copy = g_slist_copy(user->deny_list);
for (node = copy; node && node->data; node = node->next) {
rc = nm_send_remove_privacy_item(user, (const char *)node->data,
FALSE, NULL, NULL);
if (_check_for_disconnect(user, rc))
break;
}
g_slist_free(copy);
g_slist_free(user->deny_list);
user->deny_list = NULL;
}
break;
case PURPLE_ACCOUNT_PRIVACY_DENY_ALL:
rc = nm_send_set_privacy_default(user, TRUE,
_set_privacy_default_resp_cb, NULL);
_check_for_disconnect(user, rc);
/* clear server side allow list */
if (rc == NM_OK) {
copy = g_slist_copy(user->allow_list);
for (node = copy; node && node->data; node = node->next) {
rc = nm_send_remove_privacy_item(user, (const char *)node->data,
TRUE, NULL, NULL);
if (_check_for_disconnect(user, rc))
break;
}
g_slist_free(copy);
g_slist_free(user->allow_list);
user->allow_list = NULL;
}
break;
case PURPLE_ACCOUNT_PRIVACY_ALLOW_USERS:
rc = nm_send_set_privacy_default(user, TRUE,
_set_privacy_default_resp_cb, NULL);
_check_for_disconnect(user, rc);
/* sync allow lists */
if (rc == NM_OK) {
for (node = user->allow_list; node; node = node->next) {
user_record = nm_find_user_record(user, (char *)node->data);
if (user_record) {
name = nm_user_record_get_display_id(user_record);
if (!g_slist_find_custom(purple_account_privacy_get_permitted(account),
name, (GCompareFunc)purple_utf8_strcasecmp)) {
purple_account_privacy_permit_add(account, name , TRUE);
}
}
}
for (node = purple_account_privacy_get_permitted(account); node; node = node->next) {
dn = nm_lookup_dn(user, (char *)node->data);
if (dn) {
if (!g_slist_find_custom(user->allow_list,
dn, (GCompareFunc)purple_utf8_strcasecmp)) {
rc = nm_send_create_privacy_item(user, dn, TRUE,
_create_privacy_item_deny_resp_cb,
g_strdup(dn));
_check_for_disconnect(user, rc);
}
} else {
purple_account_privacy_permit_remove(account, (char *)node->data, TRUE);
}
}
}
break;
case PURPLE_ACCOUNT_PRIVACY_DENY_USERS:
/* set to default allow */
rc = nm_send_set_privacy_default(user, FALSE,
_set_privacy_default_resp_cb, NULL);
_check_for_disconnect(user, rc);
/* sync deny lists */
if (rc == NM_OK) {
for (node = user->deny_list; node; node = node->next) {
user_record = nm_find_user_record(user, (char *)node->data);
if (user_record) {
name = nm_user_record_get_display_id(user_record);
if (!g_slist_find_custom(purple_account_privacy_get_denied(account),
name, (GCompareFunc)purple_utf8_strcasecmp)) {
purple_account_privacy_deny_add(account, name , TRUE);
}
}
}
for (node = purple_account_privacy_get_denied(account); node; node = node->next) {
name = NULL;
dn = nm_lookup_dn(user, (char *)node->data);
if (dn) {
user_record = nm_find_user_record(user, dn);
name = nm_user_record_get_display_id(user_record);
if (!g_slist_find_custom(user->deny_list,
dn, (GCompareFunc)purple_utf8_strcasecmp)) {
rc = nm_send_create_privacy_item(user, dn, FALSE,
_create_privacy_item_deny_resp_cb,
g_strdup(name));
_check_for_disconnect(user, rc);
}
} else {
purple_account_privacy_deny_remove(account, (char *)node->data, TRUE);
}
}
}
break;
case PURPLE_ACCOUNT_PRIVACY_ALLOW_BUDDYLIST:
/* remove users from allow list that are not in buddy list */
copy = g_slist_copy(user->allow_list);
for (node = copy; node && node->data; node = node->next) {
if (!nm_find_contacts(user, node->data)) {
rc = nm_send_remove_privacy_item(user, (const char *)node->data,
TRUE, NULL, NULL);
if (_check_for_disconnect(user, rc))
return;
}
}
g_slist_free(copy);
/* add all buddies to allow list */
num_contacts = nm_folder_get_contact_count(user->root_folder);
for (i = 0; i < num_contacts; i++) {
contact = nm_folder_get_contact(user->root_folder, i);
dn = nm_contact_get_dn(contact);
if (dn && !g_slist_find_custom(user->allow_list,
dn, (GCompareFunc)purple_utf8_strcasecmp))
{
rc = nm_send_create_privacy_item(user, dn, TRUE,
_create_privacy_item_deny_resp_cb,
g_strdup(dn));
if (_check_for_disconnect(user, rc))
return;
}
}
num_folders = nm_folder_get_subfolder_count(user->root_folder);
for (i = 0; i < num_folders; i++) {
folder = nm_folder_get_subfolder(user->root_folder, i);
num_contacts = nm_folder_get_contact_count(folder);
for (j = 0; j < num_contacts; j++) {
contact = nm_folder_get_contact(folder, j);
dn = nm_contact_get_dn(contact);
if (dn && !g_slist_find_custom(user->allow_list,
dn, (GCompareFunc)purple_utf8_strcasecmp))
{
rc = nm_send_create_privacy_item(user, dn, TRUE,
_create_privacy_item_deny_resp_cb,
g_strdup(dn));
if (_check_for_disconnect(user, rc))
return;
}
}
}
/* set to default deny */
rc = nm_send_set_privacy_default(user, TRUE,
_set_privacy_default_resp_cb, NULL);
if (_check_for_disconnect(user, rc))
break;
break;
}
}
static GList *
novell_blist_node_menu(PurpleBlistNode *node)
{
GList *list = NULL;
PurpleActionMenu *act;
if(PURPLE_IS_BUDDY(node)) {
act = purple_action_menu_new(_("Initiate _Chat"),
PURPLE_CALLBACK(_initiate_conference_cb),
NULL, NULL);
list = g_list_append(list, act);
}
return list;
}
static void
novell_keepalive(PurpleConnection *gc)
{
NMUser *user;
NMERR_T rc = NM_OK;
if (gc == NULL)
return;
user = purple_connection_get_protocol_data(gc);
if (user == NULL)
return;
rc = nm_send_keepalive(user, NULL, NULL);
_check_for_disconnect(user, rc);
}
static gssize
novell_get_max_message_size(PurpleConversation *conv)
{
/* XXX: got from pidgin-otr - verify and document it */
return 1792;
}
static void
novell_protocol_init(NovellProtocol *self)
{
PurpleProtocol *protocol = PURPLE_PROTOCOL(self);
PurpleAccountOption *option;
protocol->id = "prpl-novell";
protocol->name = "GroupWise";
option = purple_account_option_string_new(_("Server address"), "server", NULL);
protocol->account_options =
g_list_append(protocol->account_options, option);
option = purple_account_option_int_new(_("Server port"), "port", DEFAULT_PORT);
protocol->account_options =
g_list_append(protocol->account_options, option);
}
static void
novell_protocol_class_init(NovellProtocolClass *klass)
{
PurpleProtocolClass *protocol_class = PURPLE_PROTOCOL_CLASS(klass);
protocol_class->login = novell_login;
protocol_class->close = novell_close;
protocol_class->status_types = novell_status_types;
protocol_class->list_icon = novell_list_icon;
}
static void
novell_protocol_class_finalize(G_GNUC_UNUSED NovellProtocolClass *klass)
{
}
static void
novell_protocol_client_iface_init(PurpleProtocolClientInterface *client_iface)
{
client_iface->status_text = novell_status_text;
client_iface->tooltip_text = novell_tooltip_text;
client_iface->blist_node_menu = novell_blist_node_menu;
client_iface->convo_closed = novell_convo_closed;
client_iface->normalize = purple_normalize_nocase;
client_iface->get_max_message_size = novell_get_max_message_size;
}
static void
novell_protocol_server_iface_init(PurpleProtocolServerInterface *server_iface)
{
server_iface->get_info = novell_get_info;
server_iface->set_status = novell_set_status;
server_iface->set_idle = novell_set_idle;
server_iface->add_buddy = novell_add_buddy;
server_iface->remove_buddy = novell_remove_buddy;
server_iface->keepalive = novell_keepalive;
server_iface->alias_buddy = novell_alias_buddy;
server_iface->group_buddy = novell_group_buddy;
server_iface->rename_group = novell_rename_group;
server_iface->remove_group = novell_remove_group;
}
static void
novell_protocol_im_iface_init(PurpleProtocolIMInterface *im_iface)
{
im_iface->send = novell_send_im;
im_iface->send_typing = novell_send_typing;
}
static void
novell_protocol_chat_iface_init(PurpleProtocolChatInterface *chat_iface)
{
chat_iface->invite = novell_chat_invite;
chat_iface->leave = novell_chat_leave;
chat_iface->send = novell_chat_send;
}
static void
novell_protocol_privacy_iface_init(PurpleProtocolPrivacyInterface *privacy_iface)
{
privacy_iface->add_permit = novell_add_permit;
privacy_iface->add_deny = novell_add_deny;
privacy_iface->rem_permit = novell_rem_permit;
privacy_iface->rem_deny = novell_rem_deny;
privacy_iface->set_permit_deny = novell_set_permit_deny;
}
G_DEFINE_DYNAMIC_TYPE_EXTENDED(
NovellProtocol, novell_protocol, PURPLE_TYPE_PROTOCOL, 0,
G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_CLIENT,
novell_protocol_client_iface_init)
G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_SERVER,
novell_protocol_server_iface_init)
G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_IM,
novell_protocol_im_iface_init)
G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_CHAT,
novell_protocol_chat_iface_init)
G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_PRIVACY,
novell_protocol_privacy_iface_init));
static PurplePluginInfo *
plugin_query(GError **error)
{
return purple_plugin_info_new(
"id", "prpl-novell",
"name", "Novell GroupWise Protocol",
"version", DISPLAY_VERSION,
"category", N_("Protocol"),
"summary", N_("Novell GroupWise Messenger Protocol Plugin"),
"description", N_("Novell GroupWise Messenger Protocol Plugin"),
"website", PURPLE_WEBSITE,
"abi-version", PURPLE_ABI_VERSION,
"flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL |
PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD,
NULL
);
}
static gboolean
plugin_load(PurplePlugin *plugin, GError **error)
{
novell_protocol_register_type(G_TYPE_MODULE(plugin));
my_protocol = purple_protocols_add(NOVELL_TYPE_PROTOCOL, error);
if (!my_protocol)
return FALSE;
return TRUE;
}
static gboolean
plugin_unload(PurplePlugin *plugin, GError **error)
{
if (!purple_protocols_remove(my_protocol, error))
return FALSE;
return TRUE;
}
PURPLE_PLUGIN_INIT(novell, plugin_query, plugin_load, plugin_unload);