diff src/protocols/novell/novell.c @ 8933:6663ad2386d9

[gaim-migrate @ 9703] "Initial Comment: * Added support for privacy settings * Added support for invites - ability to initiate multi-user conferences (chats). * Changed the license notice (to the standard GPL notice) * Fixed 64 bit issues * Incorporated Joe Shaw's patch for better error messages * Fixed the buddy list sync problems" --Mike Stoddard of Novell committer: Tailor Script <tailor@pidgin.im>
author Luke Schierer <lschiere@pidgin.im>
date Sat, 15 May 2004 14:00:31 +0000
parents 518455386538
children 80b4c956d7ae
line wrap: on
line diff
--- a/src/protocols/novell/novell.c	Sat May 15 06:24:52 2004 +0000
+++ b/src/protocols/novell/novell.c	Sat May 15 14:00:31 2004 +0000
@@ -1,22 +1,20 @@
 /*
  * novell.c
  *
- * Copyright © 2004 Unpublished Work of Novell, Inc. All Rights Reserved.
+ * 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 WORK IS AN UNPUBLISHED WORK OF NOVELL, INC. NO PART OF THIS WORK MAY BE
- * USED, PRACTICED, PERFORMED, COPIED, DISTRIBUTED, REVISED, MODIFIED,
- * TRANSLATED, ABRIDGED, CONDENSED, EXPANDED, COLLECTED, COMPILED, LINKED,
- * RECAST, TRANSFORMED OR ADAPTED WITHOUT THE PRIOR WRITTEN CONSENT OF NOVELL,
- * INC. ANY USE OR EXPLOITATION OF THIS WORK WITHOUT AUTHORIZATION COULD SUBJECT
- * THE PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY.
+ * 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.
  *
- * AS BETWEEN [GAIM] AND NOVELL, NOVELL GRANTS [GAIM] THE RIGHT TO REPUBLISH
- * THIS WORK UNDER THE GPL (GNU GENERAL PUBLIC LICENSE) WITH ALL RIGHTS AND
- * LICENSES THEREUNDER.  IF YOU HAVE RECEIVED THIS WORK DIRECTLY OR INDIRECTLY
- * FROM [GAIM] AS PART OF SUCH A REPUBLICATION, YOU HAVE ALL RIGHTS AND LICENSES
- * GRANTED BY [GAIM] UNDER THE GPL.  IN CONNECTION WITH SUCH A REPUBLICATION, IF
- * ANYTHING IN THIS NOTICE CONFLICTS WITH THE TERMS OF THE GPL, SUCH TERMS
- * PREVAIL.
+ * 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., 59 Temple Place, Suite 330, Boston, MA	02111-1307	USA
  *
  */
 
@@ -31,12 +29,16 @@
 #include "sslconn.h"
 #include "request.h"
 #include "network.h"
+#include "privacy.h"
+#include "multi.h"
 
 #define DEFAULT_PORT			8300
 #define NOVELL_CONNECT_STEPS	4
 
 static GaimPlugin *my_protocol = NULL;
 
+static gboolean set_permit = FALSE;
+
 static gboolean
 _is_disconnect_error(NMERR_T err);
 
@@ -59,8 +61,17 @@
 _add_gaim_buddies(NMUser * user);
 
 static void
+_sync_contact_list(NMUser *user);
+
+static void
+_sync_privacy_lists(NMUser *user);
+
+static void
 _show_info(GaimConnection * gc, NMUserRecord * user_record);
 
+const char *
+_get_conference_name(int id);
+
 /*******************************************************************************
  * Response callbacks
  *******************************************************************************/
@@ -92,25 +103,21 @@
 				gaim_account_set_alias(user->client_data, alias);
 		}
 
+		_sync_contact_list(user);
+		_sync_privacy_lists(user);
+
 		/* Tell Gaim that we are connected */
 		gaim_connection_set_state(gc, GAIM_CONNECTED);
 		serv_finish_login(gc);
 
-		/* Sync the contact list. This is pretty simplistic right now,
-		 * we just remove all of the GaimBuddy from the client side list
-		 * for this account and then add in all of the contacts from the
-		 * server side list.
-		 */
-		_remove_gaim_buddies(user);
-		_add_gaim_buddies(user);
-
 		rc = nm_send_set_status(user, NM_STATUS_AVAILABLE, NULL, NULL, NULL,
 								NULL);
 		_check_for_disconnect(user, rc);
 
 	} else {
 
-		char *err = g_strdup_printf(_("Login failed (0x%X)."), ret_code);
+		char *err = g_strdup_printf(_("Login failed (%s)."),
+					    nm_error_to_string (ret_code));
 
 		gaim_connection_error(gc, err);
 		g_free(err);
@@ -223,8 +230,8 @@
 		gc = gaim_account_get_connection(user->client_data);
 		if (gc != NULL) {
 			char *err = g_strdup_printf(_("Unable to send message."
-										  " Could not get details for user (0x%X)."),
-										ret_code);
+										  " Could not get details for user (%s)."),
+						    nm_error_to_string (ret_code));
 
 			gaim_notify_error(gc, NULL, err, NULL);
 			g_free(err);
@@ -368,8 +375,8 @@
 		char *err;
 
 		err =
-			g_strdup_printf(_("Unable to add %s to your buddy list (0x%X)."),
-							name, ret_code);
+			g_strdup_printf(_("Unable to add %s to your buddy list (%s)."),
+					name, nm_error_to_string (ret_code));
 		gaim_notify_error(gc, NULL, err, NULL);
 		g_free(err);
 
@@ -394,8 +401,8 @@
 		gc = gaim_account_get_connection(user->client_data);
 
 		/* TODO: Improve this! message to who or for what conference? */
-		err = g_strdup_printf(_("Unable to send message (0x%X)."),
-							  ret_code);
+		err = g_strdup_printf(_("Unable to send message (%s)."),
+				      nm_error_to_string (ret_code));
 		gaim_notify_error(gc, NULL, err, NULL);
 		g_free(err);
 	}
@@ -453,6 +460,28 @@
 	}
 }
 
+static void
+_sendinvite_resp_cb(NMUser *user, NMERR_T ret_code,
+					gpointer resp_data, gpointer user_data)
+{
+	char *err;
+	GaimConnection *gc;
+
+	if (user == NULL)
+		return;
+
+	if (ret_code != NM_OK) {
+		gc = gaim_account_get_connection(user->client_data);
+		err = g_strdup_printf(_("Unable to invite user (%s)."), nm_error_to_string(ret_code));
+		gaim_notify_error(gc, NULL, err, NULL);
+		g_free(err);
+
+		gaim_debug(GAIM_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.
  */
@@ -483,12 +512,13 @@
 
 			if (name)
 				err = g_strdup_printf(_("Unable to send message to %s."
-										" Could not create the conference (0x%X)."),
-									  name, ret_code);
+										" 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 (0x%X)."),
-									  ret_code);
+										" Could not create the conference (%s)."),
+						      nm_error_to_string (ret_code));
 
 			gaim_notify_error(gc, NULL, err, NULL);
 			g_free(err);
@@ -517,7 +547,7 @@
 		return;
 	}
 
-	if (ret_code == NM_OK || ret_code == 0xD126) {
+	if (ret_code == NM_OK || ret_code == NMERR_DUPLICATE_FOLDER) {
 		new_folder = nm_find_folder(user, folder_name);
 		if (new_folder) {
 
@@ -535,10 +565,10 @@
 		GaimConnection *gc = gaim_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 (0x%X)."),
-									nm_contact_get_dn(contact),
-									folder_name,
-									ret_code);
+									  " Error while creating folder (%s)."),
+					    nm_contact_get_dn(contact),
+					    folder_name,
+					    nm_error_to_string (ret_code));
 
 		gaim_notify_error(gc, NULL, err, NULL);
 		g_free(err);
@@ -569,7 +599,7 @@
 		return;
 	}
 
-	if (ret_code == NM_OK || ret_code == 0xD126) {
+	if (ret_code == NM_OK || ret_code == NMERR_DUPLICATE_FOLDER) {
 		folder = nm_find_folder(user, folder_name);
 		if (folder) {
 
@@ -582,8 +612,8 @@
 		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 (0x%X)."),
-							name, ret_code);
+					  " Error creating folder in server side list (%s)."),
+					name, nm_error_to_string (ret_code));
 
 		gaim_notify_error(gc, NULL, err, NULL);
 
@@ -602,8 +632,7 @@
 	GaimConnection *gc;
 	NMUserRecord *ur;
 	NMConference *conference = user_data;
-	const char *name;
-	char *conf_name;
+	const char *name, *conf_name;
 	int i, count;
 
 	if (user == NULL || conference == NULL)
@@ -612,8 +641,7 @@
 	gc = gaim_account_get_connection(user->client_data);
 
 	if (ret_code == NM_OK) {
-		conf_name = g_strdup_printf(_("GroupWise Conference %d"),
-									++user->conference_count);
+		conf_name = _get_conference_name(++user->conference_count);
 		chat = serv_got_joined_chat(gc, user->conference_count, conf_name);
 		if (chat) {
 
@@ -628,7 +656,6 @@
 				}
 			}
 		}
-		g_free(conf_name);
 	}
 }
 
@@ -656,8 +683,8 @@
 	} else {
 		gc = gaim_account_get_connection(user->client_data);
 		err =
-			g_strdup_printf(_("Could not get details for user %s (0x%X)."), name,
-							ret_code);
+			g_strdup_printf(_("Could not get details for user %s (%s)."),
+					name, nm_error_to_string (ret_code));
 		gaim_notify_error(gc, NULL, err, NULL);
 		g_free(err);
 	}
@@ -666,6 +693,312 @@
 		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)
+{
+	GaimConnection *gc;
+	NMUserRecord *user_record = resp_data;
+	char *err;
+	gboolean allowed = (gboolean)user_data;
+	const char *display_id;
+
+	if (user == NULL)
+		return;
+
+	gc = gaim_account_get_connection(user->client_data);
+	display_id = nm_user_record_get_display_id(user_record);
+
+	if (ret_code == NM_OK) {
+
+		if (allowed) {
+
+			if (!g_slist_find_custom(gc->account->permit,
+									 display_id, (GCompareFunc)nm_utf8_strcasecmp)) {
+				gaim_privacy_permit_add(gc->account, display_id, TRUE);
+			}
+
+		} else {
+
+			if (!g_slist_find_custom(gc->account->permit,
+									 display_id, (GCompareFunc)nm_utf8_strcasecmp)) {
+				gaim_privacy_deny_add(gc->account, display_id, TRUE);
+			}
+		}
+
+	} else {
+
+		err = g_strdup_printf(_("Unable to add user to privacy list (%s)."),
+							  nm_error_to_string(ret_code));
+		gaim_notify_error(gc, NULL, err, NULL);
+		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)
+{
+	GaimConnection *gc;
+	NMUserRecord *user_record;
+	char *who = user_data;
+	char *err;
+	NMERR_T rc = NM_OK;
+	const char *display_id = NULL;
+
+	if (user == NULL)
+		return;
+
+	gc = gaim_account_get_connection(user->client_data);
+
+	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(gc->account->deny,
+									 display_id, (GCompareFunc)nm_utf8_strcasecmp)) {
+
+				gaim_privacy_deny_add(gc->account, display_id, TRUE);
+			}
+
+		} else {
+			rc = nm_send_get_details(user, who,
+									 _get_details_resp_add_privacy_item,
+									 (gpointer)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));
+		gaim_notify_error(gc, NULL, err, NULL);
+		g_free(err);
+
+	}
+
+	if (who)
+		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)
+{
+	GaimConnection *gc;
+	NMUserRecord *user_record;
+	char *who = user_data;
+	char *err;
+	NMERR_T rc = NM_OK;
+	const char *display_id = NULL;
+
+	if (user == NULL)
+		return;
+
+	gc = gaim_account_get_connection(user->client_data);
+
+	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(gc->account->permit,
+									 display_id,
+									 (GCompareFunc)nm_utf8_strcasecmp)) {
+
+				gaim_privacy_permit_add(gc->account, display_id, TRUE);
+			}
+
+		} else {
+			rc = nm_send_get_details(user, who,
+									 _get_details_resp_add_privacy_item,
+									 (gpointer)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));
+		gaim_notify_error(gc, NULL, err, NULL);
+		g_free(err);
+
+	}
+
+	if (who)
+		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;
+	GaimConnection *gc;
+	NMUserRecord *user_record = resp_data;
+	char *err;
+	gboolean allowed = (gboolean)user_data;
+	const char *dn, *display_id;
+
+	if (user == NULL)
+		return;
+
+	gc = gaim_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));
+		gaim_notify_error(gc, NULL, err, NULL);
+		g_free(err);
+
+	}
+}
+
+static void
+_remove_privacy_item_resp_cb(NMUser *user, NMERR_T ret_code,
+									gpointer resp_data, gpointer user_data)
+{
+	GaimConnection *gc;
+	char *who = user_data;
+	char *err;
+
+	if (user == NULL)
+		return;
+
+	if (ret_code != NM_OK) {
+
+		gc = gaim_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));
+		gaim_notify_error(gc, NULL, err, NULL);
+		g_free(err);
+	}
+
+	if (who)
+		g_free(who);
+}
+
+static void
+_set_privacy_default_resp_cb(NMUser *user, NMERR_T ret_code,
+									gpointer resp_data, gpointer user_data)
+{
+	GaimConnection *gc;
+	char *err;
+
+	if (user == NULL)
+		return;
+
+	if (ret_code != NM_OK) {
+
+		gc = gaim_account_get_connection(user->client_data);
+		err = g_strdup_printf(_("Unable to change server side privacy settings (%s)."),
+							  nm_error_to_string(ret_code));
+		gaim_notify_error(gc, NULL, err, NULL);
+		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;
+	GaimConnection *gc;
+	NMUserRecord *user_record = resp_data;
+	char *err;
+	const char *display_id;
+	GSList *cnode;
+	NMConference *conference;
+	gpointer chat;
+	long id = (long) user_data;
+
+	if (user == NULL)
+		return;
+
+	gc = gaim_account_get_connection(user->client_data);
+	display_id = nm_user_record_get_display_id(user_record);
+
+	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 (gaim_conv_chat_get_id(GAIM_CONV_CHAT(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));
+		gaim_notify_error(gc, NULL, err, NULL);
+		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;
+	GaimConnection *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 = gaim_account_get_connection(user->client_data);
+		gaim_notify_error(gc, NULL, err, NULL);
+		g_free(err);
+	}
+}
+
 /*******************************************************************************
  * Helper functions
  ******************************************************************************/
@@ -802,8 +1135,7 @@
 			nm_release_message(message);
 
 		} else {
-			rc = nm_send_create_conference(user, conf,
-										   _createconf_resp_send_msg, message);
+			rc = nm_send_create_conference(user, conf, _createconf_resp_send_msg, message);
 			_check_for_disconnect(user, rc);
 		}
 	}
@@ -843,11 +1175,11 @@
 	serv_got_update(gc, buddy->name, loggedin, 0, 0, idle, gstatus);
 }
 
-/* Iterate through the cached Gaim buddy list and remove all buddies
- * for this account.
+/* Iterate through the cached Gaim buddy list and remove buddies
+ * that are not in the server side list.
  */
 static void
-_remove_gaim_buddies(NMUser * user)
+_remove_gaim_buddies(NMUser *user)
 {
 	GaimBlistNode *gnode;
 	GaimBlistNode *cnode;
@@ -857,6 +1189,7 @@
 	GaimBuddyList *blist;
 	GSList *rem_list = NULL;
 	GSList *l;
+	NMFolder *folder = NULL;
 
 	if ((blist = gaim_get_blist())) {
 		for (gnode = blist->root; gnode; gnode = gnode->next) {
@@ -871,7 +1204,11 @@
 						continue;
 					buddy = (GaimBuddy *) bnode;
 					if (buddy->account == user->client_data) {
-						rem_list = g_slist_append(rem_list, buddy);
+						folder = nm_find_folder(user, group->name);
+						if (folder == NULL ||
+							!nm_folder_find_contact_by_display_id(folder, buddy->name)) {
+							rem_list = g_slist_append(rem_list, buddy);
+						}
 					}
 				}
 			}
@@ -899,6 +1236,14 @@
 	const char *name = NULL;
 	int status = 0;
 
+	/* Does the Gaim group exist already? */
+	group = gaim_find_group(nm_folder_get_name(folder));
+
+	if (group == NULL) {
+		group = gaim_group_new(nm_folder_get_name(folder));
+		gaim_blist_add_group(group, NULL);
+	}
+
 	/* Get each contact for this folder */
 	cnt = nm_folder_get_contact_count(folder);
 	for (i = 0; i < cnt; i++) {
@@ -907,17 +1252,15 @@
 
 			name = nm_contact_get_display_id(contact);
 			if (name) {
-				/* Add it to the gaim buddy list */
-				buddy = gaim_buddy_new(user->client_data,
-									   name,
-									   nm_contact_get_display_name(contact));
-
-				/* Does the Gaim group exist already? */
-				group = gaim_find_group(nm_folder_get_name(folder));
-
-				if (group == NULL) {
-					group = gaim_group_new(nm_folder_get_name(folder));
-					gaim_blist_add_group(group, NULL);
+
+				buddy = gaim_find_buddy_in_group(user->client_data, name, group);
+				if (buddy == NULL) {
+					/* Add it to the gaim buddy list */
+					buddy = gaim_buddy_new(user->client_data,
+										   name,
+										   nm_contact_get_display_name(contact));
+
+					gaim_blist_add_buddy(buddy, NULL, group, NULL);
 				}
 
 				/* Set the initial status for the buddy */
@@ -926,8 +1269,6 @@
 					status = nm_user_record_get_status(user_record);
 					text = nm_user_record_get_status_text(user_record);
 				}
-
-				gaim_blist_add_buddy(buddy, NULL, group, NULL);
 				_update_buddy_status(buddy, status, time(0));
 
 				/* Save the new buddy as part of the contact object */
@@ -941,7 +1282,6 @@
 			break;
 		}
 	}
-
 }
 
 /* Add all of the server side contacts to the Gaim buddy list. */
@@ -969,6 +1309,111 @@
 	}
 }
 
+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_gaim_buddies(user);
+	_add_gaim_buddies(user);
+}
+
+static void
+_sync_privacy_lists(NMUser *user)
+{
+	GSList *node = NULL, *rem_list = NULL;
+	GaimConnection *gc;
+	const char *name, *dn;
+	NMUserRecord *user_record;
+
+	if (user == NULL)
+		return;
+
+	gc = gaim_account_get_connection(user->client_data);
+	if (gc == NULL)
+		return;
+
+	/* Set the Gaim privacy setting */
+	if (user->default_deny) {
+		if (user->allow_list == NULL) {
+			gc->account->perm_deny = GAIM_PRIVACY_DENY_ALL;
+		} else {
+			gc->account->perm_deny = GAIM_PRIVACY_ALLOW_USERS;
+		}
+	} else {
+		if (user->deny_list == NULL) {
+			gc->account->perm_deny = GAIM_PRIVACY_ALLOW_ALL;
+		} else {
+			gc->account->perm_deny = GAIM_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(gc->account->permit,
+								 name, (GCompareFunc)nm_utf8_strcasecmp)) {
+			gaim_privacy_permit_add(gc->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(gc->account->deny,
+								 name, (GCompareFunc)nm_utf8_strcasecmp)) {
+			gaim_privacy_deny_add(gc->account, name, TRUE);
+		}
+	}
+
+
+	/*  Remove stuff */
+	for (node = gc->account->permit; node; node = node->next) {
+		dn = nm_lookup_dn(user, (char *)node->data);
+		if (dn != NULL &&
+			!g_slist_find_custom(user->allow_list,
+								 dn, (GCompareFunc)nm_utf8_strcasecmp)) {
+			rem_list = g_slist_append(rem_list, node->data);
+		}
+	}
+
+	if (rem_list) {
+		for (node = rem_list; node; node = node->next) {
+			gaim_privacy_permit_remove(gc->account, (char *)node->data, TRUE);
+		}
+		g_free(rem_list);
+		rem_list = NULL;
+	}
+
+	for (node = gc->account->deny; node; node = node->next) {
+		dn = nm_lookup_dn(user, (char *)node->data);
+		if (dn != NULL &&
+			!g_slist_find_custom(user->deny_list,
+								 dn, (GCompareFunc)nm_utf8_strcasecmp)) {
+			rem_list = g_slist_append(rem_list, node->data);
+		}
+	}
+
+	if (rem_list) {
+		for (node = rem_list; node; node = node->next) {
+			gaim_privacy_deny_remove(gc->account, (char *)node->data, TRUE);
+		}
+		g_slist_free(rem_list);
+	}
+}
+
 /* Display a dialog box showing the properties for the given user record */
 static void
 _show_info(GaimConnection * gc, NMUserRecord * user_record)
@@ -1073,6 +1518,59 @@
 	g_slist_free(parms);
 }
 
+static void
+_initiate_conference_cb(GaimConnection *gc, const char *who)
+{
+	NMUser *user;
+	const char *conf_name;
+	GaimConversation *chat = NULL;
+	NMUserRecord *user_record;
+	NMConference *conference;
+
+	user = gc->proto_data;
+	if (user == NULL)
+		return;
+
+	/* We should already have a userrecord for the buddy */
+	user_record = nm_find_user_record(user, who);
+	if (user_record == NULL)
+		return;
+
+	conf_name = _get_conference_name(++user->conference_count);
+	chat = 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;
+
+	if (name)
+		g_free(name);
+
+	name = g_strdup_printf(_("GroupWise Conference %d"), id);
+
+	return name;
+}
+
+void
+_show_privacy_locked_error(GaimConnection *gc, NMUser *user)
+{
+	char *err;
+
+	err = g_strdup_printf(_("Unable to change server side privacy settings (%s)."),
+						  nm_error_to_string(NMERR_ADMIN_LOCKED));
+	gaim_notify_error(gc, NULL, err, NULL);
+	g_free(err);
+}
+
 /*******************************************************************************
  * Connect and recv callbacks
  ******************************************************************************/
@@ -1104,6 +1602,7 @@
 	if (rc != NM_OK) {
 
 		if (_is_disconnect_error(rc)) {
+
 			gaim_connection_error(gc,
 								  _("Error communicating with server."
 									" Closing connection."));
@@ -1111,8 +1610,8 @@
 
 			char *error;
 
-			error = g_strdup_printf(_("Error processing event or response."
-									  " (0x%X)"), rc);
+			error = g_strdup_printf(_("Error processing event or response (%s)."),
+									nm_error_to_string (rc));
 			gaim_notify_error(gc, NULL, error, NULL);
 			g_free(error);
 
@@ -1282,6 +1781,26 @@
 }
 
 static void
+_evt_conference_invite_notify(NMUser * user, NMEvent * event)
+{
+	GaimConversation *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));
+		gaim_conversation_write(gconv, NULL, str,
+								GAIM_MESSAGE_SYSTEM, time(NULL));
+		g_free(str);
+	}
+}
+
+static void
 _evt_conference_invite(NMUser * user, NMEvent * event)
 {
 	NMUserRecord *ur;
@@ -1330,7 +1849,8 @@
 	NMConference *conference = NULL;
 	NMUserRecord *ur = NULL;
 	const char *name;
-	char *conf_name;
+	const char *conf_name;
+	GList *list = NULL;
 
 	gc = gaim_account_get_connection(user->client_data);
 	if (gc == NULL)
@@ -1342,11 +1862,9 @@
 		if (nm_conference_get_participant_count(conference) == 2 && chat == NULL) {
 			ur = nm_conference_get_participant(conference, 0);
 			if (ur) {
-				conf_name = g_strdup_printf(_("GroupWise Conference %d"),
-											++user->conference_count);
+				conf_name = _get_conference_name(++user->conference_count);
 				chat =
 					serv_got_joined_chat(gc, user->conference_count, conf_name);
-				g_free(conf_name);
 				if (chat) {
 
 					nm_conference_set_data(conference, (gpointer) chat);
@@ -1362,7 +1880,10 @@
 			ur = nm_find_user_record(user, nm_event_get_source(event));
 			if (ur) {
 				name = nm_user_record_get_display_id(ur);
-				gaim_conv_chat_add_user(GAIM_CONV_CHAT(chat), name, NULL);
+				list = gaim_conv_chat_get_users(GAIM_CONV_CHAT(chat));
+				if (!g_list_find_custom(list, name, (GCompareFunc)nm_utf8_strcasecmp)) {
+					gaim_conv_chat_add_user(GAIM_CONV_CHAT(chat), name, NULL);
+				}
 			}
 		}
 	}
@@ -1504,7 +2025,7 @@
 			/* Someone else has been invited to join a
 			 * conference that we are currently a part of
 			 */
-			/* TODO: show the invite notify in chat window */
+			_evt_conference_invite_notify(user, event);
 			break;
 		case NMEVT_CONFERENCE_INVITE:
 			/* We have been invited to join a conference */
@@ -1661,8 +2182,7 @@
 			 * 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);
+			rc = nm_send_create_conference(user, conf, _createconf_resp_send_msg, message);
 			_check_for_disconnect(user, rc);
 
 			done = FALSE;
@@ -1786,6 +2306,44 @@
 	serv_got_chat_left(gc, id);
 }
 
+void
+novell_chat_invite(GaimConnection *gc, int id,
+				   const char *message, const char *who)
+{
+	NMConference *conference;
+	NMUser *user;
+	GaimConversation *chat;
+	GSList *cnode;
+	NMERR_T rc = NM_OK;
+	NMUserRecord *user_record = NULL;
+
+	if (gc == NULL)
+		return;
+
+	user = gc->proto_data;
+	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, (gpointer)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 (gaim_conv_chat_get_id(GAIM_CONV_CHAT(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(GaimConnection * gc, int id, const char *text)
 {
@@ -1814,7 +2372,14 @@
 
 				nm_message_set_conference(message, conference);
 
-				rc = nm_send_message(user, message, _send_message_resp_cb);
+				/* 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)) {
@@ -1841,6 +2406,7 @@
 		}
 	}
 
+
 	/* The conference was not found, must be closed */
 	chat = gaim_find_chat(gc, id);
 	if (chat) {
@@ -2342,6 +2908,386 @@
 		g_free(text);
 }
 
+static void
+novell_add_permit(GaimConnection *gc, const char *who)
+{
+	NMUser *user;
+	NMERR_T rc = NM_OK;
+	const char *name = who;
+
+	if (gc == NULL || who == NULL)
+		return;
+
+	user = gc->proto_data;
+	if (user == NULL)
+		return;
+
+	/* Remove first -- we will add it back in when we get
+	 * the okay from the server
+	 */
+	gaim_privacy_permit_remove(gc->account, 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,
+									 (gpointer)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(GaimConnection *gc, const char *who)
+{
+	NMUser *user;
+	NMERR_T rc = NM_OK;
+	const char *name = who;
+
+	if (gc == NULL || who == NULL)
+		return;
+
+	user = gc->proto_data;
+	if (user == NULL)
+		return;
+
+	/* Remove first -- we will add it back in when we get
+	 * the okay from the server
+	 */
+	gaim_privacy_deny_remove(gc->account, 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,
+									 (gpointer)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(GaimConnection *gc, const char *who)
+{
+	NMUser *user;
+	NMERR_T rc = NM_OK;
+	const char *dn = NULL;
+
+	if (gc == NULL || who == NULL)
+		return;
+
+	user = gc->proto_data;
+	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(GaimConnection *gc, const char *who)
+{
+	NMUser *user;
+	NMERR_T rc = NM_OK;
+	const char *dn = NULL;
+
+	if (gc == NULL || who == NULL)
+		return;
+
+	user = gc->proto_data;
+	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(GaimConnection *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;
+
+	if (gc == NULL)
+		return;
+
+	user = gc->proto_data;
+	if (user == NULL)
+		return;
+
+	if (set_permit == FALSE) {
+		set_permit = TRUE;
+		return;
+	}
+
+	if (nm_user_is_privacy_locked(user)) {
+		_show_privacy_locked_error(gc, user);
+		_sync_privacy_lists(user);
+		return;
+	}
+
+	switch (gc->account->perm_deny) {
+
+		case GAIM_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 GAIM_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 GAIM_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(gc->account->permit,
+												 name, (GCompareFunc)nm_utf8_strcasecmp)) {
+							gaim_privacy_permit_add(gc->account, name , TRUE);
+						}
+					}
+				}
+
+				for (node = gc->account->permit; 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->allow_list,
+												 dn, (GCompareFunc)nm_utf8_strcasecmp)) {
+							rc = nm_send_create_privacy_item(user, dn, TRUE,
+															 _create_privacy_item_deny_resp_cb,
+															 g_strdup(dn));
+						}
+					} else {
+						gaim_privacy_permit_remove(gc->account, (char *)node->data, TRUE);
+					}
+				}
+			}
+			break;
+
+		case GAIM_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(gc->account->deny,
+												 name, (GCompareFunc)nm_utf8_strcasecmp)) {
+							gaim_privacy_deny_add(gc->account, name , TRUE);
+						}
+					}
+				}
+
+				for (node = gc->account->deny; 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)nm_utf8_strcasecmp)) {
+							rc = nm_send_create_privacy_item(user, dn, FALSE,
+															 _create_privacy_item_deny_resp_cb,
+															 g_strdup(name));
+						}
+					} else {
+						gaim_privacy_deny_remove(gc->account, (char *)node->data, TRUE);
+					}
+				}
+
+			}
+			break;
+
+		case GAIM_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)nm_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)nm_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_buddy_menu(GaimConnection *gc, const char *who)
+{
+	GList *list = NULL;
+	struct proto_buddy_menu *pbm;
+
+	pbm = g_new0(struct proto_buddy_menu, 1);
+	pbm->label = _("Initiate _Chat");
+	pbm->callback = _initiate_conference_cb;
+	pbm->gc = gc;
+	list = g_list_append(list, pbm);
+
+	return list;
+}
+
 static GaimPluginProtocolInfo prpl_info = {
 	GAIM_PRPL_API_VERSION,
 	0,
@@ -2353,7 +3299,7 @@
 	novell_tooltip_text,
 	novell_away_states,
 	NULL,						/* prpl_actions */
-	NULL,						/* buddy_menu */
+	novell_buddy_menu,
 	NULL,						/* chat_info */
 	novell_login,
 	novell_close,
@@ -2371,15 +3317,15 @@
 	NULL,						/* add_buddies */
 	novell_remove_buddy,
 	NULL,						/* remove_buddies */
-	NULL,						/* add_permit */
-	NULL,						/* add_deny */
-	NULL,						/* rem_permit */
-	NULL,						/* rem_deny */
-	NULL,						/* set_permit_deny */
+	novell_add_permit,
+	novell_add_deny,
+	novell_rem_permit,
+	novell_rem_deny,
+	novell_set_permit_deny,
 	NULL,						/* warn */
 	NULL,						/* join_chat */
 	NULL,						/* reject_chat ?? */
-	NULL,						/* chat_invite */
+	novell_chat_invite,			/* chat_invite */
 	novell_chat_leave,
 	NULL,						/* chat_whisper */
 	novell_chat_send,