summary refs log tree commit diff stats
path: root/src/common/userlist.c
diff options
context:
space:
mode:
authorberkeviktor@aol.com <berkeviktor@aol.com>2011-02-24 04:14:30 +0100
committerberkeviktor@aol.com <berkeviktor@aol.com>2011-02-24 04:14:30 +0100
commit4a6ceffb98a0b785494f680d3776c4bfc4052f9e (patch)
tree850703c1c841ccd99f58d0b06084615aaebe782c /src/common/userlist.c
parentf16af8be941b596dedac3bf4e371ee2d21f4b598 (diff)
add xchat r1489
Diffstat (limited to 'src/common/userlist.c')
-rw-r--r--src/common/userlist.c454
1 files changed, 454 insertions, 0 deletions
diff --git a/src/common/userlist.c b/src/common/userlist.c
new file mode 100644
index 00000000..18ac4bed
--- /dev/null
+++ b/src/common/userlist.c
@@ -0,0 +1,454 @@
+/* X-Chat
+ * Copyright (C) 1998 Peter Zelezny.
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "xchat.h"
+#include "modes.h"
+#include "fe.h"
+#include "notify.h"
+#include "tree.h"
+#include "xchatc.h"
+#include "util.h"
+
+
+static int
+nick_cmp_az_ops (server *serv, struct User *user1, struct User *user2)
+{
+	unsigned int access1 = user1->access;
+	unsigned int access2 = user2->access;
+	int pos;
+
+	if (access1 != access2)
+	{
+		for (pos = 0; pos < USERACCESS_SIZE; pos++)
+		{
+			if ((access1&(1<<pos)) && (access2&(1<<pos)))
+				break;
+			if ((access1&(1<<pos)) && !(access2&(1<<pos)))
+				return -1;
+			if (!(access1&(1<<pos)) && (access2&(1<<pos)))
+				return 1;
+		}
+	}
+
+	return serv->p_cmp (user1->nick, user2->nick);
+}
+
+static int
+nick_cmp_alpha (struct User *user1, struct User *user2, server *serv)
+{
+	return serv->p_cmp (user1->nick, user2->nick);
+}
+
+static int
+nick_cmp (struct User *user1, struct User *user2, server *serv)
+{
+	switch (prefs.userlist_sort)
+	{
+	case 0:
+		return nick_cmp_az_ops (serv, user1, user2);
+	case 1:
+		return serv->p_cmp (user1->nick, user2->nick);
+	case 2:
+		return -1 * nick_cmp_az_ops (serv, user1, user2);
+	case 3:
+		return -1 * serv->p_cmp (user1->nick, user2->nick);
+	default:
+		return -1;
+	}
+}
+
+/*
+ insert name in appropriate place in linked list. Returns row number or:
+  -1: duplicate
+*/
+
+static int
+userlist_insertname (session *sess, struct User *newuser)
+{
+	if (!sess->usertree)
+	{
+		sess->usertree = tree_new ((tree_cmp_func *)nick_cmp, sess->server);
+		sess->usertree_alpha = tree_new ((tree_cmp_func *)nick_cmp_alpha, sess->server);
+	}
+
+	tree_insert (sess->usertree_alpha, newuser);
+	return tree_insert (sess->usertree, newuser);
+}
+
+void
+userlist_set_away (struct session *sess, char *nick, unsigned int away)
+{
+	struct User *user;
+
+	user = userlist_find (sess, nick);
+	if (user)
+	{
+		if (user->away != away)
+		{
+			user->away = away;
+			/* rehash GUI */
+			fe_userlist_rehash (sess, user);
+			if (away)
+				fe_userlist_update (sess, user);
+		}
+	}
+}
+
+int
+userlist_add_hostname (struct session *sess, char *nick, char *hostname,
+							  char *realname, char *servername, unsigned int away)
+{
+	struct User *user;
+
+	user = userlist_find (sess, nick);
+	if (user)
+	{
+		if (!user->hostname && hostname)
+			user->hostname = strdup (hostname);
+		if (!user->realname && realname)
+			user->realname = strdup (realname);
+		if (!user->servername && servername)
+			user->servername = strdup (servername);
+
+		if (away != 0xff)
+		{
+			if (prefs.showhostname_in_userlist || user->away != away)
+			{
+				user->away = away;
+				fe_userlist_rehash (sess, user);
+			}
+			user->away = away;
+		}
+
+		fe_userlist_update (sess, user);
+
+		return 1;
+	}
+	return 0;
+}
+
+static int
+free_user (struct User *user, gpointer data)
+{
+	if (user->realname)
+		free (user->realname);
+	if (user->hostname)
+		free (user->hostname);
+	if (user->servername)
+		free (user->servername);
+	free (user);
+
+	return TRUE;
+}
+
+void
+userlist_free (session *sess)
+{
+	tree_foreach (sess->usertree, (tree_traverse_func *)free_user, NULL);
+	tree_destroy (sess->usertree);
+	tree_destroy (sess->usertree_alpha);
+
+	sess->usertree = NULL;
+	sess->usertree_alpha = NULL;
+	sess->me = NULL;
+
+	sess->ops = 0;
+	sess->hops = 0;
+	sess->voices = 0;
+	sess->total = 0;
+}
+
+void
+userlist_clear (session *sess)
+{
+	fe_userlist_clear (sess);
+	userlist_free (sess);
+	fe_userlist_numbers (sess);
+}
+
+static int
+find_cmp (const char *name, struct User *user, server *serv)
+{
+	return serv->p_cmp ((char *)name, user->nick);
+}
+
+struct User *
+userlist_find (struct session *sess, char *name)
+{
+	int pos;
+
+	if (sess->usertree_alpha)
+		return tree_find (sess->usertree_alpha, name,
+								(tree_cmp_func *)find_cmp, sess->server, &pos);
+
+	return NULL;
+}
+
+struct User *
+userlist_find_global (struct server *serv, char *name)
+{
+	struct User *user;
+	session *sess;
+	GSList *list = sess_list;
+	while (list)
+	{
+		sess = (session *) list->data;
+		if (sess->server == serv)
+		{
+			user = userlist_find (sess, name);
+			if (user)
+				return user;
+		}
+		list = list->next;
+	}
+	return 0;
+}
+
+static void
+update_counts (session *sess, struct User *user, char prefix,
+					int level, int offset)
+{
+	switch (prefix)
+	{
+	case '@':
+		user->op = level;
+		sess->ops += offset;
+		break;
+	case '%':
+		user->hop = level;
+		sess->hops += offset;
+		break;
+	case '+':
+		user->voice = level;
+		sess->voices += offset;
+		break;
+	}
+}
+
+void
+userlist_update_mode (session *sess, char *name, char mode, char sign)
+{
+	int access;
+	int offset = 0;
+	int level;
+	int pos;
+	char prefix;
+	struct User *user;
+
+	user = userlist_find (sess, name);
+	if (!user)
+		return;
+
+	/* remove from binary trees, before we loose track of it */
+	tree_remove (sess->usertree, user, &pos);
+	tree_remove (sess->usertree_alpha, user, &pos);
+
+	/* which bit number is affected? */
+	access = mode_access (sess->server, mode, &prefix);
+
+	if (sign == '+')
+	{
+		level = TRUE;
+		if (!(user->access & (1 << access)))
+		{
+			offset = 1;
+			user->access |= (1 << access);
+		}
+	} else
+	{
+		level = FALSE;
+		if (user->access & (1 << access))
+		{
+			offset = -1;
+			user->access &= ~(1 << access);
+		}
+	}
+
+	/* now what is this users highest prefix? e.g. @ for ops */
+	user->prefix[0] = get_nick_prefix (sess->server, user->access);
+
+	/* update the various counts using the CHANGED prefix only */
+	update_counts (sess, user, prefix, level, offset);
+
+	/* insert it back into its new place */
+	tree_insert (sess->usertree_alpha, user);
+	pos = tree_insert (sess->usertree, user);
+
+	/* let GTK move it too */
+	fe_userlist_move (sess, user, pos);
+	fe_userlist_numbers (sess);
+}
+
+int
+userlist_change (struct session *sess, char *oldname, char *newname)
+{
+	struct User *user = userlist_find (sess, oldname);
+	int pos;
+
+	if (user)
+	{
+		tree_remove (sess->usertree, user, &pos);
+		tree_remove (sess->usertree_alpha, user, &pos);
+
+		safe_strcpy (user->nick, newname, NICKLEN);
+
+		tree_insert (sess->usertree_alpha, user);
+
+		fe_userlist_move (sess, user, tree_insert (sess->usertree, user));
+		fe_userlist_numbers (sess);
+
+		return 1;
+	}
+
+	return 0;
+}
+
+int
+userlist_remove (struct session *sess, char *name)
+{
+	struct User *user;
+	int pos;
+
+	user = userlist_find (sess, name);
+	if (!user)
+		return FALSE;
+
+	if (user->voice)
+		sess->voices--;
+	if (user->op)
+		sess->ops--;
+	if (user->hop)
+		sess->hops--;
+	sess->total--;
+	fe_userlist_numbers (sess);
+	fe_userlist_remove (sess, user);
+
+	if (user == sess->me)
+		sess->me = NULL;
+
+	tree_remove (sess->usertree, user, &pos);
+	tree_remove (sess->usertree_alpha, user, &pos);
+	free_user (user, NULL);
+
+	return TRUE;
+}
+
+void
+userlist_add (struct session *sess, char *name, char *hostname)
+{
+	struct User *user;
+	int row, prefix_chars;
+	unsigned int acc;
+
+	acc = nick_access (sess->server, name, &prefix_chars);
+
+	notify_set_online (sess->server, name + prefix_chars);
+
+	user = malloc (sizeof (struct User));
+	memset (user, 0, sizeof (struct User));
+
+	user->access = acc;
+
+	/* assume first char is the highest level nick prefix */
+	if (prefix_chars)
+		user->prefix[0] = name[0];
+
+	/* add it to our linked list */
+	if (hostname)
+		user->hostname = strdup (hostname);
+	safe_strcpy (user->nick, name + prefix_chars, NICKLEN);
+	/* is it me? */
+	if (!sess->server->p_cmp (user->nick, sess->server->nick))
+		user->me = TRUE;
+	row = userlist_insertname (sess, user);
+
+	/* duplicate? some broken servers trigger this */
+	if (row == -1)
+	{
+		if (user->hostname)
+			free (user->hostname);
+		free (user);
+		return;
+	}
+
+	sess->total++;
+
+	/* most ircds don't support multiple modechars infront of the nickname
+      for /NAMES - though they should. */
+	while (prefix_chars)
+	{
+		update_counts (sess, user, name[0], TRUE, 1);
+		name++;
+		prefix_chars--;
+	}
+
+	if (user->me)
+		sess->me = user;
+
+	fe_userlist_insert (sess, user, row, FALSE);
+	fe_userlist_numbers (sess);
+}
+
+static int
+rehash_cb (struct User *user, session *sess)
+{
+	fe_userlist_rehash (sess, user);
+	return TRUE;
+}
+
+void
+userlist_rehash (session *sess)
+{
+	tree_foreach (sess->usertree_alpha, (tree_traverse_func *)rehash_cb, sess);
+}
+
+static int
+flat_cb (struct User *user, GSList **list)
+{
+	*list = g_slist_prepend (*list, user);
+	return TRUE;
+}
+
+GSList *
+userlist_flat_list (session *sess)
+{
+	GSList *list = NULL;
+
+	tree_foreach (sess->usertree_alpha, (tree_traverse_func *)flat_cb, &list);
+	return g_slist_reverse (list);
+}
+
+static int
+double_cb (struct User *user, GList **list)
+{
+	*list = g_list_prepend(*list, user);
+	return TRUE;
+}
+
+GList *
+userlist_double_list(session *sess)
+{
+	GList *list = NULL;
+
+	tree_foreach (sess->usertree_alpha, (tree_traverse_func *)double_cb, &list);
+	return list;
+}