summary refs log tree commit diff stats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/cfgfiles.c2
-rw-r--r--src/common/ctcp.c23
-rw-r--r--src/common/ctcp.h4
-rw-r--r--src/common/dcc.c20
-rw-r--r--src/common/dcc.h4
-rw-r--r--src/common/hexchat.h2
-rw-r--r--src/common/inbound.c424
-rw-r--r--src/common/inbound.h105
-rw-r--r--src/common/modes.c86
-rw-r--r--src/common/modes.h7
-rw-r--r--src/common/notify.c44
-rw-r--r--src/common/notify.h13
-rw-r--r--src/common/outbound.c46
-rw-r--r--src/common/proto-irc.c521
-rw-r--r--src/common/proto-irc.h17
-rw-r--r--src/common/server.c1
-rw-r--r--src/common/text.c36
-rw-r--r--src/common/text.h12
-rw-r--r--src/common/userlist.c5
-rw-r--r--src/common/userlist.h4
-rw-r--r--src/fe-gtk/setup.c5
21 files changed, 923 insertions, 458 deletions
diff --git a/src/common/cfgfiles.c b/src/common/cfgfiles.c
index 20f1e848..008a0787 100644
--- a/src/common/cfgfiles.c
+++ b/src/common/cfgfiles.c
@@ -507,6 +507,7 @@ const struct prefs vars[] =
 
 	{"irc_auto_rejoin", P_OFFINT (hex_irc_auto_rejoin), TYPE_BOOL},
 	{"irc_ban_type", P_OFFINT (hex_irc_ban_type), TYPE_INT},
+	{"irc_cap_server_time", P_OFFINT (hex_irc_cap_server_time), TYPE_BOOL},
 	{"irc_conf_mode", P_OFFINT (hex_irc_conf_mode), TYPE_BOOL},
 	{"irc_extra_hilight", P_OFFSET (hex_irc_extra_hilight), TYPE_STR},
 	{"irc_hide_version", P_OFFINT (hex_irc_hide_version), TYPE_BOOL},
@@ -748,6 +749,7 @@ load_default_config(void)
 	prefs.hex_text_thin_sep = 1;
 	prefs.hex_text_wordwrap = 1;
 	prefs.hex_url_grabber = 1;
+	prefs.hex_irc_cap_server_time = 0;
 
 	/* NUMBERS */
 	prefs.hex_away_size_max = 300;
diff --git a/src/common/ctcp.c b/src/common/ctcp.c
index ebfb604d..b4fb55b7 100644
--- a/src/common/ctcp.c
+++ b/src/common/ctcp.c
@@ -85,7 +85,8 @@ ctcp_check (session *sess, char *nick, char *word[], char *word_eol[],
 
 void
 ctcp_handle (session *sess, char *to, char *nick, char *ip,
-				 char *msg, char *word[], char *word_eol[], int id)
+				 char *msg, char *word[], char *word_eol[], int id,
+				 const message_tags_data *tags_data)
 {
 	char *po;
 	session *chansess;
@@ -103,7 +104,7 @@ ctcp_handle (session *sess, char *to, char *nick, char *ip,
 		if (!ctcp_check (sess, nick, word, word_eol, word[4] + ctcp_offset))
 		{
 			if (!ignore_check (word[1], IG_DCC))
-				handle_dcc (sess, nick, word, word_eol);
+				handle_dcc (sess, nick, word, word_eol, tags_data);
 		}
 		return;
 	}
@@ -128,7 +129,7 @@ ctcp_handle (session *sess, char *to, char *nick, char *ip,
 		if (ctcp_check (sess, nick, word, word_eol, word[4] + ctcp_offset))
 			goto generic;
 
-		inbound_action (sess, to, nick, ip, msg + 7, FALSE, id);
+		inbound_action (sess, to, nick, ip, msg + 7, FALSE, id, tags_data);
 		return;
 	}
 
@@ -161,12 +162,13 @@ ctcp_handle (session *sess, char *to, char *nick, char *ip,
 				if (!chansess)
 					chansess = sess;
 
-				EMIT_SIGNAL (XP_TE_CTCPSNDC, chansess, word[5],
-								 nick, to, NULL, 0);
+				EMIT_SIGNAL_TIMESTAMP (XP_TE_CTCPSNDC, chansess, word[5],
+											  nick, to, NULL, 0, tags_data->timestamp);
 			} else
 			{
-				EMIT_SIGNAL (XP_TE_CTCPSND, sess->server->front_session, word[5],
-								 nick, NULL, NULL, 0);
+				EMIT_SIGNAL_TIMESTAMP (XP_TE_CTCPSND, sess->server->front_session,
+											  word[5], nick, NULL, NULL, 0,
+											  tags_data->timestamp);
 			}
 
 			/* don't let IRCers specify path */
@@ -187,13 +189,14 @@ generic:
 
 	if (!is_channel (sess->server, to))
 	{
-		EMIT_SIGNAL (XP_TE_CTCPGEN, sess->server->front_session, msg, nick,
-						 NULL, NULL, 0);
+		EMIT_SIGNAL_TIMESTAMP (XP_TE_CTCPGEN, sess->server->front_session, msg,
+									  nick, NULL, NULL, 0, tags_data->timestamp);
 	} else
 	{
 		chansess = find_channel (sess->server, to);
 		if (!chansess)
 			chansess = sess;
-		EMIT_SIGNAL (XP_TE_CTCPGENC, chansess, msg, nick, to, NULL, 0);
+		EMIT_SIGNAL_TIMESTAMP (XP_TE_CTCPGENC, chansess, msg, nick, to, NULL, 0,
+									  tags_data->timestamp);
 	}
 }
diff --git a/src/common/ctcp.h b/src/common/ctcp.h
index 54e33f4c..9d811e57 100644
--- a/src/common/ctcp.h
+++ b/src/common/ctcp.h
@@ -20,6 +20,8 @@
 #ifndef HEXCHAT_CTCP_H
 #define HEXCHAT_CTCP_H
 
-void ctcp_handle (session *sess, char *to, char *nick, char *ip, char *msg, char *word[], char *word_eol[], int id);
+void ctcp_handle (session *sess, char *to, char *nick, char *ip, char *msg,
+						char *word[], char *word_eol[], int id,
+						const message_tags_data *tags_data);
 
 #endif
diff --git a/src/common/dcc.c b/src/common/dcc.c
index 4980cabc..c0527510 100644
--- a/src/common/dcc.c
+++ b/src/common/dcc.c
@@ -509,6 +509,7 @@ dcc_chat_line (struct DCC *dcc, char *line)
 	int len;
 	gsize utf_len;
 	char portbuf[32];
+	message_tags_data no_tags = MESSAGE_TAGS_DATA_INIT;
 
 	len = strlen (line);
 	if (dcc->serv->using_cp1255)
@@ -576,10 +577,11 @@ dcc_chat_line (struct DCC *dcc, char *line)
 		po = strchr (line + 8, '\001');
 		if (po)
 			po[0] = 0;
-		inbound_action (sess, dcc->serv->nick, dcc->nick, "", line + 8, FALSE, FALSE);
+		inbound_action (sess, dcc->serv->nick, dcc->nick, "", line + 8, FALSE,
+							 FALSE, &no_tags);
 	} else
 	{
-		inbound_privmsg (dcc->serv, dcc->nick, "", line, FALSE);
+		inbound_privmsg (dcc->serv, dcc->nick, "", line, FALSE, &no_tags);
 	}
 	if (utf)
 		g_free (utf);
@@ -2375,8 +2377,8 @@ dcc_add_file (session *sess, char *file, DCC_SIZE size, int port, char *nick, gu
 }
 
 void
-handle_dcc (struct session *sess, char *nick, char *word[],
-				char *word_eol[])
+handle_dcc (struct session *sess, char *nick, char *word[], char *word_eol[],
+				const message_tags_data *tags_data)
 {
 	char tbuf[512];
 	struct DCC *dcc;
@@ -2472,8 +2474,9 @@ handle_dcc (struct session *sess, char *nick, char *word[],
 				dcc->serv->p_ctcp (dcc->serv, dcc->nick, tbuf);
 			}
 			sprintf (tbuf, "%"DCC_SFMT, dcc->pos);
-			EMIT_SIGNAL (XP_TE_DCCRESUMEREQUEST, sess, nick,
-							 file_part (dcc->file), tbuf, NULL, 0);
+			EMIT_SIGNAL_TIMESTAMP (XP_TE_DCCRESUMEREQUEST, sess, nick,
+										  file_part (dcc->file), tbuf, NULL, 0,
+										  tags_data->timestamp);
 		}
 		return;
 	}
@@ -2543,8 +2546,9 @@ handle_dcc (struct session *sess, char *nick, char *word[],
 
 	} else
 	{
-		EMIT_SIGNAL (XP_TE_DCCGENERICOFFER, sess->server->front_session,
-						 word_eol[4] + 2, nick, NULL, NULL, 0);
+		EMIT_SIGNAL_TIMESTAMP (XP_TE_DCCGENERICOFFER, sess->server->front_session,
+									  word_eol[4] + 2, nick, NULL, NULL, 0,
+									  tags_data->timestamp);
 	}
 }
 
diff --git a/src/common/dcc.h b/src/common/dcc.h
index 32d0ed5f..e3163c8a 100644
--- a/src/common/dcc.h
+++ b/src/common/dcc.h
@@ -20,6 +20,7 @@
 /* dcc.h */
 
 #include <time.h>						/* for time_t */
+#include "proto-irc.h"
 
 #ifndef HEXCHAT_DCC_H
 #define HEXCHAT_DCC_H
@@ -127,7 +128,8 @@ void dcc_send (struct session *sess, char *to, char *file, int maxcps, int passi
 struct DCC *find_dcc (char *nick, char *file, int type);
 void dcc_get_nick (struct session *sess, char *nick);
 void dcc_chat (session *sess, char *nick, int passive);
-void handle_dcc (session *sess, char *nick, char *word[], char *word_eol[]);
+void handle_dcc (session *sess, char *nick, char *word[], char *word_eol[],
+					  const message_tags_data *tags_data);
 void dcc_show_list (session *sess);
 guint32 dcc_get_my_address (void);
 void dcc_get_with_destfile (struct DCC *dcc, char *utf8file);
diff --git a/src/common/hexchat.h b/src/common/hexchat.h
index 1a759f14..074d5a22 100644
--- a/src/common/hexchat.h
+++ b/src/common/hexchat.h
@@ -215,6 +215,7 @@ struct hexchatprefs
 	unsigned int hex_irc_wallops;
 	unsigned int hex_irc_who_join;
 	unsigned int hex_irc_whois_front;
+	unsigned int hex_irc_cap_server_time;
 	unsigned int hex_net_auto_reconnect;
 	unsigned int hex_net_auto_reconnectonfail;
 	unsigned int hex_net_proxy_auth;
@@ -593,6 +594,7 @@ typedef struct server
 	unsigned int have_idmsg:1;		/* freenode's IDENTIFY-MSG */
 	unsigned int have_accnotify:1; /* cap account-notify */
 	unsigned int have_extjoin:1;	/* cap extended-join */
+	unsigned int have_server_time:1;	/* cap server-time */
 	unsigned int have_sasl:1;		/* SASL capability */
 	unsigned int have_except:1;	/* ban exemptions +e */
 	unsigned int have_invite:1;	/* invite exemptions +I */
diff --git a/src/common/inbound.c b/src/common/inbound.c
index b1b739e5..2645c438 100644
--- a/src/common/inbound.c
+++ b/src/common/inbound.c
@@ -122,13 +122,15 @@ find_session_from_nick (char *nick, server *serv)
 }
 
 static session *
-inbound_open_dialog (server *serv, char *from)
+inbound_open_dialog (server *serv, char *from,
+							const message_tags_data *tags_data)
 {
 	session *sess;
 
 	sess = new_ircwindow (serv, from, SESS_DIALOG, 0);
 	/* for playing sounds */
-	EMIT_SIGNAL (XP_TE_OPENDIALOG, sess, NULL, NULL, NULL, NULL, 0);
+	EMIT_SIGNAL_TIMESTAMP (XP_TE_OPENDIALOG, sess, NULL, NULL, NULL, NULL, 0,
+								  tags_data->timestamp);
 
 	return sess;
 }
@@ -152,7 +154,8 @@ inbound_make_idtext (server *serv, char *idtext, int max, int id)
 }
 
 void
-inbound_privmsg (server *serv, char *from, char *ip, char *text, int id)
+inbound_privmsg (server *serv, char *from, char *ip, char *text, int id,
+					  const message_tags_data *tags_data)
 {
 	session *sess;
 	struct User *user;
@@ -168,7 +171,7 @@ inbound_privmsg (server *serv, char *from, char *ip, char *text, int id)
 		{
 			if (flood_check (from, ip, serv, current_sess, 1))
 				/* Create a dialog session */
-				sess = inbound_open_dialog (serv, from);
+				sess = inbound_open_dialog (serv, from, tags_data);
 			else
 				sess = serv->server_session;
 			if (!sess)
@@ -186,7 +189,7 @@ inbound_privmsg (server *serv, char *from, char *ip, char *text, int id)
 			}
 			set_topic (sess, ip, ip);
 		}
-		inbound_chanmsg (serv, NULL, NULL, from, text, FALSE, id);
+		inbound_chanmsg (serv, NULL, NULL, from, text, FALSE, id, tags_data);
 		return;
 	}
 
@@ -208,9 +211,11 @@ inbound_privmsg (server *serv, char *from, char *ip, char *text, int id)
 	inbound_make_idtext (serv, idtext, sizeof (idtext), id);
 
 	if (sess->type == SESS_DIALOG && !nodiag)
-		EMIT_SIGNAL (XP_TE_DPRIVMSG, sess, from, text, idtext, NULL, 0);
+		EMIT_SIGNAL_TIMESTAMP (XP_TE_DPRIVMSG, sess, from, text, idtext, NULL, 0,
+									  tags_data->timestamp);
 	else
-		EMIT_SIGNAL (XP_TE_PRIVMSG, sess, from, text, idtext, NULL, 0);
+		EMIT_SIGNAL_TIMESTAMP (XP_TE_PRIVMSG, sess, from, text, idtext, NULL, 0, 
+									  tags_data->timestamp);
 }
 
 /* used for Alerts section. Masks can be separated by commas and spaces. */
@@ -323,7 +328,8 @@ is_hilight (char *from, char *text, session *sess, server *serv)
 }
 
 void
-inbound_action (session *sess, char *chan, char *from, char *ip, char *text, int fromme, int id)
+inbound_action (session *sess, char *chan, char *from, char *ip, char *text,
+					 int fromme, int id, const message_tags_data *tags_data)
 {
 	session *def = sess;
 	server *serv = sess->server;
@@ -348,7 +354,7 @@ inbound_action (session *sess, char *chan, char *from, char *ip, char *text, int
 			{
 				/* but only if it wouldn't flood */
 				if (flood_check (from, ip, serv, current_sess, 1))
-					sess = inbound_open_dialog (serv, from);
+					sess = inbound_open_dialog (serv, from, tags_data);
 				else
 					sess = serv->server_session;
 			}
@@ -394,23 +400,30 @@ inbound_action (session *sess, char *chan, char *from, char *ip, char *text, int
 	{
 		if (is_hilight (from, text, sess, serv))
 		{
-			EMIT_SIGNAL (XP_TE_HCHANACTION, sess, from, text, nickchar, idtext, 0);
+			EMIT_SIGNAL_TIMESTAMP (XP_TE_HCHANACTION, sess, from, text, nickchar,
+										  idtext, 0, tags_data->timestamp);
 			return;
 		}
 	}
 
 	if (fromme)
-		EMIT_SIGNAL (XP_TE_UACTION, sess, from, text, nickchar, idtext, 0);
+		EMIT_SIGNAL_TIMESTAMP (XP_TE_UACTION, sess, from, text, nickchar, idtext,
+									  0, tags_data->timestamp);
 	else if (!privaction)
-		EMIT_SIGNAL (XP_TE_CHANACTION, sess, from, text, nickchar, idtext, 0);
+		EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANACTION, sess, from, text, nickchar,
+									  idtext, 0, tags_data->timestamp);
 	else if (sess->type == SESS_DIALOG)
-		EMIT_SIGNAL (XP_TE_DPRIVACTION, sess, from, text, idtext, NULL, 0);
+		EMIT_SIGNAL_TIMESTAMP (XP_TE_DPRIVACTION, sess, from, text, idtext, NULL,
+									  0, tags_data->timestamp);
 	else
-		EMIT_SIGNAL (XP_TE_PRIVACTION, sess, from, text, idtext, NULL, 0);
+		EMIT_SIGNAL_TIMESTAMP (XP_TE_PRIVACTION, sess, from, text, idtext, NULL, 0,
+									  tags_data->timestamp);
 }
 
 void
-inbound_chanmsg (server *serv, session *sess, char *chan, char *from, char *text, char fromme, int id)
+inbound_chanmsg (server *serv, session *sess, char *chan, char *from, 
+					  char *text, char fromme, int id, 
+					  const message_tags_data *tags_data)
 {
 	struct User *user;
 	int hilight = FALSE;
@@ -452,7 +465,8 @@ inbound_chanmsg (server *serv, session *sess, char *chan, char *from, char *text
 	{
   		if (prefs.hex_away_auto_unmark && serv->is_away)
 			sess->server->p_set_back (sess->server);
-		EMIT_SIGNAL (XP_TE_UCHANMSG, sess, from, text, nickchar, NULL, 0);
+		EMIT_SIGNAL_TIMESTAMP (XP_TE_UCHANMSG, sess, from, text, nickchar, NULL,
+									  0, tags_data->timestamp);
 		return;
 	}
 
@@ -462,15 +476,19 @@ inbound_chanmsg (server *serv, session *sess, char *chan, char *from, char *text
 		hilight = TRUE;
 
 	if (sess->type == SESS_DIALOG)
-		EMIT_SIGNAL (XP_TE_DPRIVMSG, sess, from, text, idtext, NULL, 0);
+		EMIT_SIGNAL_TIMESTAMP (XP_TE_DPRIVMSG, sess, from, text, idtext, NULL, 0,
+									  tags_data->timestamp);
 	else if (hilight)
-		EMIT_SIGNAL (XP_TE_HCHANMSG, sess, from, text, nickchar, idtext, 0);
+		EMIT_SIGNAL_TIMESTAMP (XP_TE_HCHANMSG, sess, from, text, nickchar, idtext,
+									  0, tags_data->timestamp);
 	else
-		EMIT_SIGNAL (XP_TE_CHANMSG, sess, from, text, nickchar, idtext, 0);
+		EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANMSG, sess, from, text, nickchar, idtext,
+									  0, tags_data->timestamp);
 }
 
 void
-inbound_newnick (server *serv, char *nick, char *newnick, int quiet)
+inbound_newnick (server *serv, char *nick, char *newnick, int quiet,
+					  const message_tags_data *tags_data)
 {
 	int me = FALSE;
 	session *sess;
@@ -492,11 +510,12 @@ inbound_newnick (server *serv, char *nick, char *newnick, int quiet)
 				if (!quiet)
 				{
 					if (me)
-						EMIT_SIGNAL (XP_TE_UCHANGENICK, sess, nick, newnick, NULL,
-										 NULL, 0);
+						EMIT_SIGNAL_TIMESTAMP (XP_TE_UCHANGENICK, sess, nick, 
+													  newnick, NULL, NULL, 0,
+													  tags_data->timestamp);
 					else
-						EMIT_SIGNAL (XP_TE_CHANGENICK, sess, nick, newnick, NULL,
-										 NULL, 0);
+						EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANGENICK, sess, nick,
+													  newnick, NULL, NULL, 0, tags_data->timestamp);
 				}
 			}
 			if (sess->type == SESS_DIALOG && !serv->p_cmp (sess->channel, nick))
@@ -554,7 +573,8 @@ find_session_from_waitchannel (char *chan, struct server *serv)
 }
 
 void
-inbound_ujoin (server *serv, char *chan, char *nick, char *ip)
+inbound_ujoin (server *serv, char *chan, char *nick, char *ip,
+					const message_tags_data *tags_data)
 {
 	session *sess;
 
@@ -592,7 +612,8 @@ inbound_ujoin (server *serv, char *chan, char *nick, char *ip)
 	/* sends a MODE */
 	serv->p_join_info (sess->server, chan);
 
-	EMIT_SIGNAL (XP_TE_UJOIN, sess, nick, chan, ip, NULL, 0);
+	EMIT_SIGNAL_TIMESTAMP (XP_TE_UJOIN, sess, nick, chan, ip, NULL, 0,
+								  tags_data->timestamp);
 
 	if (prefs.hex_irc_who_join)
 	{
@@ -603,12 +624,14 @@ inbound_ujoin (server *serv, char *chan, char *nick, char *ip)
 }
 
 void
-inbound_ukick (server *serv, char *chan, char *kicker, char *reason)
+inbound_ukick (server *serv, char *chan, char *kicker, char *reason,
+					const message_tags_data *tags_data)
 {
 	session *sess = find_channel (serv, chan);
 	if (sess)
 	{
-		EMIT_SIGNAL (XP_TE_UKICK, sess, serv->nick, chan, kicker, reason, 0);
+		EMIT_SIGNAL_TIMESTAMP (XP_TE_UKICK, sess, serv->nick, chan, kicker, 
+									  reason, 0, tags_data->timestamp);
 		clear_channel (sess);
 		if (prefs.hex_irc_auto_rejoin)
 		{
@@ -619,22 +642,25 @@ inbound_ukick (server *serv, char *chan, char *kicker, char *reason)
 }
 
 void
-inbound_upart (server *serv, char *chan, char *ip, char *reason)
+inbound_upart (server *serv, char *chan, char *ip, char *reason,
+					const message_tags_data *tags_data)
 {
 	session *sess = find_channel (serv, chan);
 	if (sess)
 	{
 		if (*reason)
-			EMIT_SIGNAL (XP_TE_UPARTREASON, sess, serv->nick, ip, chan, reason,
-							 0);
+			EMIT_SIGNAL_TIMESTAMP (XP_TE_UPARTREASON, sess, serv->nick, ip, chan,
+										  reason, 0, tags_data->timestamp);
 		else
-			EMIT_SIGNAL (XP_TE_UPART, sess, serv->nick, ip, chan, NULL, 0);
+			EMIT_SIGNAL_TIMESTAMP (XP_TE_UPART, sess, serv->nick, ip, chan, NULL,
+										  0, tags_data->timestamp);
 		clear_channel (sess);
 	}
 }
 
 void
-inbound_nameslist (server *serv, char *chan, char *names)
+inbound_nameslist (server *serv, char *chan, char *names,
+						 const message_tags_data *tags_data)
 {
 	session *sess;
 	char name[NICKLEN];
@@ -643,12 +669,13 @@ inbound_nameslist (server *serv, char *chan, char *names)
 	sess = find_channel (serv, chan);
 	if (!sess)
 	{
-		EMIT_SIGNAL (XP_TE_USERSONCHAN, serv->server_session, chan, names, NULL,
-						 NULL, 0);
+		EMIT_SIGNAL_TIMESTAMP (XP_TE_USERSONCHAN, serv->server_session, chan,
+									  names, NULL, NULL, 0, tags_data->timestamp);
 		return;
 	}
 	if (!sess->ignore_names)
-		EMIT_SIGNAL (XP_TE_USERSONCHAN, sess, chan, names, NULL, NULL, 0);
+		EMIT_SIGNAL_TIMESTAMP (XP_TE_USERSONCHAN, sess, chan, names, NULL, NULL,
+									  0, tags_data->timestamp);
 
 	if (sess->end_of_names)
 	{
@@ -663,12 +690,12 @@ inbound_nameslist (server *serv, char *chan, char *names)
 		case 0:
 			name[pos] = 0;
 			if (pos != 0)
-				userlist_add (sess, name, 0, NULL, NULL);
+				userlist_add (sess, name, 0, NULL, NULL, tags_data);
 			return;
 		case ' ':
 			name[pos] = 0;
 			pos = 0;
-			userlist_add (sess, name, 0, NULL, NULL);
+			userlist_add (sess, name, 0, NULL, NULL, tags_data);
 			break;
 		default:
 			name[pos] = *names;
@@ -680,7 +707,8 @@ inbound_nameslist (server *serv, char *chan, char *names)
 }
 
 void
-inbound_topic (server *serv, char *chan, char *topic_text)
+inbound_topic (server *serv, char *chan, char *topic_text,
+					const message_tags_data *tags_data)
 {
 	session *sess = find_channel (serv, chan);
 	char *stripped_topic;
@@ -693,11 +721,13 @@ inbound_topic (server *serv, char *chan, char *topic_text)
 	} else
 		sess = serv->server_session;
 
-	EMIT_SIGNAL (XP_TE_TOPIC, sess, chan, topic_text, NULL, NULL, 0);
+	EMIT_SIGNAL_TIMESTAMP (XP_TE_TOPIC, sess, chan, topic_text, NULL, NULL, 0,
+								  tags_data->timestamp);
 }
 
 void
-inbound_topicnew (server *serv, char *nick, char *chan, char *topic)
+inbound_topicnew (server *serv, char *nick, char *chan, char *topic,
+						const message_tags_data *tags_data)
 {
 	session *sess;
 	char *stripped_topic;
@@ -705,7 +735,8 @@ inbound_topicnew (server *serv, char *nick, char *chan, char *topic)
 	sess = find_channel (serv, chan);
 	if (sess)
 	{
-		EMIT_SIGNAL (XP_TE_NEWTOPIC, sess, nick, topic, chan, NULL, 0);
+		EMIT_SIGNAL_TIMESTAMP (XP_TE_NEWTOPIC, sess, nick, topic, chan, NULL, 0,
+									  tags_data->timestamp);
 		stripped_topic = strip_color (topic, -1, STRIP_ALL);
 		set_topic (sess, topic, stripped_topic);
 		g_free (stripped_topic);
@@ -713,43 +744,51 @@ inbound_topicnew (server *serv, char *nick, char *chan, char *topic)
 }
 
 void
-inbound_join (server *serv, char *chan, char *user, char *ip, char *account, char *realname)
+inbound_join (server *serv, char *chan, char *user, char *ip, char *account,
+				  char *realname, const message_tags_data *tags_data)
 {
 	session *sess = find_channel (serv, chan);
 	if (sess)
 	{
-		EMIT_SIGNAL (XP_TE_JOIN, sess, user, chan, ip, NULL, 0);
-		userlist_add (sess, user, ip, account, realname);
+		EMIT_SIGNAL_TIMESTAMP (XP_TE_JOIN, sess, user, chan, ip, NULL, 0,
+									  tags_data->timestamp);
+		userlist_add (sess, user, ip, account, realname, tags_data);
 	}
 }
 
 void
-inbound_kick (server *serv, char *chan, char *user, char *kicker, char *reason)
+inbound_kick (server *serv, char *chan, char *user, char *kicker, char *reason,
+				  const message_tags_data *tags_data)
 {
 	session *sess = find_channel (serv, chan);
 	if (sess)
 	{
-		EMIT_SIGNAL (XP_TE_KICK, sess, kicker, user, chan, reason, 0);
+		EMIT_SIGNAL_TIMESTAMP (XP_TE_KICK, sess, kicker, user, chan, reason, 0,
+									  tags_data->timestamp);
 		userlist_remove (sess, user);
 	}
 }
 
 void
-inbound_part (server *serv, char *chan, char *user, char *ip, char *reason)
+inbound_part (server *serv, char *chan, char *user, char *ip, char *reason,
+				  const message_tags_data *tags_data)
 {
 	session *sess = find_channel (serv, chan);
 	if (sess)
 	{
 		if (*reason)
-			EMIT_SIGNAL (XP_TE_PARTREASON, sess, user, ip, chan, reason, 0);
+			EMIT_SIGNAL_TIMESTAMP (XP_TE_PARTREASON, sess, user, ip, chan, reason,
+										  0, tags_data->timestamp);
 		else
-			EMIT_SIGNAL (XP_TE_PART, sess, user, ip, chan, NULL, 0);
+			EMIT_SIGNAL_TIMESTAMP (XP_TE_PART, sess, user, ip, chan, NULL, 0,
+										  tags_data->timestamp);
 		userlist_remove (sess, user);
 	}
 }
 
 void
-inbound_topictime (server *serv, char *chan, char *nick, time_t stamp)
+inbound_topictime (server *serv, char *chan, char *nick, time_t stamp,
+						 const message_tags_data *tags_data)
 {
 	char *tim = ctime (&stamp);
 	session *sess = find_channel (serv, chan);
@@ -758,11 +797,13 @@ inbound_topictime (server *serv, char *chan, char *nick, time_t stamp)
 		sess = serv->server_session;
 
 	tim[24] = 0;	/* get rid of the \n */
-	EMIT_SIGNAL (XP_TE_TOPICDATE, sess, chan, nick, tim, NULL, 0);
+	EMIT_SIGNAL_TIMESTAMP (XP_TE_TOPICDATE, sess, chan, nick, tim, NULL, 0,
+								  tags_data->timestamp);
 }
 
 void
-inbound_quit (server *serv, char *nick, char *ip, char *reason)
+inbound_quit (server *serv, char *nick, char *ip, char *reason,
+				  const message_tags_data *tags_data)
 {
 	GSList *list = sess_list;
 	session *sess;
@@ -778,21 +819,24 @@ inbound_quit (server *serv, char *nick, char *ip, char *reason)
  				was_on_front_session = TRUE;
 			if ((user = userlist_find (sess, nick)))
 			{
-				EMIT_SIGNAL (XP_TE_QUIT, sess, nick, reason, ip, NULL, 0);
+				EMIT_SIGNAL_TIMESTAMP (XP_TE_QUIT, sess, nick, reason, ip, NULL, 0,
+											  tags_data->timestamp);
 				userlist_remove_user (sess, user);
 			} else if (sess->type == SESS_DIALOG && !serv->p_cmp (sess->channel, nick))
 			{
-				EMIT_SIGNAL (XP_TE_QUIT, sess, nick, reason, ip, NULL, 0);
+				EMIT_SIGNAL_TIMESTAMP (XP_TE_QUIT, sess, nick, reason, ip, NULL, 0,
+											  tags_data->timestamp);
 			}
 		}
 		list = list->next;
 	}
 
-	notify_set_offline (serv, nick, was_on_front_session);
+	notify_set_offline (serv, nick, was_on_front_session, tags_data);
 }
 
 void
-inbound_account (server *serv, char *nick, char *account)
+inbound_account (server *serv, char *nick, char *account,
+					  const message_tags_data *tags_data)
 {
 	session *sess = NULL;
 	GSList *list;
@@ -808,7 +852,8 @@ inbound_account (server *serv, char *nick, char *account)
 }
 
 void
-inbound_ping_reply (session *sess, char *timestring, char *from)
+inbound_ping_reply (session *sess, char *timestring, char *from,
+						  const message_tags_data *tags_data)
 {
 	unsigned long tim, nowtim, dif;
 	int lag = 0;
@@ -839,11 +884,13 @@ inbound_ping_reply (session *sess, char *timestring, char *from)
 		if (sess->server->lag_sent)
 			sess->server->lag_sent = 0;
 		else
-			EMIT_SIGNAL (XP_TE_PINGREP, sess, from, "?", NULL, NULL, 0);
+			EMIT_SIGNAL_TIMESTAMP (XP_TE_PINGREP, sess, from, "?", NULL, NULL, 0,
+										  tags_data->timestamp);
 	} else
 	{
 		snprintf (outbuf, sizeof (outbuf), "%ld.%ld%ld", dif / 1000000, (dif / 100000) % 10, dif % 10);
-		EMIT_SIGNAL (XP_TE_PINGREP, sess, from, outbuf, NULL, NULL, 0);
+		EMIT_SIGNAL_TIMESTAMP (XP_TE_PINGREP, sess, from, outbuf, NULL, NULL, 0,
+									  tags_data->timestamp);
 	}
 }
 
@@ -863,7 +910,8 @@ find_session_from_type (int type, server *serv)
 }
 
 void
-inbound_notice (server *serv, char *to, char *nick, char *msg, char *ip, int id)
+inbound_notice (server *serv, char *to, char *nick, char *msg, char *ip, int id,
+					 const message_tags_data *tags_data)
 {
 	char *po,*ptr=to;
 	session *sess = 0;
@@ -954,7 +1002,7 @@ inbound_notice (server *serv, char *to, char *nick, char *msg, char *ip, int id)
 		msg++;
 		if (!strncmp (msg, "PING", 4))
 		{
-			inbound_ping_reply (sess, msg + 5, nick);
+			inbound_ping_reply (sess, msg + 5, nick, tags_data);
 			return;
 		}
 	}
@@ -963,15 +1011,19 @@ inbound_notice (server *serv, char *to, char *nick, char *msg, char *ip, int id)
 		po[0] = 0;
 
 	if (server_notice)
-		EMIT_SIGNAL (XP_TE_SERVNOTICE, sess, msg, nick, NULL, NULL, 0);
+		EMIT_SIGNAL_TIMESTAMP (XP_TE_SERVNOTICE, sess, msg, nick, NULL, NULL, 0,
+									  tags_data->timestamp);
 	else if (ptr)
-		EMIT_SIGNAL (XP_TE_CHANNOTICE, sess, nick, to, msg, NULL, 0);
+		EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANNOTICE, sess, nick, to, msg, NULL, 0,
+									  tags_data->timestamp);
 	else
-		EMIT_SIGNAL (XP_TE_NOTICE, sess, nick, msg, NULL, NULL, 0);
+		EMIT_SIGNAL_TIMESTAMP (XP_TE_NOTICE, sess, nick, msg, NULL, NULL, 0,
+									  tags_data->timestamp);
 }
 
 void
-inbound_away (server *serv, char *nick, char *msg)
+inbound_away (server *serv, char *nick, char *msg,
+				  const message_tags_data *tags_data)
 {
 	struct away_msg *away = server_away_find_message (serv, nick);
 	session *sess = NULL;
@@ -998,7 +1050,8 @@ inbound_away (server *serv, char *nick, char *msg)
 
 	/* possibly hide the output */
 	if (!serv->inside_whois || !serv->skip_next_whois)
-		EMIT_SIGNAL (XP_TE_WHOIS5, sess, nick, msg, NULL, NULL, 0);
+		EMIT_SIGNAL_TIMESTAMP (XP_TE_WHOIS5, sess, nick, msg, NULL, NULL, 0,
+									  tags_data->timestamp);
 
 	list = sess_list;
 	while (list)
@@ -1011,7 +1064,8 @@ inbound_away (server *serv, char *nick, char *msg)
 }
 
 void
-inbound_away_notify (server *serv, char *nick, char *reason)
+inbound_away_notify (server *serv, char *nick, char *reason,
+							const message_tags_data *tags_data)
 {
 	session *sess = NULL;
 	GSList *list;
@@ -1026,9 +1080,11 @@ inbound_away_notify (server *serv, char *nick, char *reason)
 			if (sess == serv->front_session && notify_is_in_list (serv, nick))
 			{
 				if (reason)
-					EMIT_SIGNAL (XP_TE_NOTIFYAWAY, sess, nick, reason, NULL, NULL, 0);
+					EMIT_SIGNAL_TIMESTAMP (XP_TE_NOTIFYAWAY, sess, nick, reason, NULL,
+												  NULL, 0, tags_data->timestamp);
 				else
-					EMIT_SIGNAL (XP_TE_NOTIFYBACK, sess, nick, NULL, NULL, NULL, 0);
+					EMIT_SIGNAL_TIMESTAMP (XP_TE_NOTIFYBACK, sess, nick, NULL, NULL, 
+												  NULL, 0, tags_data->timestamp);
 			}
 		}
 		list = list->next;
@@ -1036,7 +1092,8 @@ inbound_away_notify (server *serv, char *nick, char *reason)
 }
 
 int
-inbound_nameslist_end (server *serv, char *chan)
+inbound_nameslist_end (server *serv, char *chan,
+							  const message_tags_data *tags_data)
 {
 	session *sess;
 	GSList *list;
@@ -1145,7 +1202,8 @@ check_autojoin_channels (server *serv)
 }
 
 void
-inbound_next_nick (session *sess, char *nick, int error)
+inbound_next_nick (session *sess, char *nick, int error,
+						 const message_tags_data *tags_data)
 {
 	char *newnick;
 	server *serv = sess->server;
@@ -1166,11 +1224,13 @@ inbound_next_nick (session *sess, char *nick, int error)
 		serv->p_change_nick (serv, newnick);
 		if (error)
 		{
-			EMIT_SIGNAL (XP_TE_NICKERROR, sess, nick, newnick, NULL, NULL, 0);
+			EMIT_SIGNAL_TIMESTAMP (XP_TE_NICKERROR, sess, nick, newnick, NULL, NULL,
+										  0, tags_data->timestamp);
 		}
 		else
 		{
-			EMIT_SIGNAL (XP_TE_NICKCLASH, sess, nick, newnick, NULL, NULL, 0);
+			EMIT_SIGNAL_TIMESTAMP (XP_TE_NICKCLASH, sess, nick, newnick, NULL, NULL,
+										  0, tags_data->timestamp);
 		}
 		break;
 
@@ -1178,21 +1238,25 @@ inbound_next_nick (session *sess, char *nick, int error)
 		serv->p_change_nick (serv, prefs.hex_irc_nick3);
 		if (error)
 		{
-			EMIT_SIGNAL (XP_TE_NICKERROR, sess, nick, prefs.hex_irc_nick3, NULL, NULL, 0);
+			EMIT_SIGNAL_TIMESTAMP (XP_TE_NICKERROR, sess, nick, prefs.hex_irc_nick3,
+										  NULL, NULL, 0, tags_data->timestamp);
 		}
 		else
 		{
-			EMIT_SIGNAL (XP_TE_NICKCLASH, sess, nick, prefs.hex_irc_nick3, NULL, NULL, 0);
+			EMIT_SIGNAL_TIMESTAMP (XP_TE_NICKCLASH, sess, nick, prefs.hex_irc_nick3,
+										  NULL, NULL, 0, tags_data->timestamp);
 		}
 		break;
 
 	default:
-		EMIT_SIGNAL (XP_TE_NICKFAIL, sess, NULL, NULL, NULL, NULL, 0);
+		EMIT_SIGNAL_TIMESTAMP (XP_TE_NICKFAIL, sess, NULL, NULL, NULL, NULL, 0,
+									  tags_data->timestamp);
 	}
 }
 
 void
-do_dns (session *sess, char *nick, char *host)
+do_dns (session *sess, char *nick, char *host,
+		  const message_tags_data *tags_data)
 {
 	char *po;
 	char tbuf[1024];
@@ -1200,7 +1264,8 @@ do_dns (session *sess, char *nick, char *host)
 	po = strrchr (host, '@');
 	if (po)
 		host = po + 1;
-	EMIT_SIGNAL (XP_TE_RESOLVINGUSER, sess, nick, host, NULL, NULL, 0);
+	EMIT_SIGNAL_TIMESTAMP (XP_TE_RESOLVINGUSER, sess, nick, host, NULL, NULL, 0,
+								  tags_data->timestamp);
 	snprintf (tbuf, sizeof (tbuf), "exec -d %s %s", prefs.hex_dnsprogram, host);
 	handle_command (sess, tbuf, FALSE);
 }
@@ -1227,9 +1292,10 @@ set_default_modes (server *serv)
 }
 
 void
-inbound_login_start (session *sess, char *nick, char *servname)
+inbound_login_start (session *sess, char *nick, char *servname,
+							const message_tags_data *tags_data)
 {
-	inbound_newnick (sess->server, sess->server->nick, nick, TRUE);
+	inbound_newnick (sess->server, sess->server->nick, nick, TRUE, tags_data);
 	server_set_name (sess->server, servname);
 	if (sess->type == SESS_SERVER)
 		log_open_or_close (sess);
@@ -1258,7 +1324,7 @@ inbound_set_all_away_status (server *serv, char *nick, unsigned int status)
 }
 
 void
-inbound_uaway (server *serv)
+inbound_uaway (server *serv, const message_tags_data *tags_data)
 {
 	serv->is_away = TRUE;
 	serv->away_time = time (NULL);
@@ -1268,7 +1334,7 @@ inbound_uaway (server *serv)
 }
 
 void
-inbound_uback (server *serv)
+inbound_uback (server *serv, const message_tags_data *tags_data)
 {
 	serv->is_away = FALSE;
 	serv->reconnect_away = FALSE;
@@ -1278,7 +1344,7 @@ inbound_uback (server *serv)
 }
 
 void
-inbound_foundip (session *sess, char *ip)
+inbound_foundip (session *sess, char *ip, const message_tags_data *tags_data)
 {
 	struct hostent *HostAddr;
 
@@ -1286,14 +1352,15 @@ inbound_foundip (session *sess, char *ip)
 	if (HostAddr)
 	{
 		prefs.dcc_ip = ((struct in_addr *) HostAddr->h_addr)->s_addr;
-		EMIT_SIGNAL (XP_TE_FOUNDIP, sess->server->server_session,
-						 inet_ntoa (*((struct in_addr *) HostAddr->h_addr)),
-						 NULL, NULL, NULL, 0);
+		EMIT_SIGNAL_TIMESTAMP (XP_TE_FOUNDIP, sess->server->server_session,
+									  inet_ntoa (*((struct in_addr *) HostAddr->h_addr)),
+									  NULL, NULL, NULL, 0, tags_data->timestamp);
 	}
 }
 
 void
-inbound_user_info_start (session *sess, char *nick)
+inbound_user_info_start (session *sess, char *nick,
+								 const message_tags_data *tags_data)
 {
 	/* set away to FALSE now, 301 may turn it back on */
 	inbound_set_all_away_status (sess->server, nick, 0);
@@ -1305,7 +1372,8 @@ inbound_user_info_start (session *sess, char *nick)
 void
 inbound_user_info (session *sess, char *chan, char *user, char *host,
 						 char *servname, char *nick, char *realname,
-						 char *account, unsigned int away)
+						 char *account, unsigned int away,
+						 const message_tags_data *tags_data)
 {
 	server *serv = sess->server;
 	session *who_sess;
@@ -1326,7 +1394,7 @@ inbound_user_info (session *sess, char *chan, char *user, char *host,
 		else
 		{
 			if (serv->doing_dns && nick && host)
-				do_dns (sess, nick, host);
+				do_dns (sess, nick, host, tags_data);
 		}
 	}
 	else
@@ -1346,7 +1414,8 @@ inbound_user_info (session *sess, char *chan, char *user, char *host,
 }
 
 int
-inbound_banlist (session *sess, time_t stamp, char *chan, char *mask, char *banner, int rplcode)
+inbound_banlist (session *sess, time_t stamp, char *chan, char *mask, 
+					  char *banner, int rplcode, const message_tags_data *tags_data)
 {
 	char *time_str = ctime (&stamp);
 	server *serv = sess->server;
@@ -1368,7 +1437,8 @@ inbound_banlist (session *sess, time_t stamp, char *chan, char *mask, char *bann
 	{
 nowindow:
 
-		EMIT_SIGNAL (XP_TE_BANLIST, sess, chan, mask, banner, time_str, 0);
+		EMIT_SIGNAL_TIMESTAMP (XP_TE_BANLIST, sess, chan, mask, banner, time_str,
+									  0, tags_data->timestamp);
 		return TRUE;
 	}
 
@@ -1405,7 +1475,7 @@ inbound_nickserv_login (server *serv)
 }
 
 void
-inbound_login_end (session *sess, char *text)
+inbound_login_end (session *sess, char *text, const message_tags_data *tags_data)
 {
 	GSList *cmdlist;
 	commandentry *cmd;
@@ -1462,11 +1532,13 @@ inbound_login_end (session *sess, char *text)
 	if (prefs.hex_irc_skip_motd && !serv->motd_skipped)
 	{
 		serv->motd_skipped = TRUE;
-		EMIT_SIGNAL (XP_TE_MOTDSKIP, serv->server_session, NULL, NULL, NULL, NULL, 0);
+		EMIT_SIGNAL_TIMESTAMP (XP_TE_MOTDSKIP, serv->server_session, NULL, NULL,
+									  NULL, NULL, 0, tags_data->timestamp);
 		return;
 	}
 
-	EMIT_SIGNAL (XP_TE_MOTD, serv->server_session, text, NULL, NULL, NULL, 0);
+	EMIT_SIGNAL_TIMESTAMP (XP_TE_MOTD, serv->server_session, text, NULL, NULL,
+								  NULL, 0, tags_data->timestamp);
 }
 
 void
@@ -1480,3 +1552,165 @@ inbound_identified (server *serv)	/* 'MODE +e MYSELF' on freenode */
 		check_autojoin_channels (serv);
 	}
 }
+
+void
+inbound_cap_ack (server *serv, char *nick, char *extensions,
+					  const message_tags_data *tags_data)
+{
+	char *pass; /* buffer for SASL password */
+
+	EMIT_SIGNAL_TIMESTAMP (XP_TE_CAPACK, serv->server_session, nick, extensions,
+								  NULL, NULL, 0, tags_data->timestamp);
+
+	if (strstr (extensions, "identify-msg") != NULL)
+	{
+		serv->have_idmsg = TRUE;
+	}
+
+	if (strstr (extensions, "multi-prefix") != NULL)
+	{
+		serv->have_namesx = TRUE;
+	}
+
+	if (strstr (extensions, "away-notify") != NULL)
+	{
+		serv->have_awaynotify = TRUE;
+	}
+
+	if (strstr (extensions, "account-notify") != NULL)
+	{
+		serv->have_accnotify = TRUE;
+	}
+					
+	if (strstr (extensions, "extended-join") != NULL)
+	{
+		serv->have_extjoin = TRUE;
+	}
+
+	if (strstr (extensions, "server-time") != NULL)
+	{
+		serv->have_server_time = TRUE;
+	}
+
+	if (strstr (extensions, "sasl") != NULL)
+	{
+		char *user;
+
+		serv->have_sasl = TRUE;
+
+		user = (((ircnet *)serv->network)->user) 
+			? (((ircnet *)serv->network)->user) : prefs.hex_irc_user_name;
+
+		EMIT_SIGNAL_TIMESTAMP (XP_TE_SASLAUTH, serv->server_session, user, NULL,
+									  NULL,	NULL,	0,	tags_data->timestamp);
+		tcp_send_len (serv, "AUTHENTICATE PLAIN\r\n", 20);
+
+		pass = encode_sasl_pass (user, serv->password);
+		tcp_sendf (serv, "AUTHENTICATE %s\r\n", pass);
+		free (pass);
+	}
+}
+
+void
+inbound_cap_ls (server *serv, char *nick, char *extensions_str,
+					 const message_tags_data *tags_data)
+{
+	char buffer[256];	/* buffer for requesting capabilities and emitting the signal */
+	guint32 want_cap; /* format the CAP REQ string based on previous capabilities being requested or not */
+	guint32 want_sasl; /* CAP END shouldn't be sent when SASL is requested, it needs further responses */
+	char **extensions;
+	int i;
+
+	EMIT_SIGNAL_TIMESTAMP (XP_TE_CAPLIST, serv->server_session, nick,
+								  extensions_str, NULL, NULL, 0, tags_data->timestamp);
+	want_cap = 0;
+	want_sasl = 0;
+
+	extensions = g_strsplit (extensions_str, " ", 0);
+
+	strcpy (buffer, "CAP REQ :");
+
+	for (i=0; extensions[i]; i++)
+	{
+		const char *extension = extensions[i];
+
+		if (!strcmp (extension, "identify-msg"))
+		{
+			strcat (buffer, "identify-msg ");
+			want_cap = 1;
+		}
+		if (!strcmp (extension, "multi-prefix"))
+		{
+			strcat (buffer, "multi-prefix ");
+			want_cap = 1;
+		}
+		if (!strcmp (extension, "away-notify"))
+		{
+			strcat (buffer, "away-notify ");
+			want_cap = 1;
+		}
+		if (!strcmp (extension, "account-notify"))
+		{
+			strcat (buffer, "account-notify ");
+			want_cap = 1;
+		}
+		if (!strcmp (extension, "extended-join"))
+		{
+			strcat (buffer, "extended-join ");
+			want_cap = 1;
+		}
+
+		/* bouncers can prefix a name space to the extension so we should use.
+		 * znc uses "znc.in/server-time".
+		 */
+		if (!strcmp (extension, "znc.in/server-time"))
+		{
+			strcat (buffer, "znc.in/server-time ");
+		}
+		if (prefs.hex_irc_cap_server_time
+			 && !strcmp (extension, "server-time"))
+		{
+			strcat (buffer, "server-time ");
+		}
+		
+		/* if the SASL password is set AND auth mode is set to SASL, request SASL auth */
+		if (serv->loginmethod == LOGIN_SASL
+			 && strcmp (extension, "sasl") != 0
+			 && strlen (serv->password) != 0)
+		{
+			strcat (buffer, "sasl ");
+			want_cap = 1;
+			want_sasl = 1;
+		}
+	}
+
+	g_strfreev (extensions);
+
+	if (want_cap)
+	{
+		/* buffer + 9 = emit buffer without "CAP REQ :" */
+		EMIT_SIGNAL_TIMESTAMP (XP_TE_CAPREQ, serv->server_session,
+									  buffer + 9, NULL, NULL, NULL, 0,
+									  tags_data->timestamp);
+		tcp_sendf (serv, "%s\r\n", buffer);
+	}
+	if (!want_sasl)
+	{
+		/* if we use SASL, CAP END is dealt via raw numerics */
+		tcp_send_len (serv, "CAP END\r\n", 9);
+	}
+}
+
+void
+inbound_cap_nak (server *serv, const message_tags_data *tags_data)
+{
+	tcp_send_len (serv, "CAP END\r\n", 9);
+}
+
+void
+inbound_cap_list (server *serv, char *nick, char *extensions,
+						const message_tags_data *tags_data)
+{
+	EMIT_SIGNAL_TIMESTAMP (XP_TE_CAPACK, serv->server_session, nick, extensions,
+								  NULL, NULL, 0, tags_data->timestamp);
+}
diff --git a/src/common/inbound.h b/src/common/inbound.h
index 32368cc1..cbb04890 100644
--- a/src/common/inbound.h
+++ b/src/common/inbound.h
@@ -17,43 +17,86 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
+#include "proto-irc.h"
+
 #ifndef HEXCHAT_INBOUND_H
 #define HEXCHAT_INBOUND_H
 
-void inbound_next_nick (session *sess, char *nick, int error);
-void inbound_uback (server *serv);
-void inbound_uaway (server *serv);
-void inbound_account (server *serv, char *nick, char *account);
-void inbound_part (server *serv, char *chan, char *user, char *ip, char *reason);
-void inbound_upart (server *serv, char *chan, char *ip, char *reason);
-void inbound_ukick (server *serv, char *chan, char *kicker, char *reason);
-void inbound_kick (server *serv, char *chan, char *user, char *kicker, char *reason);
-void inbound_notice (server *serv, char *to, char *nick, char *msg, char *ip, int id);
-void inbound_quit (server *serv, char *nick, char *ip, char *reason);
-void inbound_topicnew (server *serv, char *nick, char *chan, char *topic);
-void inbound_join (server *serv, char *chan, char *user, char *ip, char *account, char *realname);
-void inbound_ujoin (server *serv, char *chan, char *nick, char *ip);
-void inbound_topictime (server *serv, char *chan, char *nick, time_t stamp);
-void inbound_topic (server *serv, char *chan, char *topic_text);
-void inbound_user_info_start (session *sess, char *nick);
-void inbound_user_info (session *sess, char *chan, char *user, char *host, char *servname, char *nick, char *realname, char *account, unsigned int away);
-void inbound_foundip (session *sess, char *ip);
-int inbound_banlist (session *sess, time_t stamp, char *chan, char *mask, char *banner, int is_exemption);
-void inbound_ping_reply (session *sess, char *timestring, char *from);
-void inbound_nameslist (server *serv, char *chan, char *names);
-int inbound_nameslist_end (server *serv, char *chan);
-void inbound_away (server *serv, char *nick, char *msg);
-void inbound_away_notify (server *serv, char *nick, char *reason);
-void inbound_login_start (session *sess, char *nick, char *servname);
-void inbound_login_end (session *sess, char *text);
-void inbound_chanmsg (server *serv, session *sess, char *chan, char *from, char *text, char fromme, int id);
+void inbound_next_nick (session *sess, char *nick, int error,
+								const message_tags_data *tags_data);
+void inbound_uback (server *serv, const message_tags_data *tags_data);
+void inbound_uaway (server *serv, const message_tags_data *tags_data);
+void inbound_account (server *serv, char *nick, char *account,
+							 const message_tags_data *tags_data);
+void inbound_part (server *serv, char *chan, char *user, char *ip, char *reason,
+						 const message_tags_data *tags_data);
+void inbound_upart (server *serv, char *chan, char *ip, char *reason,
+						  const message_tags_data *tags_data);
+void inbound_ukick (server *serv, char *chan, char *kicker, char *reason,
+						  const message_tags_data *tags_data);
+void inbound_kick (server *serv, char *chan, char *user, char *kicker,
+						 char *reason, const message_tags_data *tags_data);
+void inbound_notice (server *serv, char *to, char *nick, char *msg, char *ip,
+							int id, const message_tags_data *tags_data);
+void inbound_quit (server *serv, char *nick, char *ip, char *reason,
+						 const message_tags_data *tags_data);
+void inbound_topicnew (server *serv, char *nick, char *chan, char *topic,
+							  const message_tags_data *tags_data);
+void inbound_join (server *serv, char *chan, char *user, char *ip, 
+						 char *account, char *realname, 
+						 const message_tags_data *tags_data);
+void inbound_ujoin (server *serv, char *chan, char *nick, char *ip,
+						  const message_tags_data *tags_data);
+void inbound_topictime (server *serv, char *chan, char *nick, time_t stamp,
+								const message_tags_data *tags_data);
+void inbound_topic (server *serv, char *chan, char *topic_text,
+						  const message_tags_data *tags_data);
+void inbound_user_info_start (session *sess, char *nick,
+										const message_tags_data *tags_data);
+void inbound_user_info (session *sess, char *chan, char *user, char *host,
+								char *servname, char *nick, char *realname, char *account,
+								unsigned int away, const message_tags_data *tags_data);
+void inbound_foundip (session *sess, char *ip, 
+							 const message_tags_data *tags_data);
+int inbound_banlist (session *sess, time_t stamp, char *chan, char *mask, 
+							char *banner, int is_exemption,
+							const message_tags_data *tags_data);
+void inbound_ping_reply (session *sess, char *timestring, char *from,
+								 const message_tags_data *tags_data);
+void inbound_nameslist (server *serv, char *chan, char *names,
+								const message_tags_data *tags_data);
+int inbound_nameslist_end (server *serv, char *chan,
+									const message_tags_data *tags_data);
+void inbound_away (server *serv, char *nick, char *msg,
+						 const message_tags_data *tags_data);
+void inbound_away_notify (server *serv, char *nick, char *reason,
+								  const message_tags_data *tags_data);
+void inbound_login_start (session *sess, char *nick, char *servname,
+								  const message_tags_data *tags_data);
+void inbound_login_end (session *sess, char *text,
+								const message_tags_data *tags_data);
+void inbound_chanmsg (server *serv, session *sess, char *chan, char *from,
+							 char *text, char fromme, int id, 
+							 const message_tags_data *tags_data);
 void clear_channel (session *sess);
 void set_topic (session *sess, char *topic, char *stripped_topic);
-void inbound_privmsg (server *serv, char *from, char *ip, char *text, int id);
-void inbound_action (session *sess, char *chan, char *from, char *ip, char *text, int fromme, int id);
-void inbound_newnick (server *serv, char *nick, char *newnick, int quiet);
-void do_dns (session *sess, char *nick, char *host);
+void inbound_privmsg (server *serv, char *from, char *ip, char *text, int id, 
+							 const message_tags_data *tags_data);
+void inbound_action (session *sess, char *chan, char *from, char *ip,
+							char *text, int fromme, int id,
+							const message_tags_data *tags_data);
+void inbound_newnick (server *serv, char *nick, char *newnick, int quiet,
+							 const message_tags_data *tags_data);
 void inbound_identified (server *serv);
+void inbound_cap_ack (server *serv, char *nick, char *extensions,
+							 const message_tags_data *tags_data);
+void inbound_cap_ls (server *serv, char *nick, char *extensions,
+							const message_tags_data *tags_data);
+void inbound_cap_nak (server *serv, const message_tags_data *tags_data);
+void inbound_cap_list (server *serv, char *nick, char *extensions,
+							  const message_tags_data *tags_data);
+void do_dns (session *sess, char *nick, char *host,
+				 const message_tags_data *tags_data);
 gboolean alert_match_word (char *word, char *masks);
 gboolean alert_match_text (char *text, char *masks);
 
diff --git a/src/common/modes.c b/src/common/modes.c
index f8b25fcd..420f3e8c 100644
--- a/src/common/modes.c
+++ b/src/common/modes.c
@@ -46,9 +46,12 @@ typedef struct
 static int is_prefix_char (server * serv, char c);
 static void record_chan_mode (session *sess, char sign, char mode, char *arg);
 static char *mode_cat (char *str, char *addition);
-static void handle_single_mode (mode_run *mr, char sign, char mode, char *nick, char *chan, char *arg, int quiet, int is_324);
+static void handle_single_mode (mode_run *mr, char sign, char mode, char *nick,
+										  char *chan, char *arg, int quiet, int is_324,
+										  const message_tags_data *tags_data);
 static int mode_has_arg (server *serv, char sign, char mode);
-static void mode_print_grouped (session *sess, char *nick, mode_run *mr);
+static void mode_print_grouped (session *sess, char *nick, mode_run *mr,
+										  const message_tags_data *tags_data);
 static int mode_chanmode_type (server * serv, char mode);
 
 
@@ -387,7 +390,8 @@ mode_cat (char *str, char *addition)
 
 static void
 handle_single_mode (mode_run *mr, char sign, char mode, char *nick,
-						  char *chan, char *arg, int quiet, int is_324)
+						  char *chan, char *arg, int quiet, int is_324,
+						  const message_tags_data *tags_data)
 {
 	session *sess;
 	server *serv = mr->serv;
@@ -440,14 +444,16 @@ handle_single_mode (mode_run *mr, char sign, char mode, char *nick,
 			fe_update_channel_key (sess);
 			fe_update_mode_buttons (sess, mode, sign);
 			if (!quiet)
-				EMIT_SIGNAL (XP_TE_CHANSETKEY, sess, nick, arg, NULL, NULL, 0);
+				EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANSETKEY, sess, nick, arg, NULL,
+											  NULL, 0, tags_data->timestamp);
 			return;
 		case 'l':
 			sess->limit = atoi (arg);
 			fe_update_channel_limit (sess);
 			fe_update_mode_buttons (sess, mode, sign);
 			if (!quiet)
-				EMIT_SIGNAL (XP_TE_CHANSETLIMIT, sess, nick, arg, NULL, NULL, 0);
+				EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANSETLIMIT, sess, nick, arg, NULL,
+											  NULL, 0, tags_data->timestamp);
 			return;
 		case 'o':
 			if (!quiet)
@@ -455,7 +461,8 @@ handle_single_mode (mode_run *mr, char sign, char mode, char *nick,
 			return;
 		case 'h':
 			if (!quiet)
-				EMIT_SIGNAL (XP_TE_CHANHOP, sess, nick, arg, NULL, NULL, 0);
+				EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANHOP, sess, nick, arg, NULL, NULL,
+											  0, tags_data->timestamp);
 			return;
 		case 'v':
 			if (!quiet)
@@ -463,21 +470,25 @@ handle_single_mode (mode_run *mr, char sign, char mode, char *nick,
 			return;
 		case 'b':
 			if (!quiet)
-				EMIT_SIGNAL (XP_TE_CHANBAN, sess, nick, arg, NULL, NULL, 0);
+				EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANBAN, sess, nick, arg, NULL, NULL,
+											  0, tags_data->timestamp);
 			return;
 		case 'e':
 			if (!quiet)
-				EMIT_SIGNAL (XP_TE_CHANEXEMPT, sess, nick, arg, NULL, NULL, 0);
+				EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANEXEMPT, sess, nick, arg, NULL,
+											  NULL, 0, tags_data->timestamp);
 			return;
 		case 'I':
 			if (!quiet)
-				EMIT_SIGNAL (XP_TE_CHANINVITE, sess, nick, arg, NULL, NULL, 0);
+				EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANINVITE, sess, nick, arg, NULL, NULL,
+											  0, tags_data->timestamp);
 			return;
 		case 'q':
 			if (!supportsq)
 				break; /* +q is owner on this server */
 			if (!quiet)
-				EMIT_SIGNAL (XP_TE_CHANQUIET, sess, nick, arg, NULL, NULL, 0);
+				EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANQUIET, sess, nick, arg, NULL, NULL, 0,
+								 tags_data->timestamp);
 			return;
 		}
 		break;
@@ -489,14 +500,16 @@ handle_single_mode (mode_run *mr, char sign, char mode, char *nick,
 			fe_update_channel_key (sess);
 			fe_update_mode_buttons (sess, mode, sign);
 			if (!quiet)
-				EMIT_SIGNAL (XP_TE_CHANRMKEY, sess, nick, NULL, NULL, NULL, 0);
+				EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANRMKEY, sess, nick, NULL, NULL,
+											  NULL, 0, tags_data->timestamp);
 			return;
 		case 'l':
 			sess->limit = 0;
 			fe_update_channel_limit (sess);
 			fe_update_mode_buttons (sess, mode, sign);
 			if (!quiet)
-				EMIT_SIGNAL (XP_TE_CHANRMLIMIT, sess, nick, NULL, NULL, NULL, 0);
+				EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANRMLIMIT, sess, nick, NULL, NULL,
+											  NULL, 0, tags_data->timestamp);
 			return;
 		case 'o':
 			if (!quiet)
@@ -504,7 +517,8 @@ handle_single_mode (mode_run *mr, char sign, char mode, char *nick,
 			return;
 		case 'h':
 			if (!quiet)
-				EMIT_SIGNAL (XP_TE_CHANDEHOP, sess, nick, arg, NULL, NULL, 0);
+				EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANDEHOP, sess, nick, arg, NULL,
+											  NULL, 0, tags_data->timestamp);
 			return;
 		case 'v':
 			if (!quiet)
@@ -512,21 +526,25 @@ handle_single_mode (mode_run *mr, char sign, char mode, char *nick,
 			return;
 		case 'b':
 			if (!quiet)
-				EMIT_SIGNAL (XP_TE_CHANUNBAN, sess, nick, arg, NULL, NULL, 0);
+				EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANUNBAN, sess, nick, arg, NULL, NULL,
+											  0, tags_data->timestamp);
 			return;
 		case 'e':
 			if (!quiet)
-				EMIT_SIGNAL (XP_TE_CHANRMEXEMPT, sess, nick, arg, NULL, NULL, 0);
+				EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANRMEXEMPT, sess, nick, arg, NULL,
+											  NULL, 0, tags_data->timestamp);
 			return;
 		case 'I':
 			if (!quiet)
-				EMIT_SIGNAL (XP_TE_CHANRMINVITE, sess, nick, arg, NULL, NULL, 0);
+				EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANRMINVITE, sess, nick, arg, NULL,
+											  NULL, 0, tags_data->timestamp);
 			return;
 		case 'q':
 			if (!supportsq)
 				break; /* -q is owner on this server */
 			if (!quiet)
-				EMIT_SIGNAL (XP_TE_CHANUNQUIET, sess, nick, arg, NULL, NULL, 0);
+				EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANUNQUIET, sess, nick, arg, NULL,
+											  NULL, 0, tags_data->timestamp);
 			return;
 		}
 	}
@@ -544,10 +562,12 @@ handle_single_mode (mode_run *mr, char sign, char mode, char *nick,
 		{
 			char *buf = malloc (strlen (chan) + strlen (arg) + 2);
 			sprintf (buf, "%s %s", chan, arg);
-			EMIT_SIGNAL (XP_TE_CHANMODEGEN, sess, nick, outbuf, outbuf + 2, buf, 0);
+			EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANMODEGEN, sess, nick, outbuf,
+										  outbuf + 2, buf, 0, tags_data->timestamp);
 			free (buf);
 		} else
-			EMIT_SIGNAL (XP_TE_CHANMODEGEN, sess, nick, outbuf, outbuf + 2, chan, 0);
+			EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANMODEGEN, sess, nick, outbuf,
+										  outbuf + 2, chan, 0, tags_data->timestamp);
 	}
 }
 
@@ -607,33 +627,38 @@ mode_chanmode_type (server * serv, char mode)
 }
 
 static void
-mode_print_grouped (session *sess, char *nick, mode_run *mr)
+mode_print_grouped (session *sess, char *nick, mode_run *mr,
+						  const message_tags_data *tags_data)
 {
 	/* print all the grouped Op/Deops */
 	if (mr->op)
 	{
-		EMIT_SIGNAL (XP_TE_CHANOP, sess, nick, mr->op, NULL, NULL, 0);
+		EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANOP, sess, nick, mr->op, NULL, NULL, 0,
+									  tags_data->timestamp);
 		free (mr->op);
 		mr->op = NULL;
 	}
 
 	if (mr->deop)
 	{
-		EMIT_SIGNAL (XP_TE_CHANDEOP, sess, nick, mr->deop, NULL, NULL, 0);
+		EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANDEOP, sess, nick, mr->deop, NULL, NULL,
+									  0, tags_data->timestamp);
 		free (mr->deop);
 		mr->deop = NULL;
 	}
 
 	if (mr->voice)
 	{
-		EMIT_SIGNAL (XP_TE_CHANVOICE, sess, nick, mr->voice, NULL, NULL, 0);
+		EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANVOICE, sess, nick, mr->voice, NULL, NULL,
+									  0, tags_data->timestamp);
 		free (mr->voice);
 		mr->voice = NULL;
 	}
 
 	if (mr->devoice)
 	{
-		EMIT_SIGNAL (XP_TE_CHANDEVOICE, sess, nick, mr->devoice, NULL, NULL, 0);
+		EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANDEVOICE, sess, nick, mr->devoice, NULL,
+									  NULL, 0, tags_data->timestamp);
 		free (mr->devoice);
 		mr->devoice = NULL;
 	}
@@ -644,7 +669,7 @@ mode_print_grouped (session *sess, char *nick, mode_run *mr)
 
 void
 handle_mode (server * serv, char *word[], char *word_eol[],
-				 char *nick, int numeric_324)
+				 char *nick, int numeric_324, const message_tags_data *tags_data)
 {
 	session *sess;
 	char *chan;
@@ -687,7 +712,8 @@ handle_mode (server * serv, char *word[], char *word_eol[],
 		word_eol[offset][len] = 0;
 
 	if (prefs.hex_irc_raw_modes && !numeric_324)
-		EMIT_SIGNAL (XP_TE_RAWMODES, sess, nick, word_eol[offset], 0, 0, 0);
+		EMIT_SIGNAL_TIMESTAMP (XP_TE_RAWMODES, sess, nick, word_eol[offset], 0, 0, 0,
+									  tags_data->timestamp);
 
 	if (numeric_324 && !using_front_tab)
 	{
@@ -731,7 +757,7 @@ handle_mode (server * serv, char *word[], char *word_eol[],
 		case '-':
 		case '+':
 			/* print all the grouped Op/Deops */
-			mode_print_grouped (sess, nick, &mr);
+			mode_print_grouped (sess, nick, &mr, tags_data);
 			sign = *modes;
 			break;
 		default:
@@ -743,7 +769,7 @@ handle_mode (server * serv, char *word[], char *word_eol[],
 			}
 			handle_single_mode (&mr, sign, *modes, nick, chan,
 									  argstr, numeric_324 || prefs.hex_irc_raw_modes,
-									  numeric_324);
+									  numeric_324, tags_data);
 		}
 
 		modes++;
@@ -754,13 +780,13 @@ handle_mode (server * serv, char *word[], char *word_eol[],
 		fe_set_title (sess);
 
 	/* print all the grouped Op/Deops */
-	mode_print_grouped (sess, nick, &mr);
+	mode_print_grouped (sess, nick, &mr, tags_data);
 }
 
 /* handle the 005 numeric */
 
 void
-inbound_005 (server * serv, char *word[])
+inbound_005 (server * serv, char *word[], const message_tags_data *tags_data)
 {
 	int w;
 	char *pre;
diff --git a/src/common/modes.h b/src/common/modes.h
index e55ec911..7e13c7df 100644
--- a/src/common/modes.h
+++ b/src/common/modes.h
@@ -17,6 +17,8 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
+#include "proto-irc.h"
+
 #ifndef HEXCHAT_MODES_H
 #define HEXCHAT_MODES_H
 
@@ -24,8 +26,9 @@ int is_channel (server *serv, char *chan);
 char get_nick_prefix (server *serv, unsigned int access);
 unsigned int nick_access (server *serv, char *nick, int *modechars);
 int mode_access (server *serv, char mode, char *prefix);
-void inbound_005 (server *serv, char *word[]);
-void handle_mode (server *serv, char *word[], char *word_eol[], char *nick, int numeric_324);
+void inbound_005 (server *serv, char *word[], const message_tags_data *tags_data);
+void handle_mode (server *serv, char *word[], char *word_eol[], char *nick,
+						int numeric_324, const message_tags_data *tags_data);
 void send_channel_modes (session *sess, char *tbuf, char *word[], int start, int end, char sign, char mode, int modes_per_line);
 
 #endif
diff --git a/src/common/notify.c b/src/common/notify.c
index 301bb393..944d826c 100644
--- a/src/common/notify.c
+++ b/src/common/notify.c
@@ -205,7 +205,8 @@ notify_find (server *serv, char *nick)
 
 static void
 notify_announce_offline (server * serv, struct notify_per_server *servnot,
-								char *nick, int quiet)
+								 char *nick, int quiet, 
+								 const message_tags_data *tags_data)
 {
 	session *sess;
 
@@ -214,15 +215,16 @@ notify_announce_offline (server * serv, struct notify_per_server *servnot,
 	servnot->ison = FALSE;
 	servnot->lastoff = time (0);
 	if (!quiet)
-		EMIT_SIGNAL (XP_TE_NOTIFYOFFLINE, sess, nick, serv->servername,
-						 server_get_network (serv, TRUE), NULL, 0);
+		EMIT_SIGNAL_TIMESTAMP (XP_TE_NOTIFYOFFLINE, sess, nick, serv->servername,
+									  server_get_network (serv, TRUE), NULL, 0,
+									  tags_data->timestamp);
 	fe_notify_update (nick);
 	fe_notify_update (0);
 }
 
 static void
 notify_announce_online (server * serv, struct notify_per_server *servnot,
-								char *nick)
+								char *nick, const message_tags_data *tags_data)
 {
 	session *sess;
 
@@ -234,8 +236,9 @@ notify_announce_online (server * serv, struct notify_per_server *servnot,
 
 	servnot->ison = TRUE;
 	servnot->laston = time (0);
-	EMIT_SIGNAL (XP_TE_NOTIFYONLINE, sess, nick, serv->servername,
-					 server_get_network (serv, TRUE), NULL, 0);
+	EMIT_SIGNAL_TIMESTAMP (XP_TE_NOTIFYONLINE, sess, nick, serv->servername,
+					 server_get_network (serv, TRUE), NULL, 0,
+					 tags_data->timestamp);
 	fe_notify_update (nick);
 	fe_notify_update (0);
 
@@ -254,7 +257,8 @@ notify_announce_online (server * serv, struct notify_per_server *servnot,
 /* handles numeric 601 */
 
 void
-notify_set_offline (server * serv, char *nick, int quiet)
+notify_set_offline (server * serv, char *nick, int quiet,
+						  const message_tags_data *tags_data)
 {
 	struct notify_per_server *servnot;
 
@@ -262,13 +266,14 @@ notify_set_offline (server * serv, char *nick, int quiet)
 	if (!servnot)
 		return;
 
-	notify_announce_offline (serv, servnot, nick, quiet);
+	notify_announce_offline (serv, servnot, nick, quiet, tags_data);
 }
 
 /* handles numeric 604 and 600 */
 
 void
-notify_set_online (server * serv, char *nick)
+notify_set_online (server * serv, char *nick,
+						 const message_tags_data *tags_data)
 {
 	struct notify_per_server *servnot;
 
@@ -276,7 +281,7 @@ notify_set_online (server * serv, char *nick)
 	if (!servnot)
 		return;
 
-	notify_announce_online (serv, servnot, nick);
+	notify_announce_online (serv, servnot, nick, tags_data);
 }
 
 static void
@@ -369,7 +374,7 @@ notify_send_watches (server * serv)
 /* called when receiving a ISON 303 - should this func go? */
 
 void
-notify_markonline (server *serv, char *word[])
+notify_markonline (server *serv, char *word[], const message_tags_data *tags_data)
 {
 	struct notify *notify;
 	struct notify_per_server *servnot;
@@ -392,7 +397,7 @@ notify_markonline (server *serv, char *word[])
 			if (!serv->p_cmp (notify->name, word[i]))
 			{
 				seen = TRUE;
-				notify_announce_online (serv, servnot, notify->name);
+				notify_announce_online (serv, servnot, notify->name, tags_data);
 				break;
 			}
 			i++;
@@ -406,7 +411,7 @@ notify_markonline (server *serv, char *word[])
 		}
 		if (!seen && servnot->ison)
 		{
-			notify_announce_offline (serv, servnot, notify->name, FALSE);
+			notify_announce_offline (serv, servnot, notify->name, FALSE, tags_data);
 		}
 		list = list->next;
 	}
@@ -467,7 +472,7 @@ notify_checklist (void)	/* check ISON list */
 }
 
 void
-notify_showlist (struct session *sess)
+notify_showlist (struct session *sess, const message_tags_data *tags_data)
 {
 	char outbuf[256];
 	struct notify *notify;
@@ -475,7 +480,8 @@ notify_showlist (struct session *sess)
 	struct notify_per_server *servnot;
 	int i = 0;
 
-	EMIT_SIGNAL (XP_TE_NOTIFYHEAD, sess, NULL, NULL, NULL, NULL, 0);
+	EMIT_SIGNAL_TIMESTAMP (XP_TE_NOTIFYHEAD, sess, NULL, NULL, NULL, NULL, 0,
+								  tags_data->timestamp);
 	while (list)
 	{
 		i++;
@@ -485,15 +491,17 @@ notify_showlist (struct session *sess)
 			snprintf (outbuf, sizeof (outbuf), _("  %-20s online\n"), notify->name);
 		else
 			snprintf (outbuf, sizeof (outbuf), _("  %-20s offline\n"), notify->name);
-		PrintText (sess, outbuf);
+		PrintTextTimeStamp (sess, outbuf, tags_data->timestamp);
 		list = list->next;
 	}
 	if (i)
 	{
 		sprintf (outbuf, "%d", i);
-		EMIT_SIGNAL (XP_TE_NOTIFYNUMBER, sess, outbuf, NULL, NULL, NULL, 0);
+		EMIT_SIGNAL_TIMESTAMP (XP_TE_NOTIFYNUMBER, sess, outbuf, NULL, NULL, NULL,
+									  0, tags_data->timestamp);
 	} else
-		EMIT_SIGNAL (XP_TE_NOTIFYEMPTY, sess, NULL, NULL, NULL, NULL, 0);
+		EMIT_SIGNAL_TIMESTAMP (XP_TE_NOTIFYEMPTY, sess, NULL, NULL, NULL, NULL, 0,
+									  tags_data->timestamp);
 }
 
 int
diff --git a/src/common/notify.h b/src/common/notify.h
index 8e513d5f..4a6ffb35 100644
--- a/src/common/notify.h
+++ b/src/common/notify.h
@@ -17,6 +17,8 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
+#include "proto-irc.h"
+
 #ifndef HEXCHAT_NOTIFY_H
 #define HEXCHAT_NOTIFY_H
 
@@ -41,8 +43,10 @@ extern GSList *notify_list;
 extern int notify_tag;
 
 /* the WATCH stuff */
-void notify_set_online (server * serv, char *nick);
-void notify_set_offline (server * serv, char *nick, int quiet);
+void notify_set_online (server * serv, char *nick,
+								const message_tags_data *tags_data);
+void notify_set_offline (server * serv, char *nick, int quiet,
+								 const message_tags_data *tags_data);
 void notify_send_watches (server * serv);
 
 /* the general stuff */
@@ -51,13 +55,14 @@ int notify_deluser (char *name);
 void notify_cleanup (void);
 void notify_load (void);
 void notify_save (void);
-void notify_showlist (session *sess);
+void notify_showlist (session *sess, const message_tags_data *tags_data);
 gboolean notify_is_in_list (server *serv, char *name);
 int notify_isnotify (session *sess, char *name);
 struct notify_per_server *notify_find_server_entry (struct notify *notify, struct server *serv);
 
 /* the old ISON stuff - remove me? */
-void notify_markonline (server *serv, char *word[]);
+void notify_markonline (server *serv, char *word[], 
+								const message_tags_data *tags_data);
 int notify_checklist (void);
 
 #endif
diff --git a/src/common/outbound.c b/src/common/outbound.c
index 120bb241..3709ce96 100644
--- a/src/common/outbound.c
+++ b/src/common/outbound.c
@@ -1419,7 +1419,8 @@ cmd_dns (struct session *sess, char *tbuf, char *word[], char *word_eol[])
 			user = userlist_find (sess, nick);
 			if (user && user->hostname)
 			{
-				do_dns (sess, user->nick, user->hostname);
+				message_tags_data no_tags = MESSAGE_TAGS_DATA_INIT;
+				do_dns (sess, user->nick, user->hostname, &no_tags);
 			} else
 			{
 				sess->server->p_get_ip (sess->server, nick);
@@ -2606,6 +2607,7 @@ cmd_me (struct session *sess, char *tbuf, char *word[], char *word_eol[])
 	char *split_text = NULL;
 	int cmd_length = 22; /* " PRIVMSG ", " ", :, \001ACTION, " ", \001, \r, \n */
 	int offset = 0;
+	message_tags_data no_tags = MESSAGE_TAGS_DATA_INIT;
 
 	if (!(*act))
 		return FALSE;
@@ -2621,7 +2623,8 @@ cmd_me (struct session *sess, char *tbuf, char *word[], char *word_eol[])
 	if (dcc_write_chat (sess->channel, tbuf))
 	{
 		/* print it to screen */
-		inbound_action (sess, sess->channel, sess->server->nick, "", act, TRUE, FALSE);
+		inbound_action (sess, sess->channel, sess->server->nick, "", act, TRUE, FALSE,
+							 &no_tags);
 	} else
 	{
 		/* DCC CHAT failed, try through server */
@@ -2631,7 +2634,9 @@ cmd_me (struct session *sess, char *tbuf, char *word[], char *word_eol[])
 			{
 				sess->server->p_action (sess->server, sess->channel, split_text);
 				/* print it to screen */
-				inbound_action (sess, sess->channel, sess->server->nick, "", split_text, TRUE, FALSE);
+				inbound_action (sess, sess->channel, sess->server->nick, "",
+									 split_text, TRUE, FALSE,
+									 &no_tags);
 
 				if (*split_text)
 					offset += strlen(split_text);
@@ -2641,7 +2646,8 @@ cmd_me (struct session *sess, char *tbuf, char *word[], char *word_eol[])
 
 			sess->server->p_action (sess->server, sess->channel, act + offset);
 			/* print it to screen */
-			inbound_action (sess, sess->channel, sess->server->nick, "", act + offset, TRUE, FALSE);
+			inbound_action (sess, sess->channel, sess->server->nick, "",
+								 act + offset, TRUE, FALSE, &no_tags);
 		} else
 		{
 			notc_msg (sess);
@@ -2701,7 +2707,6 @@ cmd_msg (struct session *sess, char *tbuf, char *word[], char *word_eol[])
 	char *nick = word[2];
 	char *msg = word_eol[3];
 	struct session *newsess;
-
 	char *split_text = NULL;
 	int cmd_length = 13; /* " PRIVMSG ", " ", :, \r, \n */
 	int offset = 0;
@@ -2752,10 +2757,13 @@ cmd_msg (struct session *sess, char *tbuf, char *word[], char *word_eol[])
 				newsess = find_channel (sess->server, nick);
 			if (newsess)
 			{
+				message_tags_data no_tags = MESSAGE_TAGS_DATA_INIT;
+
 				while ((split_text = split_up_text (sess, msg + offset, cmd_length, split_text)))
 				{
 					inbound_chanmsg (newsess->server, NULL, newsess->channel,
-										  newsess->server->nick, split_text, TRUE, FALSE);
+										  newsess->server->nick, split_text, TRUE, FALSE,
+										  &no_tags);
 
 					if (*split_text)
 						offset += strlen(split_text);
@@ -2763,7 +2771,8 @@ cmd_msg (struct session *sess, char *tbuf, char *word[], char *word_eol[])
 					g_free(split_text);
 				}
 				inbound_chanmsg (newsess->server, NULL, newsess->channel,
-									  newsess->server->nick, msg + offset, TRUE, FALSE);
+									  newsess->server->nick, msg + offset, TRUE, FALSE,
+									  &no_tags);
 			}
 			else
 			{
@@ -2825,7 +2834,11 @@ cmd_nick (struct session *sess, char *tbuf, char *word[], char *word_eol[])
 		if (sess->server->connected)
 			sess->server->p_change_nick (sess->server, nick);
 		else
-			inbound_newnick (sess->server, sess->server->nick, nick, TRUE);
+		{
+			message_tags_data no_tags = MESSAGE_TAGS_DATA_INIT;
+			inbound_newnick (sess->server, sess->server->nick, nick, TRUE,
+								  &no_tags);
+		}
 		return TRUE;
 	}
 	return FALSE;
@@ -2894,7 +2907,10 @@ cmd_notify (struct session *sess, char *tbuf, char *word[], char *word_eol[])
 			}
 		}
 	} else
-		notify_showlist (sess);
+	{
+		message_tags_data no_tags = MESSAGE_TAGS_DATA_INIT;
+		notify_showlist (sess, &no_tags);
+	}
 	return TRUE;
 }
 
@@ -3699,8 +3715,11 @@ cmd_wallchan (struct session *sess, char *tbuf, char *word[],
 			sess = list->data;
 			if (sess->type == SESS_CHANNEL)
 			{
+				message_tags_data no_tags = MESSAGE_TAGS_DATA_INIT;
+
 				inbound_chanmsg (sess->server, NULL, sess->channel,
-									  sess->server->nick, word_eol[2], TRUE, FALSE);
+									  sess->server->nick, word_eol[2], TRUE, FALSE, 
+									  &no_tags);
 				sess->server->p_message (sess->server, sess->channel, word_eol[2]);
 			}
 			list = list->next;
@@ -4344,6 +4363,7 @@ handle_say (session *sess, char *text, int check_spch)
 	char *newcmd = newcmd_static;
 	int len;
 	int newcmdlen = sizeof newcmd_static;
+	message_tags_data no_tags = MESSAGE_TAGS_DATA_INIT;
 
 	if (strcmp (sess->channel, "(lastlog)") == 0)
 	{
@@ -4396,7 +4416,7 @@ handle_say (session *sess, char *text, int check_spch)
 		if (dcc)
 		{
 			inbound_chanmsg (sess->server, NULL, sess->channel,
-								  sess->server->nick, text, TRUE, FALSE);
+								  sess->server->nick, text, TRUE, FALSE, &no_tags);
 			set_topic (sess, net_ip (dcc->addr), net_ip (dcc->addr));
 			goto xit;
 		}
@@ -4411,7 +4431,7 @@ handle_say (session *sess, char *text, int check_spch)
 		while ((split_text = split_up_text (sess, text + offset, cmd_length, split_text)))
 		{
 			inbound_chanmsg (sess->server, sess, sess->channel, sess->server->nick,
-								  split_text, TRUE, FALSE);
+								  split_text, TRUE, FALSE, &no_tags);
 			sess->server->p_message (sess->server, sess->channel, split_text);
 			
 			if (*split_text)
@@ -4421,7 +4441,7 @@ handle_say (session *sess, char *text, int check_spch)
 		}
 
 		inbound_chanmsg (sess->server, sess, sess->channel, sess->server->nick,
-							  text + offset, TRUE, FALSE);
+							  text + offset, TRUE, FALSE, &no_tags);
 		sess->server->p_message (sess->server, sess->channel, text + offset);
 	} else
 	{
diff --git a/src/common/proto-irc.c b/src/common/proto-irc.c
index ec4a36a3..204fccda 100644
--- a/src/common/proto-irc.c
+++ b/src/common/proto-irc.c
@@ -29,6 +29,7 @@
 #endif
 
 #include "hexchat.h"
+#include "proto-irc.h"
 #include "ctcp.h"
 #include "fe.h"
 #include "ignore.h"
@@ -443,17 +444,20 @@ irc_raw (server *serv, char *raw)
 
 
 static void
-channel_date (session *sess, char *chan, char *timestr)
+channel_date (session *sess, char *chan, char *timestr,
+				  const message_tags_data *tags_data)
 {
 	time_t timestamp = (time_t) atol (timestr);
 	char *tim = ctime (&timestamp);
 	tim[24] = 0;	/* get rid of the \n */
-	EMIT_SIGNAL (XP_TE_CHANDATE, sess, chan, tim, NULL, NULL, 0);
+	EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANDATE, sess, chan, tim, NULL, NULL, 0,
+								  tags_data->timestamp);
 }
 
 static void
 process_numeric (session * sess, int n,
-					  char *word[], char *word_eol[], char *text)
+					  char *word[], char *word_eol[], char *text,
+					  const message_tags_data *tags_data)
 {
 	server *serv = sess->server;
 	/* show whois is the server tab */
@@ -468,7 +472,7 @@ process_numeric (session * sess, int n,
 	switch (n)
 	{
 	case 1:
-		inbound_login_start (sess, word[3], word[1]);
+		inbound_login_start (sess, word[3], word[1], tags_data);
 		/* if network is PTnet then you must get your IP address
 			from "001" server message */
 		if ((strncmp(word[7], "PTnet", 5) == 0) &&
@@ -478,7 +482,7 @@ process_numeric (session * sess, int n,
 		{
 			serv->use_who = FALSE;
 			if (prefs.hex_dcc_ip_from_server)
-				inbound_foundip (sess, strrchr(word[10], '@')+1);
+				inbound_foundip (sess, strrchr(word[10], '@')+1, tags_data);
 		}
 
 		goto def;
@@ -500,7 +504,7 @@ process_numeric (session * sess, int n,
 		goto def;
 
 	case 5:
-		inbound_005 (serv, word);
+		inbound_005 (serv, word, tags_data);
 		goto def;
 
 	case 263:	/*Server load is temporarily too heavy */
@@ -513,7 +517,8 @@ process_numeric (session * sess, int n,
 
 	case 301:
 		inbound_away (serv, word[4],
-						(word_eol[5][0] == ':') ? word_eol[5] + 1 : word_eol[5]);
+						  (word_eol[5][0] == ':') ? word_eol[5] + 1 : word_eol[5],
+						  tags_data);
 		break;
 
 	case 302:
@@ -527,7 +532,7 @@ process_numeric (session * sess, int n,
 				{
 					char *at = strrchr (eq + 1, '@');
 					if (at)
-						inbound_foundip (sess, at + 1);
+						inbound_foundip (sess, at + 1, tags_data);
 				}
 			}
 
@@ -538,39 +543,42 @@ process_numeric (session * sess, int n,
 
 	case 303:
 		word[4]++;
-		notify_markonline (serv, word);
+		notify_markonline (serv, word, tags_data);
 		break;
 
 	case 305:
-		inbound_uback (serv);
+		inbound_uback (serv, tags_data);
 		goto def;
 
 	case 306:
-		inbound_uaway (serv);
+		inbound_uaway (serv, tags_data);
 		goto def;
 
 	case 312:
 		if (!serv->skip_next_whois)
-			EMIT_SIGNAL (XP_TE_WHOIS3, whois_sess, word[4], word_eol[5], NULL, NULL, 0);
+			EMIT_SIGNAL_TIMESTAMP (XP_TE_WHOIS3, whois_sess, word[4], word_eol[5],
+										  NULL, NULL, 0, tags_data->timestamp);
 		else
-			inbound_user_info (sess, NULL, NULL, NULL, word[5], word[4], NULL, NULL, 0xff);
+			inbound_user_info (sess, NULL, NULL, NULL, word[5], word[4], NULL, NULL,
+									 0xff, tags_data);
 		break;
 
 	case 311:	/* WHOIS 1st line */
 		serv->inside_whois = 1;
-		inbound_user_info_start (sess, word[4]);
+		inbound_user_info_start (sess, word[4], tags_data);
 		if (!serv->skip_next_whois)
-			EMIT_SIGNAL (XP_TE_WHOIS1, whois_sess, word[4], word[5],
-							 word[6], word_eol[8] + 1, 0);
+			EMIT_SIGNAL_TIMESTAMP (XP_TE_WHOIS1, whois_sess, word[4], word[5],
+										  word[6], word_eol[8] + 1, 0, tags_data->timestamp);
 		else
 			inbound_user_info (sess, NULL, word[5], word[6], NULL, word[4],
-									 word_eol[8][0] == ':' ? word_eol[8] + 1 : word_eol[8], NULL, 0xff);
+									 word_eol[8][0] == ':' ? word_eol[8] + 1 : word_eol[8],
+									 NULL, 0xff, tags_data);
 		break;
 
 	case 314:	/* WHOWAS */
-		inbound_user_info_start (sess, word[4]);
-		EMIT_SIGNAL (XP_TE_WHOIS1, whois_sess, word[4], word[5],
-						 word[6], word_eol[8] + 1, 0);
+		inbound_user_info_start (sess, word[4], tags_data);
+		EMIT_SIGNAL_TIMESTAMP (XP_TE_WHOIS1, whois_sess, word[4], word[5],
+									  word[6], word_eol[8] + 1, 0, tags_data->timestamp);
 		break;
 
 	case 317:
@@ -585,22 +593,22 @@ process_numeric (session * sess, int n,
 						"%02ld:%02ld:%02ld", idle / 3600, (idle / 60) % 60,
 						idle % 60);
 			if (timestamp == 0)
-				EMIT_SIGNAL (XP_TE_WHOIS4, whois_sess, word[4],
-								 outbuf, NULL, NULL, 0);
+				EMIT_SIGNAL_TIMESTAMP (XP_TE_WHOIS4, whois_sess, word[4],
+											  outbuf, NULL, NULL, 0, tags_data->timestamp);
 			else
 			{
 				tim = ctime (&timestamp);
 				tim[19] = 0; 	/* get rid of the \n */
-				EMIT_SIGNAL (XP_TE_WHOIS4T, whois_sess, word[4],
-								 outbuf, tim, NULL, 0);
+				EMIT_SIGNAL_TIMESTAMP (XP_TE_WHOIS4T, whois_sess, word[4],
+											  outbuf, tim, NULL, 0, tags_data->timestamp);
 			}
 		}
 		break;
 
 	case 318:	/* END OF WHOIS */
 		if (!serv->skip_next_whois)
-			EMIT_SIGNAL (XP_TE_WHOIS6, whois_sess, word[4], NULL,
-							 NULL, NULL, 0);
+			EMIT_SIGNAL_TIMESTAMP (XP_TE_WHOIS6, whois_sess, word[4], NULL,
+										  NULL, NULL, 0, tags_data->timestamp);
 		serv->skip_next_whois = 0;
 		serv->inside_whois = 0;
 		break;
@@ -608,20 +616,23 @@ process_numeric (session * sess, int n,
 	case 313:
 	case 319:
 		if (!serv->skip_next_whois)
-			EMIT_SIGNAL (XP_TE_WHOIS2, whois_sess, word[4],
-							 word_eol[5] + 1, NULL, NULL, 0);
+			EMIT_SIGNAL_TIMESTAMP (XP_TE_WHOIS2, whois_sess, word[4],
+										  word_eol[5] + 1, NULL, NULL, 0,
+										  tags_data->timestamp);
 		break;
 
 	case 307:	/* dalnet version */
 	case 320:	/* :is an identified user */
 		if (!serv->skip_next_whois)
-			EMIT_SIGNAL (XP_TE_WHOIS_ID, whois_sess, word[4],
-							 word_eol[5] + 1, NULL, NULL, 0);
+			EMIT_SIGNAL_TIMESTAMP (XP_TE_WHOIS_ID, whois_sess, word[4],
+										  word_eol[5] + 1, NULL, NULL, 0,
+										  tags_data->timestamp);
 		break;
 
 	case 321:
 		if (!fe_is_chanwindow (sess->server))
-			EMIT_SIGNAL (XP_TE_CHANLISTHEAD, serv->server_session, NULL, NULL, NULL, NULL, 0);
+			EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANLISTHEAD, serv->server_session, NULL,
+										  NULL, NULL, NULL, 0, tags_data->timestamp);
 		break;
 
 	case 322:
@@ -630,14 +641,16 @@ process_numeric (session * sess, int n,
 			fe_add_chan_list (sess->server, word[4], word[5], word_eol[6] + 1);
 		} else
 		{
-			PrintTextf (serv->server_session, "%-16s %-7d %s\017\n",
-							word[4], atoi (word[5]), word_eol[6] + 1);
+			PrintTextTimeStampf (serv->server_session, tags_data->timestamp,
+										"%-16s %-7d %s\017\n", word[4], atoi (word[5]),
+										word_eol[6] + 1);
 		}
 		break;
 
 	case 323:
 		if (!fe_is_chanwindow (sess->server))
-			EMIT_SIGNAL (XP_TE_SERVTEXT, serv->server_session, text, word[1], word[2], NULL, 0);
+			EMIT_SIGNAL_TIMESTAMP (XP_TE_SERVTEXT, serv->server_session, text, 
+										  word[1], word[2], NULL, 0, tags_data->timestamp);
 		else
 			fe_chan_list_end (sess->server);
 		break;
@@ -649,8 +662,8 @@ process_numeric (session * sess, int n,
 		if (sess->ignore_mode)
 			sess->ignore_mode = FALSE;
 		else
-			EMIT_SIGNAL (XP_TE_CHANMODES, sess, word[4], word_eol[5],
-							 NULL, NULL, 0);
+			EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANMODES, sess, word[4], word_eol[5],
+										  NULL, NULL, 0, tags_data->timestamp);
 		fe_update_mode_buttons (sess, 't', '-');
 		fe_update_mode_buttons (sess, 'n', '-');
 		fe_update_mode_buttons (sess, 's', '-');
@@ -659,7 +672,7 @@ process_numeric (session * sess, int n,
 		fe_update_mode_buttons (sess, 'm', '-');
 		fe_update_mode_buttons (sess, 'l', '-');
 		fe_update_mode_buttons (sess, 'k', '-');
-		handle_mode (serv, word, word_eol, "", TRUE);
+		handle_mode (serv, word, word_eol, "", TRUE, tags_data);
 		break;
 
 	case 329:
@@ -669,36 +682,40 @@ process_numeric (session * sess, int n,
 			if (sess->ignore_date)
 				sess->ignore_date = FALSE;
 			else
-				channel_date (sess, word[4], word[5]);
+				channel_date (sess, word[4], word[5], tags_data);
 		}
 		break;
 
 	case 330:
 		if (!serv->skip_next_whois)
-			EMIT_SIGNAL (XP_TE_WHOIS_AUTH, whois_sess, word[4],
-							 word_eol[6] + 1, word[5], NULL, 0);
-		inbound_user_info (sess, NULL, NULL, NULL, NULL, word[4], NULL, word[5], 0xff);
+			EMIT_SIGNAL_TIMESTAMP (XP_TE_WHOIS_AUTH, whois_sess, word[4],
+										  word_eol[6] + 1, word[5], NULL, 0,
+										  tags_data->timestamp);
+		inbound_user_info (sess, NULL, NULL, NULL, NULL, word[4], NULL, word[5],
+								 0xff, tags_data);
 		break;
 
 	case 332:
 		inbound_topic (serv, word[4],
-						(word_eol[5][0] == ':') ? word_eol[5] + 1 : word_eol[5]);
+							(word_eol[5][0] == ':') ? word_eol[5] + 1 : word_eol[5],
+							tags_data);
 		break;
 
 	case 333:
-		inbound_topictime (serv, word[4], word[5], atol (word[6]));
+		inbound_topictime (serv, word[4], word[5], atol (word[6]), tags_data);
 		break;
 
 #if 0
 	case 338:  /* Undernet Real user@host, Real IP */
-		EMIT_SIGNAL (XP_TE_WHOIS_REALHOST, sess, word[4], word[5], word[6], 
-			(word_eol[7][0]==':') ? word_eol[7]+1 : word_eol[7], 0);
+		EMIT_SIGNAL_TIMESTAMP (XP_TE_WHOIS_REALHOST, sess, word[4], word[5], word[6], 
+									  (word_eol[7][0]==':') ? word_eol[7]+1 : word_eol[7],
+									  0, tags_data->timestamp);
 		break;
 #endif
 
 	case 341:						  /* INVITE ACK */
-		EMIT_SIGNAL (XP_TE_UINVITE, sess, word[4], word[5], serv->servername,
-						 NULL, 0);
+		EMIT_SIGNAL_TIMESTAMP (XP_TE_UINVITE, sess, word[4], word[5],
+									  serv->servername, NULL, 0, tags_data->timestamp);
 		break;
 
 	case 352:						  /* WHO */
@@ -710,12 +727,13 @@ process_numeric (session * sess, int n,
 				away = 1;
 
 			inbound_user_info (sess, word[4], word[5], word[6], word[7],
-									 word[8], word_eol[11], NULL, away);
+									 word[8], word_eol[11], NULL, away,
+									 tags_data);
 
 			/* try to show only user initiated whos */
 			if (!who_sess || !who_sess->doing_who)
-				EMIT_SIGNAL (XP_TE_SERVTEXT, serv->server_session, text, word[1],
-								 word[2], NULL, 0);
+				EMIT_SIGNAL_TIMESTAMP (XP_TE_SERVTEXT, serv->server_session, text, word[1],
+											  word[2], NULL, 0, tags_data->timestamp);
 		}
 		break;
 
@@ -734,12 +752,14 @@ process_numeric (session * sess, int n,
 
 				/* :server 354 yournick 152 #channel ~ident host servname nick H account :realname */
 				inbound_user_info (sess, word[5], word[6], word[7], word[8],
-									 word[9], word_eol[12]+1, word[11], away);
+										 word[9], word_eol[12]+1, word[11], away,
+										 tags_data);
 
 				/* try to show only user initiated whos */
 				if (!who_sess || !who_sess->doing_who)
-					EMIT_SIGNAL (XP_TE_SERVTEXT, serv->server_session, text,
-									 word[1], word[2], NULL, 0);
+					EMIT_SIGNAL_TIMESTAMP (XP_TE_SERVTEXT, serv->server_session, text,
+												  word[1], word[2], NULL, 0,
+												  tags_data->timestamp);
 			} else
 				goto def;
 		}
@@ -752,21 +772,23 @@ process_numeric (session * sess, int n,
 			if (who_sess)
 			{
 				if (!who_sess->doing_who)
-					EMIT_SIGNAL (XP_TE_SERVTEXT, serv->server_session, text,
-									 word[1], word[2], NULL, 0);
+					EMIT_SIGNAL_TIMESTAMP (XP_TE_SERVTEXT, serv->server_session, text,
+												  word[1], word[2], NULL, 0,
+												  tags_data->timestamp);
 				who_sess->doing_who = FALSE;
 			} else
 			{
 				if (!serv->doing_dns)
-					EMIT_SIGNAL (XP_TE_SERVTEXT, serv->server_session, text,
-									 word[1], word[2], NULL, 0);
+					EMIT_SIGNAL_TIMESTAMP (XP_TE_SERVTEXT, serv->server_session, text,
+												  word[1], word[2], NULL, 0, tags_data->timestamp);
 				serv->doing_dns = FALSE;
 			}
 		}
 		break;
 
 	case 346:	/* +I-list entry */
-		if (!inbound_banlist (sess, atol (word[7]), word[4], word[5], word[6], 346))
+		if (!inbound_banlist (sess, atol (word[7]), word[4], word[5], word[6], 346,
+									 tags_data))
 			goto def;
 		break;
 
@@ -776,7 +798,8 @@ process_numeric (session * sess, int n,
 		break;
 
 	case 348:	/* +e-list entry */
-		if (!inbound_banlist (sess, atol (word[7]), word[4], word[5], word[6], 348))
+		if (!inbound_banlist (sess, atol (word[7]), word[4], word[5], word[6], 348,
+									 tags_data))
 			goto def;
 		break;
 
@@ -793,16 +816,18 @@ process_numeric (session * sess, int n,
 
 	case 353:						  /* NAMES */
 		inbound_nameslist (serv, word[5],
-							(word_eol[6][0] == ':') ? word_eol[6] + 1 : word_eol[6]);
+								 (word_eol[6][0] == ':') ? word_eol[6] + 1 : word_eol[6],
+								 tags_data);
 		break;
 
 	case 366:
-		if (!inbound_nameslist_end (serv, word[4]))
+		if (!inbound_nameslist_end (serv, word[4], tags_data))
 			goto def;
 		break;
 
 	case 367: /* banlist entry */
-		if (!inbound_banlist (sess, atol (word[7]), word[4], word[5], word[6], 367))
+		if (!inbound_banlist (sess, atol (word[7]), word[4], word[5], word[6], 367,
+									 tags_data))
 			goto def;
 		break;
 
@@ -819,20 +844,21 @@ process_numeric (session * sess, int n,
 
 	case 369:	/* WHOWAS end */
 	case 406:	/* WHOWAS error */
-		EMIT_SIGNAL (XP_TE_SERVTEXT, whois_sess, text, word[1], word[2], NULL, 0);
+		EMIT_SIGNAL_TIMESTAMP (XP_TE_SERVTEXT, whois_sess, text, word[1], word[2],
+									  NULL, 0, tags_data->timestamp);
 		serv->inside_whois = 0;
 		break;
 
 	case 372:	/* motd text */
 	case 375:	/* motd start */
 		if (!prefs.hex_irc_skip_motd || serv->motd_skipped)
-			EMIT_SIGNAL (XP_TE_MOTD, serv->server_session, text, NULL, NULL,
-							 NULL, 0);
+			EMIT_SIGNAL_TIMESTAMP (XP_TE_MOTD, serv->server_session, text, NULL,
+										  NULL, NULL, 0, tags_data->timestamp);
 		break;
 
 	case 376:	/* end of motd */
 	case 422:	/* motd file is missing */
-		inbound_login_end (sess, text);
+		inbound_login_end (sess, text, tags_data);
 		break;
 
 	case 432:	/* erroneous nickname */
@@ -840,7 +866,7 @@ process_numeric (session * sess, int n,
 		{
 			goto def;
 		}
-		inbound_next_nick (sess,  word[4], 1);
+		inbound_next_nick (sess,  word[4], 1, tags_data);
 		break;
 
 	case 433:	/* nickname in use */
@@ -848,48 +874,53 @@ process_numeric (session * sess, int n,
 		{
 			goto def;
 		}
-		inbound_next_nick (sess,  word[4], 0);
+		inbound_next_nick (sess,  word[4], 0, tags_data);
 		break;
 
 	case 437:
 		if (serv->end_of_motd || is_channel (serv, word[4]))
 			goto def;
-		inbound_next_nick (sess, word[4], 0);
+		inbound_next_nick (sess, word[4], 0, tags_data);
 		break;
 
 	case 471:
-		EMIT_SIGNAL (XP_TE_USERLIMIT, sess, word[4], NULL, NULL, NULL, 0);
+		EMIT_SIGNAL_TIMESTAMP (XP_TE_USERLIMIT, sess, word[4], NULL, NULL, NULL, 0,
+									  tags_data->timestamp);
 		break;
 
 	case 473:
-		EMIT_SIGNAL (XP_TE_INVITE, sess, word[4], NULL, NULL, NULL, 0);
+		EMIT_SIGNAL_TIMESTAMP (XP_TE_INVITE, sess, word[4], NULL, NULL, NULL, 0,
+									  tags_data->timestamp);
 		break;
 
 	case 474:
-		EMIT_SIGNAL (XP_TE_BANNED, sess, word[4], NULL, NULL, NULL, 0);
+		EMIT_SIGNAL_TIMESTAMP (XP_TE_BANNED, sess, word[4], NULL, NULL, NULL, 0,
+									  tags_data->timestamp);
 		break;
 
 	case 475:
-		EMIT_SIGNAL (XP_TE_KEYWORD, sess, word[4], NULL, NULL, NULL, 0);
+		EMIT_SIGNAL_TIMESTAMP (XP_TE_KEYWORD, sess, word[4], NULL, NULL, NULL, 0,
+									  tags_data->timestamp);
 		break;
 
 	case 601:
-		notify_set_offline (serv, word[4], FALSE);
+		notify_set_offline (serv, word[4], FALSE, tags_data);
 		break;
 
 	case 605:
-		notify_set_offline (serv, word[4], TRUE);
+		notify_set_offline (serv, word[4], TRUE, tags_data);
 		break;
 
 	case 600:
 	case 604:
-		notify_set_online (serv, word[4]);
+		notify_set_online (serv, word[4], tags_data);
 		break;
 
 	case 728:	/* +q-list entry */
 		/* NOTE:  FREENODE returns these results inconsistent with e.g. +b */
 		/* Who else has imlemented MODE_QUIET, I wonder? */
-		if (!inbound_banlist (sess, atol (word[8]), word[4], word[6], word[7], 728))
+		if (!inbound_banlist (sess, atol (word[8]), word[4], word[6], word[7], 728,
+									 tags_data))
 			goto def;
 		break;
 
@@ -902,25 +933,29 @@ process_numeric (session * sess, int n,
 		ex = strchr (word[4], '!'); /* only send the nick */
 		if (ex)
 			ex[0] = 0;
-		notify_set_online (serv, word[4] + 1);
+		notify_set_online (serv, word[4] + 1, tags_data);
 		break;
 
 	case 731: /* RPL_MONOFFLINE */
 		ex = strchr (word[4], '!'); /* only send the nick */
 		if (ex)
 			ex[0] = 0;
-		notify_set_offline (serv, word[4] + 1, FALSE);
+		notify_set_offline (serv, word[4] + 1, FALSE, tags_data);
 		break;
 
 	case 900:	/* successful SASL 'logged in as ' */
-		EMIT_SIGNAL (XP_TE_SERVTEXT, serv->server_session, word_eol[6]+1, word[1], word[2], NULL, 0);
+		EMIT_SIGNAL_TIMESTAMP (XP_TE_SERVTEXT, serv->server_session, 
+									  word_eol[6]+1, word[1], word[2], NULL, 0,
+									  tags_data->timestamp);
 		break;
 	case 903:	/* successful SASL auth */
 	case 904:	/* aborted SASL auth */
 	case 905:	/* failed SASL auth */
 	case 906:	/* registration completes before SASL auth */
 	case 907:	/* attempting to re-auth after a successful auth */
-		EMIT_SIGNAL (XP_TE_SASLRESPONSE, serv->server_session, word[1], word[2], word[3], ++word_eol[4], 0);
+		EMIT_SIGNAL_TIMESTAMP (XP_TE_SASLRESPONSE, serv->server_session, word[1],
+									  word[2], word[3], ++word_eol[4], 0,
+									  tags_data->timestamp);
 		tcp_send_len (serv, "CAP END\r\n", 9);
 		break;
 
@@ -930,9 +965,9 @@ process_numeric (session * sess, int n,
 		{
 			/* some unknown WHOIS reply, ircd coders make them up weekly */
 			if (!serv->skip_next_whois)
-				EMIT_SIGNAL (XP_TE_WHOIS_SPECIAL, whois_sess, word[4],
-								(word_eol[5][0] == ':') ? word_eol[5] + 1 : word_eol[5],
-								 word[2], NULL, 0);
+				EMIT_SIGNAL_TIMESTAMP (XP_TE_WHOIS_SPECIAL, whois_sess, word[4],
+											  (word_eol[5][0] == ':') ? word_eol[5] + 1 : word_eol[5],
+											  word[2], NULL, 0, tags_data->timestamp);
 			return;
 		}
 
@@ -951,7 +986,8 @@ process_numeric (session * sess, int n,
 			else
 				sess=serv->server_session;
 			
-			EMIT_SIGNAL (XP_TE_SERVTEXT, sess, text, word[1], word[2], NULL, 0);
+			EMIT_SIGNAL_TIMESTAMP (XP_TE_SERVTEXT, sess, text, word[1], word[2],
+										  NULL, 0, tags_data->timestamp);
 		}
 	}
 }
@@ -959,7 +995,8 @@ process_numeric (session * sess, int n,
 /* handle named messages that starts with a ':' */
 
 static void
-process_named_msg (session *sess, char *type, char *word[], char *word_eol[])
+process_named_msg (session *sess, char *type, char *word[], char *word_eol[],
+						 const message_tags_data *tags_data)
 {
 	server *serv = sess->server;
 	char ip[128], nick[NICKLEN];
@@ -997,9 +1034,10 @@ process_named_msg (session *sess, char *type, char *word[], char *word_eol[])
 				if (*chan == ':')
 					chan++;
 				if (!serv->p_cmp (nick, serv->nick))
-					inbound_ujoin (serv, chan, nick, ip);
+					inbound_ujoin (serv, chan, nick, ip, tags_data);
 				else
-					inbound_join (serv, chan, nick, ip, account, realname);
+					inbound_join (serv, chan, nick, ip, account, realname,
+									  tags_data);
 			}
 			return;
 
@@ -1012,24 +1050,26 @@ process_named_msg (session *sess, char *type, char *word[], char *word_eol[])
 					if (*reason == ':')
 						reason++;
 					if (!strcmp (kicked, serv->nick))
-	 					inbound_ukick (serv, word[3], nick, reason);
+	 					inbound_ukick (serv, word[3], nick, reason, tags_data);
 					else
-						inbound_kick (serv, word[3], kicked, nick, reason);
+						inbound_kick (serv, word[3], kicked, nick, reason, tags_data);
 				}
 			}
 			return;
 
 		case WORDL('K','I','L','L'):
-			EMIT_SIGNAL (XP_TE_KILL, sess, nick, word_eol[5], NULL, NULL, 0);
+			EMIT_SIGNAL_TIMESTAMP (XP_TE_KILL, sess, nick, word_eol[5], NULL, NULL,
+										  0, tags_data->timestamp);
 			return;
 
 		case WORDL('M','O','D','E'):
-			handle_mode (serv, word, word_eol, nick, FALSE);	/* modes.c */
+			handle_mode (serv, word, word_eol, nick, FALSE, tags_data);	/* modes.c */
 			return;
 
 		case WORDL('N','I','C','K'):
-			inbound_newnick (serv, nick, (word_eol[3][0] == ':')
-									? word_eol[3] + 1 : word_eol[3], FALSE);
+			inbound_newnick (serv, nick, 
+								  (word_eol[3][0] == ':') ? word_eol[3] + 1 : word_eol[3],
+								  FALSE, tags_data);
 			return;
 
 		case WORDL('P','A','R','T'):
@@ -1042,25 +1082,28 @@ process_named_msg (session *sess, char *type, char *word[], char *word_eol[])
 				if (*reason == ':')
 					reason++;
 				if (!strcmp (nick, serv->nick))
-					inbound_upart (serv, chan, ip, reason);
+					inbound_upart (serv, chan, ip, reason, tags_data);
 				else
-					inbound_part (serv, chan, nick, ip, reason);
+					inbound_part (serv, chan, nick, ip, reason, tags_data);
 			}
 			return;
 
 		case WORDL('P','O','N','G'):
 			inbound_ping_reply (serv->server_session,
-								 (word[4][0] == ':') ? word[4] + 1 : word[4], word[3]);
+									  (word[4][0] == ':') ? word[4] + 1 : word[4],
+									  word[3], tags_data);
 			return;
 
 		case WORDL('Q','U','I','T'):
 			inbound_quit (serv, nick, ip,
-							  (word_eol[3][0] == ':') ? word_eol[3] + 1 : word_eol[3]);
+							  (word_eol[3][0] == ':') ? word_eol[3] + 1 : word_eol[3],
+							  tags_data);
 			return;
 
 		case WORDL('A','W','A','Y'):
 			inbound_away_notify (serv, nick,
-						(word_eol[3][0] == ':') ? word_eol[3] + 1 : NULL);
+										(word_eol[3][0] == ':') ? word_eol[3] + 1 : NULL,
+										tags_data);
 			return;
 		}
 
@@ -1077,7 +1120,7 @@ process_named_msg (session *sess, char *type, char *word[], char *word_eol[])
 		{
 
 		case WORDL('A','C','C','O'):
-			inbound_account (serv, nick, word[3]);
+			inbound_account (serv, nick, word[3], tags_data);
 			return;
 			
 		case WORDL('I','N','V','I'):
@@ -1085,11 +1128,13 @@ process_named_msg (session *sess, char *type, char *word[], char *word_eol[])
 				return;
 			
 			if (word[4][0] == ':')
-				EMIT_SIGNAL (XP_TE_INVITED, sess, word[4] + 1, nick,
-								 serv->servername, NULL, 0);
+				EMIT_SIGNAL_TIMESTAMP (XP_TE_INVITED, sess, word[4] + 1, nick,
+											  serv->servername, NULL, 0,
+											  tags_data->timestamp);
 			else
-				EMIT_SIGNAL (XP_TE_INVITED, sess, word[4], nick,
-								 serv->servername, NULL, 0);
+				EMIT_SIGNAL_TIMESTAMP (XP_TE_INVITED, sess, word[4], nick,
+											  serv->servername, NULL, 0,
+											  tags_data->timestamp);
 				
 			return;
 
@@ -1129,7 +1174,7 @@ process_named_msg (session *sess, char *type, char *word[], char *word_eol[])
 				}
 
 				if (!ignore_check (word[1], IG_NOTI))
-					inbound_notice (serv, word[3], nick, text, ip, id);
+					inbound_notice (serv, word[3], nick, text, ip, id, tags_data);
 			}
 			return;
 
@@ -1166,19 +1211,21 @@ process_named_msg (session *sess, char *type, char *word[], char *word_eol[])
 						if (g_ascii_strncasecmp (text, "DCC ", 4) == 0)
 							/* redo this with handle_quotes TRUE */
 							process_data_init (word[1], word_eol[1], word, word_eol, TRUE, FALSE);
-						ctcp_handle (sess, to, nick, ip, text, word, word_eol, id);
+						ctcp_handle (sess, to, nick, ip, text, word, word_eol, id,
+										 tags_data);
 					} else
 					{
 						if (is_channel (serv, to))
 						{
 							if (ignore_check (word[1], IG_CHAN))
 								return;
-							inbound_chanmsg (serv, NULL, to, nick, text, FALSE, id);
+							inbound_chanmsg (serv, NULL, to, nick, text, FALSE, id,
+												  tags_data);
 						} else
 						{
 							if (ignore_check (word[1], IG_PRIV))
 								return;
-							inbound_privmsg (serv, nick, ip, text, id);
+							inbound_privmsg (serv, nick, ip, text, id, tags_data);
 						}
 					}
 				}
@@ -1187,14 +1234,16 @@ process_named_msg (session *sess, char *type, char *word[], char *word_eol[])
 
 		case WORDL('T','O','P','I'):
 			inbound_topicnew (serv, nick, word[3],
-									(word_eol[4][0] == ':') ? word_eol[4] + 1 : word_eol[4]);
+									(word_eol[4][0] == ':') ? word_eol[4] + 1 : word_eol[4],
+									tags_data);
 			return;
 
 		case WORDL('W','A','L','L'):
 			text = word_eol[3];
 			if (*text == ':')
 				text++;
-			EMIT_SIGNAL (XP_TE_WALLOPS, sess, nick, text, NULL, NULL, 0);
+			EMIT_SIGNAL_TIMESTAMP (XP_TE_WALLOPS, sess, nick, text, NULL, NULL, 0,
+										  tags_data->timestamp);
 			return;
 		}
 	}
@@ -1202,10 +1251,6 @@ process_named_msg (session *sess, char *type, char *word[], char *word_eol[])
 	else if (len == 3)
 	{
 		guint32 t;
-		guint32 want_cap;		/* format the CAP REQ string based on previous capabilities being requested or not */
-		guint32 want_sasl;		/* CAP END shouldn't be sent when SASL is requested, it needs further responses */
-		char *pass;				/* buffer for SASL password */
-		char buffer[256];		/* buffer for requesting capabilities and emitting the signal */
 
 		t = WORDL((guint8)type[0], (guint8)type[1], (guint8)type[2], (guint8)type[3]);
 		switch (t)
@@ -1213,117 +1258,25 @@ process_named_msg (session *sess, char *type, char *word[], char *word_eol[])
 			case WORDL('C','A','P','\0'):
 				if (strncasecmp (word[4], "ACK", 3) == 0)
 				{
-					EMIT_SIGNAL (XP_TE_CAPACK, sess->server->server_session, word[1], word[5][0]==':' ? ++word_eol[5] : word_eol[5], NULL, NULL, 0);
-
-					if (strstr (word_eol[5], "identify-msg") != 0)
-					{
-						serv->have_idmsg = TRUE;
-					}
-
-					if (strstr (word_eol[5], "multi-prefix") != 0)
-					{
-						serv->have_namesx = TRUE;
-					}
-
-					if (strstr (word_eol[5], "away-notify") != 0)
-					{
-						serv->have_awaynotify = TRUE;
-					}
-
-					if (strstr (word_eol[5], "account-notify") != 0)
-					{
-						serv->have_accnotify = TRUE;
-					}
-					
-					if (strstr (word_eol[5], "extended-join") != 0)
-					{
-						serv->have_extjoin = TRUE;
-					}
-
-					if (strstr (word_eol[5], "sasl") != 0)
-					{
-						serv->have_sasl = TRUE;
-						EMIT_SIGNAL
-						(
-							XP_TE_SASLAUTH,
-							serv->server_session,
-							(((ircnet *)sess->server->network)->user) ? (((ircnet *)sess->server->network)->user) : prefs.hex_irc_user_name,
-							NULL,
-							NULL,
-							NULL,
-							0
-						);
-						tcp_send_len (serv, "AUTHENTICATE PLAIN\r\n", 20);
-
-						pass = encode_sasl_pass
-						(
-							(((ircnet *)sess->server->network)->user) ? (((ircnet *)sess->server->network)->user) : prefs.hex_irc_user_name,
-							sess->server->password
-						);
-						tcp_sendf (sess->server, "AUTHENTICATE %s\r\n", pass);
-						free (pass);
-					}
+					inbound_cap_ack (serv, word[1], 
+										  word[5][0] == ':' ? word_eol[5] + 1 : word_eol[5],
+										  tags_data);
 				}
 				else if (strncasecmp (word[4], "LS", 2) == 0)
 				{
-					EMIT_SIGNAL (XP_TE_CAPLIST, serv->server_session, word[1], word[5][0]==':' ? ++word_eol[5] : word_eol[5], NULL, NULL, 0);
-					want_cap = 0;
-					want_sasl = 0;
-
-					strcpy (buffer, "CAP REQ :");
-
-					if (strstr (word_eol[5], "identify-msg") != 0)
-					{
-						strcat (buffer, "identify-msg ");
-						want_cap = 1;
-					}
-					if (strstr (word_eol[5], "multi-prefix") != 0)
-					{
-						strcat (buffer, "multi-prefix ");
-						want_cap = 1;
-					}
-					if (strstr (word_eol[5], "away-notify") != 0)
-					{
-						strcat (buffer, "away-notify ");
-						want_cap = 1;
-					}
-					if (strstr (word_eol[5], "account-notify") != 0)
-					{
-						strcat (buffer, "account-notify ");
-						want_cap = 1;
-					}
-					if (strstr (word_eol[5], "extended-join") != 0)
-					{
-						strcat (buffer, "extended-join ");
-						want_cap = 1;
-					}
-					/* if the SASL password is set AND auth mode is set to SASL, request SASL auth */
-					if (strstr (word_eol[5], "sasl") != 0 && strlen (sess->server->password) != 0 && serv->loginmethod == LOGIN_SASL)
-					{
-						strcat (buffer, "sasl ");
-						want_cap = 1;
-						want_sasl = 1;
-					}
-
-					if (want_cap)
-					{
-						/* buffer + 9 = emit buffer without "CAP REQ :" */
-						EMIT_SIGNAL (XP_TE_CAPREQ, sess->server->server_session, buffer + 9, NULL, NULL, NULL, 0);
-						tcp_sendf (serv, "%s\r\n", buffer);
-					}
-					if (!want_sasl)
-					{
-						/* if we use SASL, CAP END is dealt via raw numerics */
-						tcp_send_len (serv, "CAP END\r\n", 9);
-					}
+					inbound_cap_ls (serv, word[1], 
+										 word[5][0] == ':' ? word_eol[5] + 1 : word_eol[5],
+										 tags_data);
 				}
 				else if (strncasecmp (word[4], "NAK", 3) == 0)
 				{
-					tcp_send_len (serv, "CAP END\r\n", 9);
+					inbound_cap_nak (serv, tags_data);
 				}
 				else if (strncasecmp (word[4], "LIST", 4) == 0)	
 				{
-					EMIT_SIGNAL (XP_TE_CAPACK, sess->server->server_session, word[1], word[5][0]==':' ? ++word_eol[5] : word_eol[5], NULL, NULL, 0);
+					inbound_cap_list (serv, word[1], 
+											word[5][0] == ':' ? word_eol[5] + 1 : word_eol[5],
+											tags_data);
 				}
 
 				return;
@@ -1332,13 +1285,14 @@ process_named_msg (session *sess, char *type, char *word[], char *word_eol[])
 
 garbage:
 	/* unknown message */
-	PrintTextf (sess, "GARBAGE: %s\n", word_eol[1]);
+	PrintTextTimeStampf (sess, tags_data->timestamp, "GARBAGE: %s\n", word_eol[1]);
 }
 
 /* handle named messages that DON'T start with a ':' */
 
 static void
-process_named_servermsg (session *sess, char *buf, char *rawname, char *word_eol[])
+process_named_servermsg (session *sess, char *buf, char *rawname, char *word_eol[],
+								 const message_tags_data *tags_data)
 {
 	sess = sess->server->server_session;
 
@@ -1349,7 +1303,8 @@ process_named_servermsg (session *sess, char *buf, char *rawname, char *word_eol
 	}
 	if (!strncmp (buf, "ERROR", 5))
 	{
-		EMIT_SIGNAL (XP_TE_SERVERERROR, sess, buf + 7, NULL, NULL, NULL, 0);
+		EMIT_SIGNAL_TIMESTAMP (XP_TE_SERVERERROR, sess, buf + 7, NULL, NULL, NULL,
+									  0, tags_data->timestamp);
 		return;
 	}
 	if (!strncmp (buf, "NOTICE ", 7))
@@ -1357,7 +1312,9 @@ process_named_servermsg (session *sess, char *buf, char *rawname, char *word_eol
 		buf = word_eol[3];
 		if (*buf == ':')
 			buf++;
-		EMIT_SIGNAL (XP_TE_SERVNOTICE, sess, buf, sess->server->servername, NULL, NULL, 0);
+		EMIT_SIGNAL_TIMESTAMP (XP_TE_SERVNOTICE, sess, buf, 
+									  sess->server->servername, NULL, NULL, 0,
+									  tags_data->timestamp);
 		return;
 	}
 	if (!strncmp (buf, "AUTHENTICATE +", 14))	/* omit SASL "empty" responses */
@@ -1365,11 +1322,99 @@ process_named_servermsg (session *sess, char *buf, char *rawname, char *word_eol
 		return;
 	}
 
-	EMIT_SIGNAL (XP_TE_SERVTEXT, sess, buf, sess->server->servername, rawname, NULL, 0);
+	EMIT_SIGNAL_TIMESTAMP (XP_TE_SERVTEXT, sess, buf, sess->server->servername,
+								  rawname, NULL, 0, tags_data->timestamp);
 }
 
-/* irc_inline() - 1 single line received from serv */
+/* Handle time-server tags.
+ * 
+ * Sets tags_data->timestamp to the correct time (in unix time). 
+ * This received time is always in UTC.
+ *
+ * See http://ircv3.atheme.org/extensions/server-time-3.2
+ */
+static void
+handle_message_tag_time (const char *time, message_tags_data *tags_data)
+{
+	/* The time format defined in the ircv3.2 specification is
+	 *       YYYY-MM-DDThh:mm:ss.sssZ
+	 * but znc simply sends a unix time (with 3 decimal places for miliseconds)
+	 * so we might as well support both.
+	 */
+	if (!*time)
+		return;
+	
+	if (time[strlen (time) - 1] == 'Z')
+	{
+		/* as defined in the specification */
+		struct tm t;
+		int z;
+
+		/* we ignore the milisecond part */
+		z = sscanf (time, "%d-%d-%dT%d:%d:%d", &t.tm_year, &t.tm_mon, &t.tm_mday,
+						&t.tm_hour, &t.tm_min, &t.tm_sec);
 
+		if (z != 6)
+			return;
+
+		t.tm_isdst = 0; /* day light saving time */
+
+		tags_data->timestamp = mktime (&t);
+
+		if (tags_data->timestamp < 0)
+		{
+			tags_data->timestamp = 0;
+			return;
+		}
+	}
+	else
+	{
+		/* znc */
+		long long int t;
+
+		/* we ignore the milisecond part */
+		if (sscanf (time, "%lld", &t) != 1)
+			return;
+
+		tags_data->timestamp = (time_t) t;
+	}
+}
+
+/* Handle message tags.
+ *
+ * See http://ircv3.atheme.org/specification/message-tags-3.2 
+ */
+static void
+handle_message_tags (server *serv, const char *tags_str,
+							message_tags_data *tags_data)
+{
+	char **tags;
+	int i;
+
+	/* FIXME We might want to avoid the allocation overhead here since 
+	 * this might be called for every message from the server.
+	 */
+	tags = g_strsplit (tags_str, ";", 0);
+
+	for (i=0; tags[i]; i++)
+	{
+		char *key = tags[i];
+		char *value = strchr (tags[i], '=');
+
+		if (!value)
+			continue;
+
+		*value = '\0';
+		value++;
+
+		if (serv->have_server_time && !strcmp (key, "time"))
+			handle_message_tag_time (value, tags_data);
+	}
+	
+	g_strfreev (tags);
+}
+
+/* irc_inline() - 1 single line received from serv */
 static void
 irc_inline (server *serv, char *buf, int len)
 {
@@ -1379,8 +1424,7 @@ irc_inline (server *serv, char *buf, int len)
 	char *word_eol[PDIWORDS+1];
 	char pdibuf_static[522]; /* 1 line can potentially be 512*6 in utf8 */
 	char *pdibuf = pdibuf_static;
-
-	url_check_line (buf, len);
+	message_tags_data tags_data = MESSAGE_TAGS_DATA_INIT;
 
 	/* need more than 522? fall back to malloc */
 	if (len >= sizeof (pdibuf_static))
@@ -1392,11 +1436,27 @@ irc_inline (server *serv, char *buf, int len)
 	word[PDIWORDS] = NULL;
 	word_eol[PDIWORDS] = NULL;
 
-	if (buf[0] == ':')
+	if (*buf == '@')
 	{
-		/* split line into words and words_to_end_of_line */
-		process_data_init (pdibuf, buf, word, word_eol, FALSE, FALSE);
+		char *tags = buf + 1; /* skip the '@' */
+		char *sep = strchr (buf, ' ');
+
+		if (!sep)
+			goto xit;
+		
+		*sep = '\0';
+		buf = sep + 1;
+
+		handle_message_tags(serv, tags, &tags_data);
+	}
 
+	url_check_line (buf, len);
+
+	/* split line into words and words_to_end_of_line */
+	process_data_init (pdibuf, buf, word, word_eol, FALSE, FALSE);
+
+	if (buf[0] == ':')
+	{
 		/* find a context for this message */
 		if (is_channel (serv, word[3]))
 		{
@@ -1417,7 +1477,6 @@ irc_inline (server *serv, char *buf, int len)
 
 	} else
 	{
-		process_data_init (pdibuf, buf, word, word_eol, FALSE, FALSE);
 		word[0] = type = word[1];
 		if (plugin_emit_server (sess, type, word, word_eol))
 			goto xit;
@@ -1425,7 +1484,7 @@ irc_inline (server *serv, char *buf, int len)
 
 	if (buf[0] != ':')
 	{
-		process_named_servermsg (sess, buf, word[0], word_eol);
+		process_named_servermsg (sess, buf, word[0], word_eol, &tags_data);
 		goto xit;
 	}
 
@@ -1436,10 +1495,10 @@ irc_inline (server *serv, char *buf, int len)
 		if (*text == ':')
 			text++;
 
-		process_numeric (sess, atoi (word[2]), word, word_eol, text);
+		process_numeric (sess, atoi (word[2]), word, word_eol, text, &tags_data);
 	} else
 	{
-		process_named_msg (sess, type, word, word_eol);
+		process_named_msg (sess, type, word, word_eol, &tags_data);
 	}
 
 xit:
diff --git a/src/common/proto-irc.h b/src/common/proto-irc.h
index 2de9815c..a7b4029c 100644
--- a/src/common/proto-irc.h
+++ b/src/common/proto-irc.h
@@ -17,9 +17,26 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
+#include <time.h>
+#include "hexchat.h"
+
 #ifndef HEXCHAT_PROTO_H
 #define HEXCHAT_PROTO_H
 
+#define MESSAGE_TAGS_DATA_INIT			\
+	{									\
+		(time_t)0, /* timestamp */		\
+	}
+
+/* Message tag information that might be passed along with a server message
+ *
+ * See http://ircv3.atheme.org/specification/capability-negotiation-3.1
+ */
+typedef struct 
+{
+	time_t timestamp;
+} message_tags_data;
+
 void proto_fill_her_up (server *serv);
 
 #endif
diff --git a/src/common/server.c b/src/common/server.c
index 99e7563e..e59a7ee3 100644
--- a/src/common/server.c
+++ b/src/common/server.c
@@ -1895,6 +1895,7 @@ server_set_defaults (server *serv)
 	serv->have_idmsg = FALSE;
 	serv->have_accnotify = FALSE;
 	serv->have_extjoin = FALSE;
+	serv->have_server_time = FALSE;
 	serv->have_sasl = FALSE;
 	serv->have_except = FALSE;
 	serv->have_invite = FALSE;
diff --git a/src/common/text.c b/src/common/text.c
index f0a1dfb1..a0e860ce 100644
--- a/src/common/text.c
+++ b/src/common/text.c
@@ -866,7 +866,7 @@ text_validate (char **text, int *len)
 }
 
 void
-PrintText (session *sess, char *text)
+PrintTextTimeStamp (session *sess, char *text, time_t timestamp)
 {
 	char *conv;
 
@@ -890,13 +890,19 @@ PrintText (session *sess, char *text)
 
 	log_write (sess, text);
 	scrollback_save (sess, text);
-	fe_print_text (sess, text, 0);
+	fe_print_text (sess, text, timestamp);
 
 	if (conv)
 		g_free (conv);
 }
 
 void
+PrintText (session *sess, char *text)
+{
+	PrintTextTimeStamp (sess, text, 0);
+}
+
+void
 PrintTextf (session *sess, char *format, ...)
 {
 	va_list args;
@@ -910,6 +916,20 @@ PrintTextf (session *sess, char *format, ...)
 	g_free (buf);
 }
 
+void
+PrintTextTimeStampf (session *sess, time_t timestamp, char *format, ...)
+{
+	va_list args;
+	char *buf;
+
+	va_start (args, format);
+	buf = g_strdup_vprintf (format, args);
+	va_end (args);
+
+	PrintTextTimeStamp (sess, buf, timestamp);
+	g_free (buf);
+}
+
 /* Print Events stuff here --AGL */
 
 /* Consider the following a NOTES file:
@@ -1836,12 +1856,13 @@ format_event (session *sess, int index, char **args, char *o, int sizeofo, unsig
 }
 
 static void
-display_event (session *sess, int event, char **args, unsigned int stripcolor_args)
+display_event (session *sess, int event, char **args, 
+					unsigned int stripcolor_args, time_t timestamp)
 {
 	char o[4096];
 	format_event (sess, event, args, o, sizeof (o), stripcolor_args);
 	if (o[0])
-		PrintText (sess, o);
+		PrintTextTimeStamp (sess, o, timestamp);
 }
 
 int
@@ -2042,7 +2063,8 @@ text_color_of (char *name)
 /* called by EMIT_SIGNAL macro */
 
 void
-text_emit (int index, session *sess, char *a, char *b, char *c, char *d)
+text_emit (int index, session *sess, char *a, char *b, char *c, char *d,
+			  time_t timestamp)
 {
 	char *word[PDIWORDS];
 	int i;
@@ -2120,7 +2142,7 @@ text_emit (int index, session *sess, char *a, char *b, char *c, char *d)
 	}
 
 	sound_play_event (index);
-	display_event (sess, index, word, stripcolor_args);
+	display_event (sess, index, word, stripcolor_args, timestamp);
 }
 
 char *
@@ -2143,7 +2165,7 @@ text_emit_by_name (char *name, session *sess, char *a, char *b, char *c, char *d
 	i = pevent_find (name, &i);
 	if (i >= 0)
 	{
-		text_emit (i, sess, a, b, c, d);
+		text_emit (i, sess, a, b, c, d, 0);
 		return 1;
 	}
 
diff --git a/src/common/text.h b/src/common/text.h
index 52cc8ea9..5a51c894 100644
--- a/src/common/text.h
+++ b/src/common/text.h
@@ -17,12 +17,17 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
+#include <time.h>
 #include "textenums.h"
 
 #ifndef HEXCHAT_TEXT_H
 #define HEXCHAT_TEXT_H
 
-#define EMIT_SIGNAL(i, sess, a, b, c, d, e) text_emit(i, sess, a, b, c, d)
+/* timestamp is non-zero if we are using server-time */
+#define EMIT_SIGNAL_TIMESTAMP(i, sess, a, b, c, d, e, timestamp) \
+	text_emit(i, sess, a, b, c, d, timestamp)
+#define EMIT_SIGNAL(i, sess, a, b, c, d, e) \
+	text_emit(i, sess, a, b, c, d, 0)
 
 struct text_event
 {
@@ -37,7 +42,9 @@ void scrollback_load (session *sess);
 
 int text_word_check (char *word, int len);
 void PrintText (session *sess, char *text);
+void PrintTextTimeStamp (session *sess, char *text, time_t timestamp);
 void PrintTextf (session *sess, char *format, ...);
+void PrintTextTimeStampf (session *sess, time_t timestamp, char *format, ...);
 void log_close (session *sess);
 void log_open_or_close (session *sess);
 void load_text_events (void);
@@ -46,7 +53,8 @@ int pevt_build_string (const char *input, char **output, int *max_arg);
 int pevent_load (char *filename);
 void pevent_make_pntevts (void);
 int text_color_of (char *name);
-void text_emit (int index, session *sess, char *a, char *b, char *c, char *d);
+void text_emit (int index, session *sess, char *a, char *b, char *c, char *d,
+		time_t timestamp);
 int text_emit_by_name (char *name, session *sess, char *a, char *b, char *c, char *d);
 char *text_validate (char **text, int *len);
 int get_stamp_str (char *fmt, time_t tim, char **ret);
diff --git a/src/common/userlist.c b/src/common/userlist.c
index 5e53be2a..c6c806bd 100644
--- a/src/common/userlist.c
+++ b/src/common/userlist.c
@@ -383,7 +383,8 @@ userlist_remove_user (struct session *sess, struct User *user)
 }
 
 void
-userlist_add (struct session *sess, char *name, char *hostname, char *account, char *realname)
+userlist_add (struct session *sess, char *name, char *hostname,
+				  char *account, char *realname, const message_tags_data *tags_data)
 {
 	struct User *user;
 	int row, prefix_chars;
@@ -391,7 +392,7 @@ userlist_add (struct session *sess, char *name, char *hostname, char *account, c
 
 	acc = nick_access (sess->server, name, &prefix_chars);
 
-	notify_set_online (sess->server, name + prefix_chars);
+	notify_set_online (sess->server, name + prefix_chars, tags_data);
 
 	user = malloc (sizeof (struct User));
 	memset (user, 0, sizeof (struct User));
diff --git a/src/common/userlist.h b/src/common/userlist.h
index 777d61ea..ebf95606 100644
--- a/src/common/userlist.h
+++ b/src/common/userlist.h
@@ -18,6 +18,7 @@
  */
 
 #include <time.h>
+#include "proto-irc.h"
 
 #ifndef HEXCHAT_USERLIST_H
 #define HEXCHAT_USERLIST_H
@@ -51,7 +52,8 @@ struct User *userlist_find (session *sess, const char *name);
 struct User *userlist_find_global (server *serv, char *name);
 void userlist_clear (session *sess);
 void userlist_free (session *sess);
-void userlist_add (session *sess, char *name, char *hostname, char *account, char *realname);
+void userlist_add (session *sess, char *name, char *hostname, char *account,
+						 char *realname, const message_tags_data *tags_data);
 int userlist_remove (session *sess, char *name);
 void userlist_remove_user (session *sess, struct User *user);
 int userlist_change (session *sess, char *oldname, char *newname);
diff --git a/src/fe-gtk/setup.c b/src/fe-gtk/setup.c
index 391004ee..2c5b982f 100644
--- a/src/fe-gtk/setup.c
+++ b/src/fe-gtk/setup.c
@@ -522,12 +522,13 @@ static const setting advanced_settings[] =
 						"Otherwise, include color information if the CONTROL key is held down "
 						"while selecting."), 0, 0},
 
-	{ST_HEADER,	N_("Miscellaneous"),0,0,0},
+	{ST_HEADER,	N_("Miscellaneous"), 0, 0, 0},
 	{ST_ENTRY,  N_("Real name:"), P_OFFSETNL(hex_irc_real_name), 0, 0, sizeof prefs.hex_irc_real_name},
 #ifdef WIN32
-	{ST_ENTRY,  N_("Alternative fonts:"), P_OFFSETNL(hex_text_font_alternative), "Separate multiple entries with commas without spaces before or after.", 0, sizeof prefs.hex_text_font_alternative},
+	{ST_ENTRY,  N_("Alternative fonts:"), P_OFFSETNL(hex_text_font_alternative), N_("Separate multiple entries with commas without spaces before or after."), 0, sizeof prefs.hex_text_font_alternative},
 #endif
 	{ST_TOGGLE,	N_("Display lists in compact mode"), P_OFFINTNL(hex_gui_compact), N_("Use less spacing between user list/channel tree rows."), 0, 0},
+	{ST_TOGGLE,	N_("Use server time if supported"), P_OFFINTNL(hex_irc_cap_server_time), N_("Display timestamps obtained from server if it supports the time-server extension."), 0, 0},
 	{ST_TOGGLE,	N_("Automatically reconnect to servers on disconnect"), P_OFFINTNL(hex_net_auto_reconnect), 0, 0, 1},
 	{ST_NUMBER,	N_("Auto reconnect delay:"), P_OFFINTNL(hex_net_reconnect_delay), 0, 0, 9999},
 	{ST_NUMBER,	N_("Auto join delay:"), P_OFFINTNL(hex_irc_join_delay), 0, 0, 9999},