summary refs log tree commit diff stats
path: root/src/common/proto-irc.c
AgeCommit message (Expand)Author
2021-10-20Adjust parsing of RPL_WHOISSPECIAL to handle missing : for single-word whois ...alicetries
2021-08-23Remove some weird guesswork on the 004 numeric. (#2621)Sadie Powell
2021-06-22Fix parsing +beI lists on InspIRCd.Sadie Powell
2021-06-20Implement generic support for IRCv3 standard replies. (#2589)Sadie Powell
2021-06-19Replace identify-msg support with solanum.chat/identify-msg.Sadie Powell
2021-05-23Merge pull request #1457 from arodland/forgiving-ctcpAndrew Rodland
2021-05-23Implement support for the IRCv3 account-tag specification. (#2572)Sadie Powell
2021-05-23Implement support for the IRCv3 invite-notify specification. (#2574)Sadie Powell
2021-05-23Implement support for the IRCv3 SETNAME specification. (#2571)Sadie Powell
2021-03-03Better handle various ctime() calls failingPatrick Griffis
2020-05-26Fix parsing of 313Patrick Griffis
2019-12-20Follow more modern conventions for USER messagePatrick Griffis
2019-11-24Fix capability negotiation ending before sasl finishes with multi-line capPatrick Griffis
2019-01-30Remove : from various trailing parameters (#2301)linuxdaemon
2018-05-08Fix sending PASS with spaces or starting with :jkhsjdhjs
2018-03-09Various fixes for mingwPatrick Griffis
2018-03-04Fix out of bounds read when DCC message sender contains quotesPatrick Griffis
2017-02-18Better handle mode numerics starting with :Patrick Griffis
2016-03-24Only intercept QuakeNet challengeauth when enabledPatrick Griffis
2016-01-29Remove DH-AES/DH-BLOWFISH mechanisms and misc cleanupPatrick Griffis
2016-01-29Add support for chghostPatrick Griffis
2016-01-29Add support for CAP 3.2Patrick Griffis
2016-01-28Properly handle a few args optionally starting with :Patrick Griffis
2016-01-28Handle PING and AUTHENTICATE with a prefixPatrick Griffis
2016-01-28Add support for cap-notifyPatrick Griffis
2015-04-21Remove r from mode buttonsTingPing
2014-12-31Fix some unused warningsTingPing
2014-12-28Fix building as c89TingPing
2014-12-28Use glib for all allocationsTingPing
2014-12-15Fixed some more signed-unsigned-comparison warnings.Arnavion
2014-12-04Fix some obvious type warnings.Arnavion
2014-05-12Fixed truncation of the reason when receiving a KILL message.Derp Derpingston
2014-04-06Add account to Join eventTingPing
2014-01-12Tweak 908 syntaxTingPing
2014-01-12Support numeric 908TingPing
2013-11-28Print{,_attr} and server{,_attr} hooks were incorrectly handled when bothDiogo Sousa
2013-10-03Topic bar improvementsTingPing
2013-09-20Fix messages on networks that have chantypes the same as nick prefixesTingPing
2013-09-07Disable challangeauth without opensslTingPing
2013-09-07Implement BLOWFISh, AES, and EXTERNAL SASL mechanismsTingPing
2013-08-20Handle lists in MONITOR repliesTingPing
2013-08-07Format Channel URLsTingPing
2013-07-10Indentation fixes and code cleanup.Diogo Sousa
2013-07-09Now hexchat_hook_server_attrs() and hexchat_hook_print_attrs() is calledDiogo Sousa
2013-07-04Default to nickserv when using /ghost or /id commandsTingPing
2013-07-02Fixed get_timezone() to work on windows.Diogo Sousa
2013-07-02Now handle_message_tag_time() doesn't use "timezone" since it is not definedDiogo Sousa
2013-06-30Fixed server-time timestamps issue regarding local time.Diogo Sousa
2013-06-28Fixed another bug in handle_message_tag_time(): tm_mon is between 0 and 11.Diogo Sousa
2013-06-28Fixed bug in handle_message_tag_time().Diogo Sousa
5' href='#n425'>425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459
/* 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;

	user = userlist_find (sess, name);
	if (!user)
		return FALSE;

	userlist_remove_user (sess, user);
	return TRUE;
}

void
userlist_remove_user (struct session *sess, struct User *user)
{
	int pos;
	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);
}

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;
}