* 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 "purpleaccountoption.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; _is_disconnect_error(NMERR_T err); _check_for_disconnect(NMUser * user, NMERR_T err); _send_message(NMUser * user, NMMessage * message); _update_buddy_status(NMUser *user, PurpleBuddy * buddy, int status, int gmt); _remove_purple_buddies(NMUser * user); _add_contacts_to_purple_blist(NMUser * user, NMFolder * folder); _add_purple_buddies(NMUser * user); _sync_contact_list(NMUser *user); _sync_privacy_lists(NMUser *user); _show_info(PurpleConnection * gc, NMUserRecord * user_record, char * name); _get_conference_name(int id); /******************************************************************************* *******************************************************************************/ /* Handle login response */ _login_resp_cb(NMUser * user, NMERR_T ret_code, gpointer resp_data, gpointer user_data) gc = purple_account_get_connection(user->client_data); /* 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); 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, _check_for_disconnect(user, rc); PurpleConnectionError reason; char *err = g_strdup_printf(_("Unable to login: %s"), nm_error_to_string (ret_code)); case NMERR_AUTHENTICATION_FAILED: case NMERR_CREDENTIALS_MISSING: case NMERR_PASSWORD_INVALID: /* Don't attempt to auto-reconnect if our 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; /* FIXME: There are other reasons login could fail */ reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR; purple_connection_error(gc, reason, err); /* Handle getstatus response*/ _get_status_resp_cb(NMUser * user, NMERR_T ret_code, gpointer resp_data, gpointer user_data) NMUserRecord *user_record = (NMUserRecord *) resp_data; if (user == NULL || user_record == NULL) /* Find all Purple buddies and update their statuses */ const char *name = nm_user_record_get_display_id(user_record); buddies = purple_blist_find_buddies((PurpleAccount *) user->client_data, name); for (bnode = buddies; bnode; bnode = bnode->next) { buddy = (PurpleBuddy *) bnode->data; status = nm_user_record_get_status(user_record); _update_buddy_status(user, buddy, status, time(0)); purple_debug(PURPLE_DEBUG_INFO, "novell", "_get_status_resp_cb(): rc = 0x%X\n", ret_code); /* Show an error if the rename failed */ _rename_contact_resp_cb(NMUser * user, NMERR_T ret_code, gpointer resp_data, gpointer user_data) purple_debug(PURPLE_DEBUG_INFO, "novell", "_rename_contact_resp_cb(): rc = 0x%X\n", ret_code); /* Handle the getdetails response and send the message */ _get_details_resp_send_msg(NMUser * user, NMERR_T ret_code, gpointer resp_data, gpointer user_data) PurpleConversation *gconv; NMUserRecord *user_record = NULL; NMMessage *msg = user_data; if (user == NULL || msg == NULL) user_record = (NMUserRecord *) resp_data; /* 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); dn = nm_user_record_get_dn(user_record); cntct = nm_find_contact(user, dn); purple_conversation_set_title(gconv, nm_contact_get_display_name(cntct)); /* Not in the contact list, try to user full name */ name = (char *) nm_user_record_get_full_name(user_record); purple_conversation_set_title(gconv, name); /* Add the user record to particpant list */ conf = nm_message_get_conference(msg); nm_conference_add_participant(conf, user_record); _send_message(user, msg); gc = purple_account_get_connection(user->client_data); 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)); /* Set up the new PurpleBuddy based on the response from getdetails */ _get_details_resp_setup_buddy(NMUser * user, NMERR_T ret_code, gpointer resp_data, gpointer user_data) NMUserRecord *user_record; if (user == NULL || resp_data == NULL || user_data == NULL) buddy = nm_contact_get_data(contact); nm_contact_set_user_record(contact, user_record); 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), _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 */ _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; const char *folder_name = NULL; new_contact = (NMContact *) resp_data; if (new_contact == NULL || tmp_contact == NULL) /* Get the userid and folder name for the new contact */ folder = nm_find_folder_by_id(user, nm_contact_get_parent_id(new_contact)); 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); const char *alias = nm_contact_get_display_name(tmp_contact); const char *display_id = nm_contact_get_display_id(new_contact); 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); /* 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); 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); PurpleConnection *gc = purple_account_get_connection(user->client_data); const char *name = nm_contact_get_dn(tmp_contact); 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)); nm_release_contact(tmp_contact); /* Show an error if we failed to send the message */ _send_message_resp_cb(NMUser * user, NMERR_T ret_code, gpointer resp_data, gpointer user_data) 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)); /* Show an error if the remove failed */ _remove_contact_resp_cb(NMUser * user, NMERR_T ret_code, gpointer resp_data, gpointer user_data) /* 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 */ _remove_folder_resp_cb(NMUser * user, NMERR_T ret_code, gpointer resp_data, gpointer user_data) /* 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 */ _move_contact_resp_cb(NMUser * user, NMERR_T ret_code, gpointer resp_data, gpointer user_data) /* 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 */ _rename_folder_resp_cb(NMUser * user, NMERR_T ret_code, gpointer resp_data, gpointer user_data) /* TODO: Display an error? */ purple_debug(PURPLE_DEBUG_INFO, "novell", "_rename_folder_resp_cb(): rc = 0x%x\n", ret_code); _sendinvite_resp_cb(NMUser *user, NMERR_T ret_code, gpointer resp_data, gpointer user_data) 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)); 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. _createconf_resp_send_msg(NMUser * user, NMERR_T ret_code, gpointer resp_data, gpointer user_data) NMMessage *msg = user_data; if (user == NULL || msg == NULL) _send_message(user, msg); if ((conf = nm_message_get_conference(msg))) { PurpleConnection *gc = purple_account_get_connection(user->client_data); ur = nm_conference_get_participant(conf, 0); name = nm_user_record_get_userid(ur); err = g_strdup_printf(_("Unable to send message to %s." " Could not create the conference (%s)."), nm_error_to_string (ret_code)); 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)); /* Move contact to newly created folder */ _create_folder_resp_move_contact(NMUser * user, NMERR_T ret_code, gpointer resp_data, gpointer user_data) NMContact *contact = user_data; char *folder_name = resp_data; if (user == NULL || folder_name == NULL || contact == NULL) { if (ret_code == NM_OK || ret_code == NMERR_DUPLICATE_FOLDER) { new_folder = nm_find_folder(user, folder_name); /* 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, _check_for_disconnect(user, rc); 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), nm_error_to_string (ret_code)); purple_notify_error(gc, NULL, err, NULL, purple_request_cpar_from_connection(gc)); /* Add contact to newly create folder */ _create_folder_resp_add_contact(NMUser * user, NMERR_T ret_code, gpointer resp_data, gpointer user_data) NMContact *contact = (NMContact *) user_data; char *folder_name = (char *) resp_data; if (user == NULL || folder_name == NULL || contact == NULL) { nm_release_contact(contact); if (ret_code == NM_OK || ret_code == NMERR_DUPLICATE_FOLDER) { folder = nm_find_folder(user, folder_name); rc = nm_send_create_contact(user, folder, contact, _create_contact_resp_cb, contact); _check_for_disconnect(user, rc); PurpleConnection *gc = purple_account_get_connection(user->client_data); const char *name = nm_contact_get_dn(contact); 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); _join_conf_resp_cb(NMUser * user, NMERR_T ret_code, gpointer resp_data, gpointer user_data) PurpleChatConversation *chat; NMConference *conference = user_data; const char *name, *conf_name; if (user == NULL || conference == NULL) gc = purple_account_get_connection(user->client_data); conf_name = _get_conference_name(++user->conference_count); chat = purple_serv_got_joined_chat(gc, user->conference_count, conf_name); 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); 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 */ _get_details_resp_show_info(NMUser * user, NMERR_T ret_code, gpointer resp_data, gpointer user_data) NMUserRecord *user_record; user_record = (NMUserRecord *) resp_data; _show_info(purple_account_get_connection(user->client_data), user_record, g_strdup(name)); gc = purple_account_get_connection(user->client_data); 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)); /* Handle get details response add to privacy list */ _get_details_resp_add_privacy_item(NMUser *user, NMERR_T ret_code, gpointer resp_data, gpointer user_data) NMUserRecord *user_record = resp_data; gboolean allowed = GPOINTER_TO_INT(user_data); 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 (!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); 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); 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)); /* Handle response to create privacy item request */ _create_privacy_item_deny_resp_cb(NMUser *user, NMERR_T ret_code, gpointer resp_data, gpointer user_data) NMUserRecord *user_record; const char *display_id = NULL; gc = purple_account_get_connection(user->client_data); account = purple_connection_get_account(gc); user_record = nm_find_user_record(user, who); display_id = nm_user_record_get_display_id(user_record); 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); rc = nm_send_get_details(user, who, _get_details_resp_add_privacy_item, _check_for_disconnect(user, rc); 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)); /* Handle response to create privacy item request */ _create_privacy_item_permit_resp_cb(NMUser *user, NMERR_T ret_code, gpointer resp_data, gpointer user_data) NMUserRecord *user_record; const char *display_id = NULL; gc = purple_account_get_connection(user->client_data); account = purple_connection_get_account(gc); user_record = nm_find_user_record(user, who); display_id = nm_user_record_get_display_id(user_record); if (!g_slist_find_custom(purple_account_privacy_get_permitted(account), (GCompareFunc)purple_utf8_strcasecmp)) { purple_account_privacy_permit_add(account, display_id, TRUE); rc = nm_send_get_details(user, who, _get_details_resp_add_privacy_item, _check_for_disconnect(user, rc); 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)); _get_details_send_privacy_create(NMUser *user, NMERR_T ret_code, gpointer resp_data, gpointer user_data) NMUserRecord *user_record = resp_data; gboolean allowed = GPOINTER_TO_INT(user_data); const char *dn, *display_id; 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); rc = nm_send_create_privacy_item(user, dn, TRUE, _create_privacy_item_permit_resp_cb, _check_for_disconnect(user, rc); rc = nm_send_create_privacy_item(user, dn, FALSE, _create_privacy_item_deny_resp_cb, _check_for_disconnect(user, rc); 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)); _remove_privacy_item_resp_cb(NMUser *user, NMERR_T ret_code, gpointer resp_data, gpointer user_data) 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)); _set_privacy_default_resp_cb(NMUser *user, NMERR_T ret_code, gpointer resp_data, gpointer user_data) 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)); /* Handle get details response add to privacy list */ _get_details_resp_send_invite(NMUser *user, NMERR_T ret_code, gpointer resp_data, gpointer user_data) NMUserRecord *user_record = resp_data; NMConference *conference; int id = GPOINTER_TO_INT(user_data); gc = purple_account_get_connection(user->client_data); 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); 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)); _createconf_resp_send_invite(NMUser * user, NMERR_T ret_code, gpointer resp_data, gpointer user_data) NMConference *conference = resp_data; NMUserRecord *user_record = user_data; rc = nm_send_conference_invite(user, conference, user_record, NULL, _sendinvite_resp_cb, NULL); _check_for_disconnect(user, rc); 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)); /******************************************************************************* ******************************************************************************/ const char *sysname = ""; const char *release = ""; return g_strdup_printf("Purple/%s (%s; %s)", VERSION, sysname, release); const char *sysname = ""; os_info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); GetSystemInfo(&sys_info); if (os_info.dwPlatformId == VER_PLATFORM_WIN32_NT) { switch (os_info.dwMajorVersion) { switch (os_info.dwMinorVersion) { sysname = "Windows 2000"; sysname = "Windows Server 2003"; } else if (os_info.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) { switch (os_info.dwMinorVersion) { return g_strdup_printf("Purple/%s (%s; %ld.%ld)", VERSION, sysname, os_info.dwMajorVersion, os_info.dwMinorVersion); _is_disconnect_error(NMERR_T err) return (err == NMERR_TCP_WRITE || err == NMERR_TCP_READ || err == NMERR_PROTOCOL); _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.")); /* 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. _send_message(NMUser * user, NMMessage * message) conf = nm_message_get_conference(message); /* 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); 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 _update_buddy_status(NMUser *user, PurpleBuddy * buddy, int novellstatus, int gmt) account = purple_buddy_get_account(buddy); name = purple_buddy_get_name(buddy); case NM_STATUS_AVAILABLE: status_id = NOVELL_STATUS_TYPE_AVAILABLE; status_id = NOVELL_STATUS_TYPE_AWAY; status_id = NOVELL_STATUS_TYPE_BUSY; status_id = NOVELL_STATUS_TYPE_OFFLINE; case NM_STATUS_AWAY_IDLE: status_id = NOVELL_STATUS_TYPE_AWAY; status_id = NOVELL_STATUS_TYPE_OFFLINE; /* Get status text for the user */ dn = nm_lookup_dn(user, name); NMUserRecord *user_record = nm_find_user_record(user, dn); text = nm_user_record_get_status_text(user_record); purple_protocol_got_user_status(account, name, status_id, 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. _remove_purple_buddies(NMUser *user) 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)) group = (PurpleGroup *) gnode; gname = purple_group_get_name(group); for (cnode = purple_blist_node_get_first_child(gnode); cnode = purple_blist_node_get_sibling_next(cnode)) { if (!PURPLE_IS_CONTACT(cnode)) for (bnode = purple_blist_node_get_first_child(cnode); bnode = purple_blist_node_get_sibling_next(bnode)) { if (!PURPLE_IS_BUDDY(bnode)) buddy = (PurpleBuddy *) bnode; if (purple_buddy_get_account(buddy) == user->client_data) { if (purple_strequal(gname, NM_ROOT_FOLDER_NAME)) folder = nm_find_folder(user, gname); !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 */ _add_contacts_to_purple_blist(NMUser * user, NMFolder * folder) NMUserRecord *user_record = NULL; NMContact *contact = NULL; PurpleBuddy *buddy = NULL; const char *fname = NULL; /* If this is the root folder give it a name. Purple does not have the concept of 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); 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); name = nm_contact_get_display_id(contact); buddy = purple_blist_find_buddy_in_group(user->client_data, name, group); /* Add it to the purple buddy list */ buddy = purple_buddy_new(user->client_data, 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); 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); /* NULL contact. This should not happen, but * let's break out of the loop. /* Add all of the server side contacts to the Purple buddy list. */ _add_purple_buddies(NMUser * user) NMFolder *root_folder = NULL; root_folder = nm_get_root_folder(user); /* 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); _add_contacts_to_purple_blist(user, folder); /* Add contacts for the root folder */ _add_contacts_to_purple_blist(user, root_folder); _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 _remove_purple_buddies(user); _add_purple_buddies(user); user->clist_synched = TRUE; _sync_privacy_lists(NMUser *user) GSList *node = NULL, *rem_list = NULL; NMUserRecord *user_record; gc = purple_account_get_connection(user->client_data); 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); purple_account_set_privacy_type(account, PURPLE_ACCOUNT_PRIVACY_ALLOW_USERS); if (user->deny_list == NULL) { purple_account_set_privacy_type(account, PURPLE_ACCOUNT_PRIVACY_ALLOW_ALL); purple_account_set_privacy_type(account, PURPLE_ACCOUNT_PRIVACY_DENY_USERS); for (node = user->allow_list; node; node = node->next) { user_record = nm_find_user_record(user, (char *)node->data); name = nm_user_record_get_display_id(user_record); 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); name = nm_user_record_get_display_id(user_record); 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); for (node = purple_account_privacy_get_permitted(account); node; node = node->next) { dn = nm_lookup_dn(user, (char *)node->data); !g_slist_find_custom(user->allow_list, dn, (GCompareFunc)purple_utf8_strcasecmp)) { rem_list = g_slist_append(rem_list, node->data); for (node = rem_list; node; node = node->next) { purple_account_privacy_permit_remove(account, (char *)node->data, TRUE); for (node = purple_account_privacy_get_denied(account); node; node = node->next) { dn = nm_lookup_dn(user, (char *)node->data); !g_slist_find_custom(user->deny_list, dn, (GCompareFunc)purple_utf8_strcasecmp)) { rem_list = g_slist_append(rem_list, node->data); for (node = rem_list; node; node = node->next) { purple_account_privacy_deny_remove(account, (char *)node->data, TRUE); /* Map known property tags to user-friendly strings */ _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")) else if (purple_strequal(tag, "OU")) else if (purple_strequal(tag, "personalTitle")) return _("Personal Title"); else if (purple_strequal(tag, "Title")) else if (purple_strequal(tag, "mailstop")) else if (purple_strequal(tag, "Internet EMail Address")) return _("Email Address"); /* Display a dialog box showing the properties for the given user record */ _show_info(PurpleConnection * gc, NMUserRecord * user_record, char * name) PurpleNotifyUserInfo *user_info = purple_notify_user_info_new(); value = nm_user_record_get_userid(user_record); /* 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); value = nm_user_record_get_full_name(user_record); /* 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); tag = _map_property_tag(nm_property_get_tag(property)); value = nm_property_get_value(property); /* 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); /* 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. _join_conference_cb(GSList * parms) NMConference *conference; if (parms == NULL || g_slist_length(parms) != 2) 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); /* 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. _reject_conference_cb(GSList * parms) NMConference *conference; if (parms == NULL || g_slist_length(parms) != 2) 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); _initiate_conference_cb(PurpleBlistNode *node, gpointer ignored) 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); /* We should already have a userrecord for the buddy */ user_record = nm_find_user_record(user, purple_buddy_get_name(buddy)); conf_name = _get_conference_name(++user->conference_count); chat = purple_serv_got_joined_chat(gc, user->conference_count, conf_name); 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); _get_conference_name(int id) static char *name = NULL; name = g_strdup_printf(_("GroupWise Conference %d"), id); _show_privacy_locked_error(PurpleConnection *gc, NMUser *user) 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)); /******************************************************************************* * Connect and recv callbacks ******************************************************************************/ novell_ssl_connect_error(PurpleSslConnection * gsc, PurpleSslErrorType error, gpointer data) user = purple_connection_get_protocol_data(gc); purple_connection_ssl_error (gc, error); novell_ssl_recv_cb(gpointer data, PurpleSslConnection * gsc, PurpleInputCondition condition) PurpleConnection *gc = data; user = purple_connection_get_protocol_data(gc); rc = nm_process_new_data(user); if (_is_disconnect_error(rc)) { purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Error communicating with server. Closing connection.")); purple_debug(PURPLE_DEBUG_INFO, "novell", "Error processing event or response (%d).\n", rc); novell_ssl_connected_cb(gpointer data, PurpleSslConnection * gsc, PurpleInputCondition cond) PurpleConnection *gc = data; const char *my_addr = NULL; if (gc == NULL || gsc == NULL) user = purple_connection_get_protocol_data(gc); if ((user == NULL) || (conn = user->conn) == NULL) purple_connection_update_progress(gc, _("Authenticating..."), 2, NOVELL_CONNECT_STEPS); my_addr = purple_network_get_my_ip(gsc->fd); pwd = purple_connection_get_password(gc); ua = _user_agent_string(); rc = nm_send_login(user, pwd, my_addr, ua, _login_resp_cb, NULL); purple_ssl_input_add(gsc, novell_ssl_recv_cb, gc); purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, purple_connection_update_progress(gc, _("Waiting for response..."), 3, NOVELL_CONNECT_STEPS); /******************************************************************************* * Event callback and event handlers ******************************************************************************/ _evt_receive_message(NMUser * user, NMEvent * event) NMUserRecord *user_record = NULL; NMContact *contact = NULL; PurpleIMConversation *im; NMConference *conference; PurpleMessageFlags flags; text = g_markup_escape_text(nm_event_get_text(event), -1); conference = nm_event_get_conference(event); 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 (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), nm_event_get_gmt(event)); im = purple_conversations_find_im_with_account( nm_user_record_get_display_id(user_record), (PurpleAccount *) user->client_data); contact = nm_find_contact(user, nm_event_get_source(event)); purple_conversation_set_title(PURPLE_CONVERSATION(im), nm_contact_get_display_name(contact)); nm_user_record_get_full_name(user_record); name = nm_user_record_get_userid(user_record); purple_conversation_set_title(PURPLE_CONVERSATION(im), name); /* 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 /* 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)); const char *name = nm_contact_get_display_name(contact); name = nm_user_record_get_full_name(user_record); 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)); _evt_conference_left(NMUser * user, NMEvent * event) PurpleChatConversation *chat; NMConference *conference; conference = nm_event_get_conference(event); chat = nm_conference_get_data(conference); NMUserRecord *ur = nm_find_user_record(user, nm_event_get_source(event)); purple_chat_conversation_remove_user(chat, nm_user_record_get_display_id(ur), _evt_conference_invite_notify(NMUser * user, NMEvent * event) PurpleConversation *gconv; NMConference *conference; NMUserRecord *user_record = 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); _evt_conference_invite(NMUser * user, NMEvent * event) const char *title = NULL; const char *secondary = NULL; ur = nm_find_user_record(user, nm_event_get_source(event)); name = nm_user_record_get_full_name(ur); 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 = g_slist_append(parms, user); parms = g_slist_append(parms, nm_event_get_conference(event)); /* 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), _("Yes"), G_CALLBACK(_join_conference_cb), _("No"), G_CALLBACK(_reject_conference_cb)); _evt_conference_joined(NMUser * user, NMEvent * event) PurpleChatConversation *chat = NULL; NMConference *conference = NULL; gc = purple_account_get_connection(user->client_data); conference = nm_event_get_conference(event); chat = nm_conference_get_data(conference); if (nm_conference_get_participant_count(conference) == 2 && chat == NULL) { ur = nm_conference_get_participant(conference, 0); conf_name = _get_conference_name(++user->conference_count); purple_serv_got_joined_chat(gc, user->conference_count, conf_name); 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); ur = nm_find_user_record(user, nm_event_get_source(event)); 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); _evt_status_change(NMUser * user, NMEvent * event) PurpleBuddy *buddy = NULL; NMUserRecord *user_record; user_record = nm_event_get_user_record(event); /* 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; _update_buddy_status(user, buddy, status, nm_event_get_gmt(event)); _evt_user_disconnect(NMUser * user, NMEvent * event) PurpleAccount *account = user->client_data; gc = purple_account_get_connection(account); 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")); _evt_user_typing(NMUser * user, NMEvent * event) NMUserRecord *user_record = NULL; gc = purple_account_get_connection((PurpleAccount *) user->client_data); user_record = nm_find_user_record(user, nm_event_get_source(event)); purple_serv_got_typing(gc, nm_user_record_get_display_id(user_record), _evt_user_not_typing(NMUser * user, NMEvent * event) NMUserRecord *user_record; gc = purple_account_get_connection((PurpleAccount *) user->client_data); user_record = nm_find_user_record(user, nm_event_get_source(event)); purple_serv_got_typing_stopped(gc, nm_user_record_get_display_id(user_record)); _evt_undeliverable_status(NMUser * user, NMEvent * event) PurpleConversation *gconv; ur = nm_find_user_record(user, nm_event_get_source(event)); /* XXX - Should this be PURPLE_CONV_TYPE_IM? */ purple_conversations_find_with_account(nm_user_record_get_display_id(ur), const char *name = nm_user_record_get_full_name(ur); 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); _event_callback(NMUser * user, NMEvent * event) if (user == NULL || event == NULL) switch (nm_event_get_type(event)) { case NMEVT_STATUS_CHANGE: _evt_status_change(user, event); case NMEVT_RECEIVE_AUTOREPLY: case NMEVT_RECEIVE_MESSAGE: _evt_receive_message(user, event); case NMEVT_USER_DISCONNECT: _evt_user_disconnect(user, event); _evt_user_typing(user, event); case NMEVT_USER_NOT_TYPING: _evt_user_not_typing(user, event); case NMEVT_SERVER_DISCONNECT: case NMEVT_INVALID_RECIPIENT: case NMEVT_UNDELIVERABLE_STATUS: _evt_undeliverable_status(user, event); 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); case NMEVT_CONFERENCE_INVITE: /* We have been invited to join a conference */ _evt_conference_invite(user, event); case NMEVT_CONFERENCE_JOINED: /* Some one has joined a conference that we _evt_conference_joined(user, event); case NMEVT_CONFERENCE_LEFT: /* Someone else has left a conference that we * are currently a part of _evt_conference_left(user, event); purple_debug(PURPLE_DEBUG_INFO, "novell", "_event_callback(): unhandled event, %d\n", nm_event_get_type(event)); /******************************************************************************* ******************************************************************************/ novell_login(PurpleAccount * account) gc = purple_account_get_connection(account); 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.")); 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) { purple_connection_set_protocol_data(gc, user); /* connect to the server */ purple_connection_update_progress(gc, _("Connecting"), 1, NOVELL_CONNECT_STEPS); user->conn->read = (nm_ssl_read_cb)purple_ssl_read; user->conn->write = (nm_ssl_write_cb)purple_ssl_write; user->conn->data = purple_ssl_connect( user->client_data, user->conn->addr, user->conn->port, novell_ssl_connected_cb, novell_ssl_connect_error, gc); if (user->conn->data == NULL) { purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT, _("SSL support unavailable")); novell_close(PurpleConnection * gc) user = purple_connection_get_protocol_data(gc); purple_ssl_close(user->conn->data); nm_deinitialize_user(user); purple_connection_set_protocol_data(gc, NULL); novell_send_im(PurpleConnection *gc, PurpleMessage *msg) NMUserRecord *user_record = NULL; NMConference *conf = NULL; gboolean done = TRUE, created_conf = FALSE; const gchar *name = purple_message_get_recipient(msg); if (gc == NULL || name == NULL || purple_message_is_empty(msg)) user = purple_connection_get_protocol_data(gc); /* Create a new message */ plain = purple_unescape_html(purple_message_get_contents(msg)); message = nm_create_message(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); /* Do we already have an instantiated conference? */ conf = nm_find_conversation(user, dn); /* If not, create a blank conference */ conf = nm_create_conference(NULL); 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); /* 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); nm_message_set_conference(message, conf); rc = nm_send_get_details(user, name, _get_details_resp_send_msg, message); _check_for_disconnect(user, rc); /* 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); novell_send_typing(PurpleConnection * gc, const char *name, PurpleIMTypingState state) NMConference *conf = NULL; if (gc == NULL || name == NULL) user = purple_connection_get_protocol_data(gc); /* Need to get the DN for the buddy so we can look up the convo */ dn = nm_lookup_dn(user, name); /* Now find the conference in our list */ conf = nm_find_conversation(user, dn); rc = nm_send_typing(user, conf, ((state == PURPLE_IM_TYPING) ? TRUE : FALSE), NULL); _check_for_disconnect(user, rc); novell_convo_closed(PurpleConnection * gc, const char *who) if (gc == NULL || who == NULL) user = purple_connection_get_protocol_data(gc); if (user && (dn = nm_lookup_dn(user, who))) { conf = nm_find_conversation(user, dn); rc = nm_send_leave_conference(user, conf, NULL, NULL); _check_for_disconnect(user, rc); novell_chat_leave(PurpleConnection * gc, int id) NMConference *conference; PurpleChatConversation *chat; user = purple_connection_get_protocol_data(gc); 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); purple_serv_got_chat_left(gc, id); novell_chat_invite(PurpleConnection *gc, int id, const char *message, const char *who) NMConference *conference; PurpleChatConversation *chat; NMUserRecord *user_record = NULL; user = purple_connection_get_protocol_data(gc); 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); 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); novell_chat_send(PurpleConnection * gc, int id, PurpleMessage *msg) NMConference *conference; PurpleChatConversation *chat; if (gc == NULL || purple_message_is_empty(msg)) user = purple_connection_get_protocol_data(gc); plain = purple_unescape_html(purple_message_get_contents(msg)); message = nm_create_message(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); 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)); /* The conference was not found, must be closed */ chat = purple_conversations_find_chat(gc, id); str = g_strdup(_("This conference has been closed." " No more messages can be sent.")); purple_conversation_write_system_message(PURPLE_CONVERSATION(chat), str, 0); nm_release_message(message); novell_add_buddy(PurpleConnection * gc, PurpleBuddy *buddy, PurpleGroup * group, const char *message) const char *alias, *gname, *bname; if (gc == NULL || buddy == NULL || group == NULL) user = (NMUser *) purple_connection_get_protocol_data(gc); /* If we haven't synched the contact list yet, ignore * the add_buddy calls. Server side list is the master. if (!user->clist_synched) /* 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) 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); gname = purple_group_get_name(group); if (purple_strequal(gname, NM_ROOT_FOLDER_NAME)) { folder = nm_find_folder(user, gname); /* We have everything that we need, so send the createcontact */ rc = nm_send_create_contact(user, folder, contact, _create_contact_resp_cb, contact); /* 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); novell_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group) if (gc == NULL || buddy == NULL || group == NULL) 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)) { folder = nm_find_folder(user, gname); contact = nm_folder_find_contact(folder, dn); /* 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); novell_remove_group(PurpleConnection * gc, PurpleGroup *group) if (gc == NULL || group == NULL) user = purple_connection_get_protocol_data(gc); NMFolder *folder = nm_find_folder(user, purple_group_get_name(group)); rc = nm_send_remove_folder(user, folder, _remove_folder_resp_cb, NULL); _check_for_disconnect(user, rc); novell_alias_buddy(PurpleConnection * gc, const char *name, const char *alias) const char *dn = NULL, *fname = NULL; if (gc == NULL || name == NULL || alias == NULL) 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; PurpleGroup *group = NULL; /* Alias the Purple buddy? */ folder = nm_find_folder_by_id(user, nm_contact_get_parent_id(contact)); fname = nm_folder_get_name(folder); fname = NM_ROOT_FOLDER_NAME; group = purple_blist_find_group(fname); buddy = purple_blist_find_buddy_in_group(user->client_data, 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); novell_group_buddy(PurpleConnection * gc, const char *name, const char *old_group_name, const char *new_group_name) if (gc == NULL || name == NULL || old_group_name == NULL || new_group_name == NULL) 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); 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); /* 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); 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, _check_for_disconnect(user, rc); novell_rename_group(PurpleConnection * gc, const char *old_name, PurpleGroup *group, GList *moved_buddies) if (gc == NULL || old_name == NULL || group == NULL || moved_buddies == NULL) { user = purple_connection_get_protocol_data(gc); 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. if (purple_strequal(old_name, NM_ROOT_FOLDER_NAME)) { /* Can't rename the root folder ... need to revisit this */ folder = nm_find_folder(user, old_name); rc = nm_send_rename_folder(user, folder, gname, _rename_folder_resp_cb, NULL); _check_for_disconnect(user, rc); novell_list_icon(PurpleAccount * account, PurpleBuddy * buddy) novell_tooltip_text(PurpleBuddy * buddy, PurpleNotifyUserInfo * user_info, gboolean full) NMUserRecord *user_record = NULL; const char *status_str = NULL; gc = purple_account_get_connection(purple_buddy_get_account(buddy)); if (gc == NULL || (user = purple_connection_get_protocol_data(gc)) == NULL) if (PURPLE_BUDDY_IS_ONLINE(buddy)) { user_record = nm_find_user_record(user, purple_buddy_get_name(buddy)); status = nm_user_record_get_status(user_record); text = nm_user_record_get_status_text(user_record); /* No custom text, so default it ... */ case NM_STATUS_AVAILABLE: status_str = _("Available"); case NM_STATUS_AWAY_IDLE: status_str = _("Offline"); status_str = _("Unknown"); purple_notify_user_info_add_pair_plaintext(user_info, _("Status"), status_str); /* 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); novell_set_idle(PurpleConnection * gc, int time) PurpleStatus *status = NULL; user = purple_connection_get_protocol_data(gc); 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)) { rc = nm_send_set_status(user, NM_STATUS_AWAY_IDLE, NULL, NULL, NULL, NULL); rc = nm_send_set_status(user, NM_STATUS_AVAILABLE, NULL, NULL, NULL, NULL); _check_for_disconnect(user, rc); novell_get_info(PurpleConnection * gc, const char *name) NMUserRecord *user_record; if (gc == NULL || name == NULL) user = purple_connection_get_protocol_data(gc); user_record = nm_find_user_record(user, name); _show_info(gc, user_record, g_strdup(name)); rc = nm_send_get_details(user, name, _get_details_resp_show_info, g_strdup(name)); _check_for_disconnect(user, rc); novell_status_text(PurpleBuddy * buddy) account = buddy ? purple_buddy_get_account(buddy) : NULL; PurpleConnection *gc = purple_account_get_connection(account); NMUser *user = purple_connection_get_protocol_data(gc); dn = nm_lookup_dn(user, purple_buddy_get_name(buddy)); NMUserRecord *user_record = nm_find_user_record(user, dn); text = nm_user_record_get_status_text(user_record); novell_status_types(PurpleAccount *account) GList *status_types = NULL; g_return_val_if_fail(account != NULL, NULL); type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE, NOVELL_STATUS_TYPE_AVAILABLE, "message", _("Message"), purple_value_new(G_TYPE_STRING), status_types = g_list_append(status_types, type); type = purple_status_type_new_with_attrs(PURPLE_STATUS_AWAY, NOVELL_STATUS_TYPE_AWAY, "message", _("Message"), purple_value_new(G_TYPE_STRING), 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), 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); novell_set_status(PurpleAccount *account, PurpleStatus *status) PurplePresence *presence; PurpleStatusPrimitive primitive; NMSTATUS_T novellstatus = NM_STATUS_AVAILABLE; 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)) gc = purple_account_get_connection(account); user = purple_connection_get_protocol_data(gc); 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; 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"); 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); novell_add_permit(PurpleConnection *gc, const char *who) if (gc == NULL || who == NULL) user = purple_connection_get_protocol_data(gc); /* 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); /* Work around for problem with un-typed, dotted contexts */ const char *dn = nm_lookup_dn(user, who); rc = nm_send_get_details(user, who, _get_details_send_privacy_create, _check_for_disconnect(user, rc); rc = nm_send_create_privacy_item(user, name, TRUE, _create_privacy_item_permit_resp_cb, _check_for_disconnect(user, rc); novell_add_deny(PurpleConnection *gc, const char *who) if (gc == NULL || who == NULL) user = purple_connection_get_protocol_data(gc); /* 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); /* Work around for problem with un-typed, dotted contexts */ const char *dn = nm_lookup_dn(user, who); rc = nm_send_get_details(user, who, _get_details_send_privacy_create, _check_for_disconnect(user, rc); rc = nm_send_create_privacy_item(user, name, FALSE, _create_privacy_item_deny_resp_cb, _check_for_disconnect(user, rc); novell_rem_permit(PurpleConnection *gc, const char *who) if (gc == NULL || who == NULL) user = purple_connection_get_protocol_data(gc); if (nm_user_is_privacy_locked(user)) { _show_privacy_locked_error(gc, user); _sync_privacy_lists(user); dn = nm_lookup_dn(user, who); rc = nm_send_remove_privacy_item(user, dn, TRUE, _remove_privacy_item_resp_cb, _check_for_disconnect(user, rc); novell_rem_deny(PurpleConnection *gc, const char *who) if (gc == NULL || who == NULL) user = purple_connection_get_protocol_data(gc); if (nm_user_is_privacy_locked(user)) { _show_privacy_locked_error(gc, user); _sync_privacy_lists(user); dn = nm_lookup_dn(user, who); rc = nm_send_remove_privacy_item(user, dn, FALSE, _remove_privacy_item_resp_cb, _check_for_disconnect(user, rc); novell_set_permit_deny(PurpleConnection *gc) const char *dn, *name = NULL; NMUserRecord *user_record = NULL; GSList *node = NULL, *copy = NULL; int i, j, num_contacts, num_folders; account = purple_connection_get_account(gc); user = purple_connection_get_protocol_data(gc); if (user->privacy_synched == FALSE) { _sync_privacy_lists(user); user->privacy_synched = TRUE; if (nm_user_is_privacy_locked(user)) { _show_privacy_locked_error(gc, user); _sync_privacy_lists(user); 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 */ 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, if (_check_for_disconnect(user, rc)) g_slist_free(user->deny_list); 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 */ 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, if (_check_for_disconnect(user, rc)) g_slist_free(user->allow_list); 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); for (node = user->allow_list; node; node = node->next) { user_record = nm_find_user_record(user, (char *)node->data); 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 (!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, _check_for_disconnect(user, rc); purple_account_privacy_permit_remove(account, (char *)node->data, TRUE); 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); for (node = user->deny_list; node; node = node->next) { user_record = nm_find_user_record(user, (char *)node->data); 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) { dn = nm_lookup_dn(user, (char *)node->data); 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, _check_for_disconnect(user, rc); purple_account_privacy_deny_remove(account, (char *)node->data, TRUE); 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, if (_check_for_disconnect(user, rc)) /* 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, if (_check_for_disconnect(user, rc)) 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, if (_check_for_disconnect(user, rc)) /* set to default deny */ rc = nm_send_set_privacy_default(user, TRUE, _set_privacy_default_resp_cb, NULL); if (_check_for_disconnect(user, rc)) novell_blist_node_menu(PurpleBlistNode *node) if(PURPLE_IS_BUDDY(node)) { act = purple_action_menu_new(_("Initiate _Chat"), PURPLE_CALLBACK(_initiate_conference_cb), list = g_list_append(list, act); novell_keepalive(PurpleConnection *gc) user = purple_connection_get_protocol_data(gc); rc = nm_send_keepalive(user, NULL, NULL); _check_for_disconnect(user, rc); novell_get_max_message_size(PurpleConversation *conv) /* XXX: got from pidgin-otr - verify and document it */ 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); 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; novell_protocol_class_finalize(G_GNUC_UNUSED NovellProtocolClass *klass) 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; 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; novell_protocol_im_iface_init(PurpleProtocolIMInterface *im_iface) im_iface->send = novell_send_im; im_iface->send_typing = novell_send_typing; 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; 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( "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, plugin_load(PurplePlugin *plugin, GError **error) novell_protocol_register_type(G_TYPE_MODULE(plugin)); my_protocol = purple_protocols_add(NOVELL_TYPE_PROTOCOL, error); plugin_unload(PurplePlugin *plugin, GError **error) if (!purple_protocols_remove(my_protocol, error)) PURPLE_PLUGIN_INIT(novell, plugin_query, plugin_load, plugin_unload);