//=============================================================================
//
//   File : KviIrcServerParser_ctcp.cpp
//   Creation date : Thu Aug 16 2000 13:34:42 by Szymon Stefanek
//
//   This file is part of the KVIrc IRC client distribution
//   Copyright (C) 2000-2010 Szymon Stefanek (pragma at kvirc dot net)
//
//   This program is FREE software. You can redistribute it and/or
//   modify it under the terms of the GNU General Public License
//   as published by the Free Software Foundation; either version 2
//   of the License, or (at your option) any later version.
//
//   This program is distributed in the HOPE that it will be USEFUL,
//   but WITHOUT ANY WARRANTY; without even the implied warranty of
//   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//   See the GNU General Public License for more details.
//
//   You should have received a copy of the GNU General Public License
//   along with this program. If not, write to the Free Software Foundation,
//   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
//=============================================================================

// FIXME: #warning "CTCP BEEP == WAKEUP == AWAKE"
// FIXME: #warning "CTCP AVATARREQ or QUERYAVATAR"

#include "KviControlCodes.h"
#include "KviRuntimeInfo.h"
#include "KviApplication.h"
#include "KviIrcServerParser.h"
#include "KviWindow.h"
#include "kvi_out.h"
#include "KviLocale.h"
#include "KviIrcSocket.h"
#include "KviChannelWindow.h"
#include "kvi_defaults.h"
#include "KviQueryWindow.h"
#include "KviIrcUserDataBase.h"
#include "KviIconManager.h"
#include "KviModuleManager.h"
#include "KviSharedFilesManager.h"
#include "KviTimeUtils.h"
#include "KviFileUtils.h"
#include "KviCtcpPageDialog.h"
#include "KviUserAction.h"
#include "KviOptions.h"
#include "KviIrcConnection.h"
#include "KviIrcConnectionUserInfo.h"
#include "KviIrcConnectionAntiCtcpFloodData.h"
#include "KviLagMeter.h"
#include "KviKvsEventTriggers.h"
#include "KviKvsScript.h"
#include "kvi_sourcesdate.h"
#include "KviRegisteredUserDataBase.h"
#include "KviBuildInfo.h"
#include "KviIrcConnectionServerInfo.h"
#include "KviIrcMessage.h"

#ifdef COMPILE_CRYPT_SUPPORT
#include "KviCryptEngine.h"
#include "KviCryptController.h"
#endif //COMPILE_CRYPT_SUPPORT

#include <cstdlib>

#include <QDateTime>
#include <QLocale>

extern KVIRC_API KviSharedFilesManager * g_pSharedFilesManager;
extern KVIRC_API KviCtcpPageDialog * g_pCtcpPageDialog;

/*
	@doc: ctcp_handling
	@title:
		KVIrc and CTCP
	@short:
		For developers: Client-To-Client Protocol handling in KVIrc
	@body:
		[big]Introduction[/big]
		Personally, I think that the CTCP specification is to
		be symbolically printed & burned. It is really too complex
		(you can go mad with the quoting specifications)
		and NO IRC CLIENT supports it completely.
		Here is my personal point of view on the CTCP protocol.
		[big]What is CTCP?[/big]
		CTCP stands for Client-to-Client Protocol. It is designed
		for exchanging almost arbitrary data between IRC clients;
		the data is embedded into text messages of the underlying
		IRC protocol.
		[big]Basic concepts[/big]
		A CTCP message is sent as the <text> part of the PRIVMSG and
		NOTICE IRC commands.[br]
		To differentiate the CTCP message from a normal IRC message
		text we use a delimiter character (ASCII char 1); we will
		use the symbol <0x01> for this delimiter.
		You may receive a CTCP message from server in one of the
		following two ways:[br]
		[b]:<source_mask> PRIVMSG <target> :<0x01><ctcp message><0x01>[/b][br]
		[b]:<source_mask> NOTICE <target>:<0x01><ctcp message><0x01>[/b][br]
		The PRIVMSG is used for CTCP REQUESTS, the NOTICE for CTCP REPLIES.
		The NOTICE form should never generate an automatic reply.[br]
		The two delimiters were used to begin and terminate the
		CTCP message; The original protocol allowed more than one CTCP
		message inside a single IRC message. [b]Nobody sends more than
		one message at once, no client can recognize it (since it
		complicates the message parsing), it could be even dangerous (see below)[/b].
		It makes no real sense unless we wanted to use the CTCP protocol to embed escape sequences
		into IRC messages, which is not the case.[br]
		Furthermore, sending more CTCP messages in a single IRC message could
		be easily used to flood a client. Assuming 450 characters available for the IRC message
		text part, you could include 50 CTCP messages containing "<0x01>VERSION<0x01>".[br]
		Since the VERSION replies are usually long (there can be 3 or 4 replies per IRC message),
		a client that has no CTCP flood protection (or has it disabled) will surely
		be disconnected while sending the replies, after only
		receiving a single IRC message (no flood for the sender).
		From my personal point of view, only [b]one CTCP message per IRC message[/b]
		should be allowed and theoretically the trailing <0x01> delimiter can be optional.
		[big]How to extract the CTCP message[/big]
		The IRC messages do not allow the following characters to be sent:[br]
		<NUL> (ASCII character 0), <CR> (Carriage return), <LF> (Line feed).[br]
		So finally we have four characters that [b]cannot appear literally into a
		CTCP message[/b]: <NUL>,<CR>,<LF>,<0x01>.[br]
		To extract a <ctcp_message> from an IRC PRIVMSG or NOTICE command you
		have to perform the following actions:[br]
		Find the <trailing> part of the IRC message (the one just after the ':'
		delimiter, or the last message token).[br]
		Check if the first character of the <trailing> is a <0x01>, if it is
		we have a <ctcp_message> beginning just after <0x01>.
		The trailing (optional) <0x01> can be removed in this phase
		or later, assuming that it is not a valid char in the <ctcp message>.[br]
		In this document I will assume that you have stripped the trailing <0x01>
		and thus from now on we will deal only with the <ctcp message> part.
		[big]Parsing a CTCP message: The quoting dilemma[/big]
		Since there are characters that cannot appear in a <ctcp message>,
		theoretically we should have to use a quoting mechanism.
		Well, in fact, no actual CTCP message uses the quoting: there
		is no need to include a <NUL>, a <CR> or <LF> inside the actually
		defined messages (The only one could be CTCP SED, but I have never
		seen it in action... is there any client that implements it?).
		We could also leave the [i]quoting[/i] to the [i]single message type semantic[/i]:
		a message that needs to include [i]any character[/i] could have its own
		encoding method (Base64 for example). With the "one CTCP per IRC message"
		convention we could even allow <0x01> inside messages. Only the leading
		(and eventually trailing) <0x01> would be the delimiter, the other ones
		would be valid characters. Finally, is there any CTCP type that needs
		<0x01> inside a message? <0x01> is not printable (as well as <CR>,<LF> and <NUL>),
		so only encoded messages (and again we can stick to the single message semantic)
		messages or the ones including special parameters. Some machines might
		allow <0x01> in filenames... well, a file with <0x01> in its name has something
		broken inside, or the creator is a sort of [i]hacker[/i] (so he also
		knows how to rename a file...) :).[br]
		Anyway, let's be pedantic, and define this quoting method.
		Let's use the most intuitive method, adopted all around the world:[br]
		The backslash character ('\') as escape.[br]
		An escape sequence is formed by the backslash character and a number
		of following ASCII characters. We define the following two types of escape sequences:[br]
		[b]'\XXX'[/b] (where XXX is an [b]octal number[/b] formed by three digits)
		that indicates the ASCII character with code that corresponds to the number.[br]
		[b]'\C'[/b] (where C is a [b]CTCP valid ASCII non digit character[/b]) that corresponds
		literally to the character C discarding any other semantic that might be associated
		with it (This will become clear later).
		I've chosen the octal representation just to follow a bit the old specification:
		the authors seemed to like it. This point could be discussed in
		some mailing list or sth. The '\C' sequence is useful to include the backslash
		character (escape sequence '\\').
		[big]Let's mess a little more[/big]
		A CTCP message is made of [b]space separated parameters[/b].[br]
		The natural way of separating parameters is to use the space character.
		We define a [i]token[/i] as a sequence of valid CTCP characters not including literal space.
		A <ctcp parameter> is usually a token, but not always;
		filenames can contain spaces inside names (and it happens very often!).
		So one of the parameters of CTCP DCC is not a space separated token.
		How do we handle it? Again a standard is missing. Some clients simply change
		the filename placing underscores instead of spaces, this is a reasonable solution if used with care.
		Other clients attempt to [i]isolate[/i] the filename token by surrounding it with some kind
		of quotes, usually the [b]"[/b] or [b]'[/b] characters. This is also a good solution.
		Another one that naturally comes into my mind is to use the previously defined
		quoting to define a [i]non-breaking space[/i] character, because a space after a backslash
		could lose its original semantic. Better yet, use the backslash followed by
		the octal representation of the space character ('\040').
		Anyway, to maintain compatibility with other popular IRC clients (such as mIRC),
		let's include the [b]"[/b] quotes in our standard: literal (unescaped) [b]"[/b] quotes
		define a single token string. To include a literal [b]"[/b] character, escape it.
		Additionally, the last parameter of a <ctcp message> may be made of multiple tokens.
		[big]A CTCP parameter extracting example[/big]
		A trivial example of a C [i]CTCP parameter extracting routine[/i] follows.[br]
		An IRC message is made of up to 510 usable characters.
		When a CTCP is sent there is a PRIVMSG or NOTICE token that uses at least 6 characters,
		at least two spaces and a target token (that can not be empty, so it is at least one character)
		and finally one <0x01> escape character. This gives 500 characters as maximum size
		for a complete <ctcp message> and thus for a <ctcp token>.
		In fact, the <ctcp message> is always smaller than 500 characters; there are usually two
		<0x01> chars, there is a message source part at the beginning of the IRC message
		that is 10-15 characters long, and there is a [b]:[/b] character before the trailing parameter.
		Anyway, to really be on the [i]safe side[/i], we use a 512 character buffer for each
		<ctcp token>. Finally, I'll assume that you have already ensured that
		the <ctcp message> that we are extracting from is shorter than 511 characters in all,
		and have provided a buffer big enough to avoid this code segfaulting.
		I'm assuming that msg_ptr points somewhere in the <ctcp message> and is null-terminated.[br]
		(There are C++ style comments, you might want to remove them)
		[example]
		const char * decode_escape(const char * msg_ptr,char * buffer)
		{
			// This one decodes an escape sequence
			// and returns the pointer "just after it"
			// and should be called when *msg_ptr points
			// just after a backslash
			char c;
			if((*msg_ptr >= '0') && (*msg_ptr < '8'))
			{
				// a digit follows the backslash
				c = *msg_ptr - '0';
				msg_ptr++;
				if(*msg_ptr >= '0') && (*msg_ptr < '8'))
				{
					c = ((c << 3) + (*msg_ptr - '0'));
					msg_ptr++;
					if(*msg_ptr >= '0') && (*msg_ptr < '8'))
					{
						c = ((c << 3) + (*msg_ptr - '0'));
						msg_ptr++;
					} // else broken message, but let's be flexible
				} // else it is broken, but let's be flexible
				// append the character and return
				*buffer = c;
				return msg_ptr;
			} else {
				// simple escape: just append the following
				// character (thus discarding its semantic)
				*buffer = *msg_ptr;
				return ++msg_ptr;
			}
		}

		const char * extract_ctcp_parameter(const char * msg_ptr,char * buffer,int spaceBreaks)
		{
			// this one extracts the "next" ctcp parameter in msg_ptr
			// it skips the leading and trailing spaces.
			// spaceBreaks should be set to 0 if (and only if) the
			// extracted parameter is the last in the CTCP message.
			int inString = 0;
			while(*msg_ptr == ' ')msg_ptr++;
			while(*msg_ptr)
			{
				switch(*msg_ptr)
				{
					case '\\':
						// backslash : escape sequence
						msg_ptr++;
						if(*msg_ptr)msg_ptr = decode_escape(msg_ptr,buffer);
						else return msg_ptr; // senseless backslash
					break;
					case ' ':
						// space : separate tokens?
						if(inString || (!spaceBreaks))*buffer++ = *msg_ptr++;
						else {
							// not in string and space breaks: end of token
							// skip trailing white space (this could be avoided)
							// and return
							while(*msg_ptr == ' ')msg_ptr++;
							return msg_ptr;
						}
					break;
					case '"':
						// a string begin or end
						inString = !inString;
						msg_ptr++;
					break;
					default:
						// any other char
						*buffer++ = *msg_ptr++;
					break;
				}
			}
			return msg_ptr;
		}
		[/example]
		[big]CTCP parameter semantics[/big]
		The first <ctcp parameter> of a <ctcp message> is the <ctcp tag>: it defines
		the semantic of the rest of the message.[br]
		Although it is a convention to specify the <ctcp tag> as uppercase letters,
		and the original specification says that the whole <ctcp message> is
		case sensitive, I'd prefer to follow the IRC message semantic (just to
		have less "special cases") and treat the whole message as [b]case insensitive[/b].[br]
		The remaining tokens depend on the <ctcp tag>. A description of known <ctcp tags>
		and thus <ctcp messages> follows.
		[big]PING[/big]
		[b]Syntax: <0x01>PING <data><0x01>[/b][br]
		The PING request is used to check the round trip time from one client to another.
		The receiving client should reply with exactly the same message but sent
		through a NOTICE instead of a PRIVMSG. The <data> usually contains an unsigned
		integer but not necessarily; it is not even mandatory for <data> to be a single token.
		The receiver should ignore the semantic of <data>.[br]
		The reply is intended to be processed by IRC clients.
		[big]VERSION[/big]
		[b]Syntax: <0x01>VERSION<0x01>[/b][br]
		The VERSION request asks for information about another user's IRC client program.
		The reply should be sent through a NOTICE with the following syntax:[br]
		<0x01>VERSION <client_version_data><0x01>[br]
		The preferred form for <client_version_data> is
		[i]<client_name>:<client_version>:<client_enviroinement>[/i], but historically
		clients (and users) send a generic reply describing the client name, version
		and eventually the used script name. This CTCP reply is intended to be human
		readable, so any form is accepted.
		[big]USERINFO[/big]
		[b]Syntax: <0x01>USERINFO<0x01>[/b][br]
		The USERINFO request asks for information about another user.
		The reply should be sent through a NOTICE with the following syntax:[br]
		<0x01>USERINFO <user_info_data><0x01>[br]
		The <user_info_data> should be a human readable [i]user defined[/i] string;
		[big]CLIENTINFO[/big]
		[b]Syntax: <0x01>CLIENTINFO<0x01>[/b][br]
		The CLIENTINFO request asks for information about another user's IRC client program.
		While VERSION requests the client program name and version, CLIENTINFO requests
		information about CTCP capabilities.[br]
		The reply should be sent through a NOTICE with the following syntax:[br]
		<0x01>CLIENTINFO <client_info_data><0x01>[br]
		The <client_info_data> should contain a list of supported CTCP request tags.
		The CLIENTINFO reply is intended to be human readable.
		[big]FINGER[/big]
		[b]Syntax: <0x01>FINGER<0x01>[/b][br]
		The FINGER request asks for information about another IRC user.
		The reply should be sent through a NOTICE with the following syntax:[br]
		<0x01>FINGER <user_info_data><0x01>[br]
		The <user_info_data> should be a human readable string containing
		the system username and possibly the system idle time;
		[big]SOURCE[/big]
		[b]Syntax: <0x01>SOURCE<0x01>[/b][br]
		The SOURCE request asks for the client homepage or ftp site information.
		The reply should be sent through a NOTICE with the following syntax:[br]
		<0x01>VERSION <homepage_url_data><0x01>[br]
		This CTCP reply is intended to be human readable, so any form is accepted.
		[big]TIME[/big]
		[b]Syntax: <0x01>TIME<0x01>[/b][br]
		The TIME request asks for the user local time.
		The reply should be sent through a NOTICE with the following syntax:[br]
		<0x01>TIME <time and date string><0x01>[br]
		This CTCP reply is intended to be human readable, so any form is accepted.
		[big]ACTION[/big]
		[b]Syntax: <0x01>ACTION<0x01>[/b][br]
		The ACTION tag is used to describe an action.[br]
		It should be sent through a NOTICE message and never generate a reply.
		[big]AVATAR (equivalent to ICON or FACE)[/big]
		[b]Syntax: <0x01>AVATAR<0x01>[/b][br]
		The AVATAR tag is used to query a user's avatar.
		[big]MULTIMEDIA (equivalent to MM or SOUND)[/big]
		[b]Syntax: <0x01>MULTIMEDIA <filename><0x01>[/b][br]
		The MULTIMEDIA tag is used to play a multimedia file on the receiver's side.[br]
		The receiving client should locate the file associated to <filename>,
		and play it. If the file can not be located
		by the receiving client, and the MULTIMEDIA tag was sent through a PRIVMSG format CTCP,
		the receiving client CAN request a [doc:dcc_connection]DCC GET[/doc] <filename> from the source user.
		If the MULTIMEDIA tag was sent through a NOTICE message, the receiving client
		should not generate any reply: the message should be notified to the receiving
		client's user and then be discarded. The <filename> should never contain a leading
		path. If any part of the <filename> appears to be a path component, it should be discarded.
		The client may decide to drop the entire message too. Older clients (including
		older releases of KVIrc) used to request the missing filenames by a particular
		non-standard private message syntax. This convention should be dropped.
		[big]DCC[/big]
		[b]Syntax: <0x01>DCC <type> <type dependent parameters><0x01>[/b][br]
		The DCC tag is used to initiate a Direct Client Connection.
		The known DCC types are:[br]
		[pre]
			CHAT
			SEND
			SSEND
			TSEND
			GET
			TGET
			ACCEPT
			RESUME
		[/pre]
*/

void KviIrcServerParser::encodeCtcpParameter(const char * param, KviCString & buffer, bool bSpaceBreaks)
{
	//
	// This one encodes a single ctcp parameter with the simplest
	// subset of rules and places it in the supplied buffer
	//
	if(!(*param))
	{
		// empty parameter: the only reason we REALLY need the double quotes
		if(bSpaceBreaks)
			buffer.append("\"\"");
		return;
	}

	const char * begin = param;

	while(*param)
	{
		switch(*param)
		{
			case ' ':
				if(bSpaceBreaks)
				{
					if(param != begin)
						buffer.append(begin, param - begin);
					buffer.append("\\040");
					param++;
					begin = param;
				}
				else
				{
					// space is non breaking (last parameter)
					param++;
				}
				break;
			case '\r':
				if(param != begin)
					buffer.append(begin, param - begin);
				buffer.append("\\015");
				param++;
				begin = param;
				break;
			case '\n':
				if(param != begin)
					buffer.append(begin, param - begin);
				buffer.append("\\012");
				param++;
				begin = param;
				break;
			case '"':
				if(param != begin)
					buffer.append(begin, param - begin);
				buffer.append("\\042");
				param++;
				begin = param;
				break;
			case '\\':
				if(param != begin)
					buffer.append(begin, param - begin);
				buffer.append("\\143");
				param++;
				begin = param;
				break;
			case 0x01:
				if(param != begin)
					buffer.append(begin, param - begin);
				buffer.append("\\001");
				param++;
				begin = param;
				break;
			default:
				param++;
				break;
		}
	}

	if(param != begin)
		buffer.append(begin, param - begin);
}

void KviIrcServerParser::encodeCtcpParameter(const char * parametr, QString & resultBuffer, bool bSpaceBreaks)
{
	// This one encodes a single ctcp parameter with the simplest
	// subset of rules and places it in the supplied buffer
	QByteArray buffer;
	const char * param = parametr;
	if(!param)
	{
		if(bSpaceBreaks)
			buffer.append("\"\"");
		return;
	}
	if(!(*param))
	{
		// empty parameter: the only reason we REALLY need the double quotes
		if(bSpaceBreaks)
			buffer.append("\"\"");
		return;
	}

	while(*param)
	{
		switch(*param)
		{
			case ' ':
				if(bSpaceBreaks)
				{
					buffer.append("\\040");
					param++;
				}
				else
				{
					buffer += *param;
					param++;
				}
				break;
			case '\r':
				buffer.append("\\015");
				param++;
				break;
			case '\n':
				buffer.append("\\012");
				param++;
				break;
			case '"':
				buffer.append("\\042");
				param++;
				break;
			case '\\':
				buffer.append("\\143");
				param++;
				break;
			case 0x01:
				buffer.append("\\001");
				param++;
				break;
			default:
				buffer += *param;
				param++;
				break;
		}
	}
	resultBuffer = buffer;
}

const char * KviIrcServerParser::decodeCtcpEscape(const char * msg_ptr, KviCString & buffer)
{
	// This one decodes an octal sequence
	// and returns the pointer "just after it".
	// It should be called when *msg_ptr points
	// just after a backslash.
	// The decoded escape is appended to the buffer
	//
	// We're also assuming that *msg_ptr is not null here
	if((*msg_ptr >= '0') && (*msg_ptr < '8'))
	{
		// a digit follows the backslash */
		char c = *msg_ptr - '0';
		msg_ptr++;
		if((*msg_ptr >= '0') && (*msg_ptr < '8'))
		{
			c = ((c << 3) + (*msg_ptr - '0'));
			msg_ptr++;
			if((*msg_ptr >= '0') && (*msg_ptr < '8'))
			{
				c = ((c << 3) + (*msg_ptr - '0'));
				msg_ptr++;
			} // else broken message, but let's be flexible
		}         // else it is broken, but let's be flexible
		buffer.append(c);
		return msg_ptr;
	}

	if(*msg_ptr == 'r')
	{
		buffer.append('\r');
		return ++msg_ptr;
	}

	if(*msg_ptr == 'n')
	{
		buffer.append('\n');
		return ++msg_ptr;
	}

	// null escape: just append the following
	// character (thus discarding its semantics)

	buffer.append(msg_ptr);
	return ++msg_ptr;
}

const char * KviIrcServerParser::decodeCtcpEscape(const char * msg_ptr, QByteArray & buffer)
{
	// This one decodes an octal sequence
	// and returns the pointer "just after it".
	// It should be called when *msg_ptr points
	// just after a backslash.
	// The decoded escape is appended to the buffer
	//
	// We're also assuming that *msg_ptr is not null here
	if((*msg_ptr >= '0') && (*msg_ptr < '8'))
	{
		// a digit follows the backslash */
		char c = *msg_ptr - '0';
		msg_ptr++;
		if((*msg_ptr >= '0') && (*msg_ptr < '8'))
		{
			c = ((c << 3) + (*msg_ptr - '0'));
			msg_ptr++;
			if((*msg_ptr >= '0') && (*msg_ptr < '8'))
			{
				c = ((c << 3) + (*msg_ptr - '0'));
				msg_ptr++;
			} // else broken message, but let's be flexible
		}     // else it is broken, but let's be flexible
		buffer += (c ? c : ' ');
		return msg_ptr;
	}

	if(*msg_ptr == 'r')
	{
		buffer += '\r';
		return ++msg_ptr;
	}
	if(*msg_ptr == 'n')
	{
		buffer += '\n';
		return ++msg_ptr;
	}

	// null escape: just append the following
	// character (thus discarding its semantics)

	buffer += *msg_ptr;
	return ++msg_ptr;
}

const char * KviIrcServerParser::extractCtcpParameter(const char * msg_ptr, KviCString & buffer, bool bSpaceBreaks, bool bSafeOnly)
{
	// This one extracts the "next" ctcp parameter in msg_ptr
	// and puts it in the supplied buffer.
	// It is assumed that the leading and trailing CTCP
	// tags have been already removed.
	// Skips the leading and trailing spaces.
	// bSpaceBreaks should be set to false if (and only if) the
	// extracted parameter is the last in a positional parameter
	// based CTCP message.

	if(!msg_ptr)
		return nullptr;
	while(*msg_ptr == ' ')
		msg_ptr++; // skip leading spaces

	int bInString = 0;
	if(*msg_ptr == '"')
	{
		// a quoted parameter
		bInString = 1;
		msg_ptr++;
	}

	const char * begin = msg_ptr;

	while(*msg_ptr)
	{
		switch(*msg_ptr)
		{
			case '\\':
				// backslash : escape sequence
				if(bSafeOnly)
					msg_ptr++;
				else
				{
					if(msg_ptr != begin)
						buffer.append(begin, msg_ptr - begin);
					msg_ptr++;
					if(*msg_ptr)
					{
						// decode the escape
						msg_ptr = decodeCtcpEscape(msg_ptr, buffer);
						begin = msg_ptr;
					}
					// else it is a senseless trailing backslash.
					// Just ignore and let the function
					// return spontaneously.
				}
				break;
			case ' ':
				// space : separate tokens if not in string
				if(bInString || (!bSpaceBreaks))
					msg_ptr++;
				else
				{
					// Not in string and space breaks: end of token
					// skip trailing white space (this could be avoided)
					// and return
					if(msg_ptr != begin)
						buffer.append(begin, msg_ptr - begin);
					while(*msg_ptr == ' ')
						msg_ptr++;
					return msg_ptr;
				}
				break;
			case '"':
				if(bInString && !bSafeOnly)
				{
					// A string terminator. We don't return
					// immediately since if !bSpaceBreaks
					// we must handle tokens until the end
					// and otherwise we just run up to the
					// next breaking space (but that's a bug anyway, heh).
					if(msg_ptr != begin)
						buffer.append(begin, msg_ptr - begin);
					bInString = 0;
					msg_ptr++;
					begin = msg_ptr;
				}
				else
				{
					// we don't begin a string here
					// since we're in the middle of the token
					// it is assumed to be simply a non encoded "
					msg_ptr++;
				}
				break;
			default:
				// any other char
				msg_ptr++;
				break;
		}
	}
	if(msg_ptr != begin)
		buffer.append(begin, msg_ptr - begin);
	return msg_ptr;
}

const char * KviIrcServerParser::extractCtcpParameter(const char * p_msg_ptr, QString & resultBuffer, bool bSpaceBreaks, bool bSafeOnly)
{
	// This one extracts the "next" ctcp parameter in p_msg_ptr
	// and puts it in the supplied buffer.
	// It is assumed that the leading and trailing CTCP
	// tags have been already removed.
	// Skips the leading and trailing spaces.
	// bSpaceBreaks should be set to false if (and only if) the
	// extracted parameter is the last in a positional parameter
	// based CTCP message.

	QByteArray buffer;
	const char * msg_ptr = p_msg_ptr;
	int bInString = 0;
	if(!msg_ptr)
		return nullptr;
	while(*msg_ptr == ' ')
		msg_ptr++; // skip leading spaces

	if(*msg_ptr == '"')
	{
		// a quoted parameter
		bInString = 1;
		msg_ptr++;
	}

	while(*msg_ptr)
	{
		switch(*msg_ptr)
		{
			case '\\':
				// backslash : escape sequence
				if(bSafeOnly)
					msg_ptr++;
				else
				{
					msg_ptr++;
					if(*msg_ptr)
					{
						// decode the escape
						msg_ptr = decodeCtcpEscape(msg_ptr, buffer);
					}
					// else it is a senseless trailing backslash.
					// Just ignore and let the function
					// return spontaneously.
				}
				break;
			case ' ':
				// space : separate tokens if not in string
				if(bInString || (!bSpaceBreaks))
				{
					buffer += *msg_ptr;
					msg_ptr++;
				}
				else
				{
					// Not in string and space breaks: end of token
					// skip trailing white space (this could be avoided)
					// and return
					while(*msg_ptr == ' ')
						msg_ptr++;
					resultBuffer = buffer;
					return msg_ptr;
				}
				break;
			case '"':
				if(bInString && !bSafeOnly)
				{
					// A string terminator. We don't return
					// immediately since if !bSpaceBreaks
					// we must handle tokens until the end
					// and otherwise we just run up to the
					// next breaking space (but that's a bug anyway, heh).
					buffer += *msg_ptr;
					bInString = 0;
					msg_ptr++;
				}
				else
				{
					// we don't begin a string here
					// since we're in the middle of the token
					// it is assumed to be simply a non encoded "
					buffer += *msg_ptr;
					msg_ptr++;
				}
				break;
			default:
				// any other char
				buffer += *msg_ptr;
				msg_ptr++;
				break;
		}
	}
	resultBuffer = buffer;
	return msg_ptr;
}

void KviIrcServerParser::parseCtcpRequest(KviCtcpMessage * msg)
{
	msg->pData = extractCtcpParameter(msg->pData, msg->szTag);

	bool bAction = KviQString::equalCI(msg->szTag, "ACTION");

	if (!bAction)
	{
		if (IS_ME(msg->msg, msg->pSource->nick()) && !IS_ME(msg->msg, msg->szTarget))
		{
			// "znc.in/self-message" capability: Handle a replayed message from ourselves to someone else.
			return;
		}
	}

	if(msg->szTag == "DCC" || msg->szTag == "XDCC" || msg->szTag == "TDCC") // is dcc request
	{
		if(KVI_OPTION_BOOL(KviOption_boolIgnoreCtcpDcc))
		{
			msg->msg->console()->output(KVI_OUT_IGNORE, __tr2qs("Ignoring DCC from \r!nc\r%s\r [%s@\r!h\r%s\r]"),
			    msg->pSource->nick().toUtf8().data(),
			    msg->pSource->user().toUtf8().data(),
			    msg->pSource->host().toUtf8().data());
			return;
		}
	}
	else
	{

		KviRegisteredUser * u = msg->msg->connection()->userDataBase()->registeredUser(msg->pSource->nick(), msg->pSource->user(), msg->pSource->host());

		//Ignore it?
		if(u)
		{
			if(
			    (!bAction && u->isIgnoreEnabledFor(KviRegisteredUser::Ctcp)) || (bAction && u->isIgnoreEnabledFor(IS_ME(msg->msg, msg->szTarget) ? KviRegisteredUser::Query : KviRegisteredUser::Channel)))
			{
				if(KVI_OPTION_BOOL(KviOption_boolVerboseIgnore))
				{
					msg->msg->console()->output(KVI_OUT_IGNORE, __tr2qs("Ignoring CTCP from \r!nc\r%s\r [%s@\r!h\r%s\r]"),
					    msg->pSource->nick().toUtf8().data(),
					    msg->pSource->user().toUtf8().data(),
					    msg->pSource->host().toUtf8().data());
				}
				return;
			}
		}
	}

	for(int i = 0; m_ctcpParseProcTable[i].msgName; i++)
	{
		if(KviQString::equalCS(KviQString::upperISO88591(msg->szTag), m_ctcpParseProcTable[i].msgName)
		    && m_ctcpParseProcTable[i].req)
		{
			if(!(m_ctcpParseProcTable[i].iFlags & KVI_CTCP_MESSAGE_PARSE_TRIGGERNOEVENT))
			{
				QString szData = msg->msg->connection()->decodeText(msg->pData);
				if(
				    KVS_TRIGGER_EVENT_6_HALTED(
				        KviEvent_OnCTCPRequest,
				        msg->msg->console(),
				        msg->pSource->nick(),
				        msg->pSource->user(),
				        msg->pSource->host(),
				        msg->szTarget,
				        msg->szTag,
				        szData))
					return;
			}
			(this->*(m_ctcpParseProcTable[i].req))(msg);
			return;
		}
	}

	QString szData = msg->msg->connection()->decodeText(msg->pData);
	// trigger the event on unrecognized requests too
	if(
	    KVS_TRIGGER_EVENT_6_HALTED(
	        KviEvent_OnCTCPRequest,
	        msg->msg->console(),
	        msg->pSource->nick(),
	        msg->pSource->user(),
	        msg->pSource->host(),
	        msg->szTarget,
	        msg->szTag,
	        szData))
		return;

	// unknown
	msg->bUnknown = true;
	echoCtcpRequest(msg);
}

void KviIrcServerParser::parseCtcpReply(KviCtcpMessage * msg)
{
	msg->pData = extractCtcpParameter(msg->pData, msg->szTag);

	for(int i = 0; m_ctcpParseProcTable[i].msgName; i++)
	{
		if(KviQString::equalCS(KviQString::upperISO88591(msg->szTag), m_ctcpParseProcTable[i].msgName)
		    && m_ctcpParseProcTable[i].rpl)
		{
			if(!(m_ctcpParseProcTable[i].iFlags & KVI_CTCP_MESSAGE_PARSE_TRIGGERNOEVENT))
			{
				QString szData = msg->msg->connection()->decodeText(msg->pData);
				if(KVS_TRIGGER_EVENT_6_HALTED(KviEvent_OnCTCPReply,
				       msg->msg->console(), msg->pSource->nick(), msg->pSource->user(),
				       msg->pSource->host(), msg->szTarget, msg->szTag, szData))
					return;
			}
			(this->*(m_ctcpParseProcTable[i].rpl))(msg);
			return;
		}
	}

	QString szData = msg->msg->connection()->decodeText(msg->pData);
	// trigger the event on unrecognized replies too
	if(KVS_TRIGGER_EVENT_6_HALTED(KviEvent_OnCTCPReply,
	       msg->msg->console(), msg->pSource->nick(), msg->pSource->user(),
	       msg->pSource->host(), msg->szTarget, msg->szTag, szData))
		return;

	// unknown
	msg->bUnknown = true;
	echoCtcpReply(msg);
}

// Ctcp message handlers

bool KviIrcServerParser::checkCtcpFlood(KviCtcpMessage * msg)
{
	if(!KVI_OPTION_BOOL(KviOption_boolUseCtcpFloodProtection))
		return false;

	kvi_time_t tNow = kvi_unixTime();

	KviIrcConnectionAntiCtcpFloodData * d = msg->msg->connection()->antiCtcpFloodData();

	unsigned int interval = (unsigned int)(((unsigned int)tNow) - ((unsigned int)d->lastCtcpTime()));

	if(interval < KVI_OPTION_UINT(KviOption_uintCtcpFloodCheckInterval))
	{
		d->increaseCtcpCount();
		if(d->ctcpCount() > KVI_OPTION_UINT(KviOption_uintMaxCtcpRequests))
		{
			// This is flood
			msg->bIsFlood = true;
			return true;
		}
	}
	else
	{
		d->setLastCtcpTime(tNow);
		d->setCtcpCount(1);
	}
	return false;
}

void KviIrcServerParser::replyCtcp(KviCtcpMessage * msg, const QString & data)
{
	QByteArray szNick = msg->msg->connection()->encodeText(msg->pSource->nick());
	msg->msg->connection()->sendFmtData(
	    "NOTICE %s :%c%s %s%c",
	    szNick.data(),
	    0x01,
	    msg->msg->connection()->encodeText(msg->szTag).data(),
	    msg->msg->connection()->encodeText(data).data(),
	    0x01);
}

void KviIrcServerParser::echoCtcpReply(KviCtcpMessage * msg)
{
	if(!msg->msg->haltOutput())
	{
		KviWindow * pOut = KVI_OPTION_BOOL(KviOption_boolCtcpRepliesToActiveWindow) ? msg->msg->console()->activeWindow() : msg->msg->console();

		bool bIsChannel = false;

		if(!IS_ME(msg->msg, msg->szTarget))
		{
			// Channel ctcp request!
			pOut = msg->msg->connection()->findChannel(msg->szTarget);
			if(!pOut)
			{
				pOut = msg->msg->console();
				pOut->output(KVI_OUT_SYSTEMWARNING,
				    __tr2qs("The following CTCP reply has unrecognized target %Q"), &(msg->szTarget));
			}
			else
				bIsChannel = true;
		}

		QString szData = msg->msg->connection()->decodeText(msg->pData);

		QString szWhat = bIsChannel ? __tr2qs("Channel CTCP") : QString("CTCP");

		pOut->output(
		    msg->bUnknown ? KVI_OUT_CTCPREPLYUNKNOWN : KVI_OUT_CTCPREPLY,
		    __tr2qs("%Q %Q reply from \r!n\r%Q\r [%Q@\r!h\r%Q\r]: %Q"),
		    &szWhat, &(msg->szTag), &(msg->pSource->nick()), &(msg->pSource->user()),
		    &(msg->pSource->host()), &szData);
	}
}

void KviIrcServerParser::echoCtcpRequest(KviCtcpMessage * msg)
{
	// FIXME: #warning "DEDICATED CTCP WINDOW...MINIMIZED ?"
	if(!msg->msg->haltOutput())
	{
		QString req = msg->szTag;
		if(*(msg->pData))
		{
			req.append(" ");
			req.append(msg->pData);
		}

		KviWindow * pOut = KVI_OPTION_BOOL(KviOption_boolCtcpRequestsToActiveWindow) ? msg->msg->console()->activeWindow() : msg->msg->console();

		bool bIsChannel = false;

		if(!IS_ME(msg->msg, msg->szTarget))
		{
			// Channel ctcp request!
			pOut = msg->msg->connection()->findChannel(msg->szTarget);
			if(!pOut)
			{
				pOut = msg->msg->console();
				pOut->output(KVI_OUT_SYSTEMWARNING,
				    __tr2qs("The following CTCP request has unrecognized target %Q"),
				    &(msg->szTarget));
			}
			else
				bIsChannel = true;
		}

		QString szRequest = req;
		QString szTag = msg->szTag;
		QString szWhat = bIsChannel ? __tr2qs("Channel CTCP") : QString("CTCP");

		if(msg->bIsFlood)
		{
			QString szData = msg->msg->connection()->decodeText(msg->pData);
			if(!KVS_TRIGGER_EVENT_6_HALTED(KviEvent_OnCTCPFlood, pOut, msg->pSource->nick(), msg->pSource->user(), msg->pSource->host(), msg->szTarget, msg->szTag, szData))
				pOut->output(KVI_OUT_CTCPREQUESTFLOOD,
				    __tr2qs("%Q %Q%c request from \r!n\r%Q\r [%Q@\r!h\r%Q\r] (%Q), ignored (flood limit exceeded)"),
				    &szWhat, &szTag, KviControlCodes::Reset, &(msg->pSource->nick()),
				    &(msg->pSource->user()), &(msg->pSource->host()), &szRequest);
		}
		else
		{
			QString szAction = msg->bUnknown ? __tr2qs("ignored (unrecognized)") : (msg->bIgnored ? __tr2qs("ignored") : __tr2qs("replied"));
			pOut->output(
			    msg->bUnknown ? KVI_OUT_CTCPREQUESTUNKNOWN : (msg->bIgnored ? KVI_OUT_CTCPREQUESTIGNORED : KVI_OUT_CTCPREQUESTREPLIED),
			    __tr2qs("%Q %Q%c request from \r!n\r%Q\r [%Q@\r!h\r%Q\r] (%Q), %Q"),
			    &szWhat, &szTag, KviControlCodes::Reset, &(msg->pSource->nick()),
			    &(msg->pSource->user()), &(msg->pSource->host()), &szRequest, &szAction);
		}
	}
}

void KviIrcServerParser::parseCtcpRequestPing(KviCtcpMessage * msg)
{
	if(!checkCtcpFlood(msg))
	{
		if(!KVI_OPTION_BOOL(KviOption_boolIgnoreCtcpPing))
		{
			replyCtcp(msg, msg->msg->connection()->encodeText(msg->pData));
		}
		else
			msg->bIgnored = true;
	}

	echoCtcpRequest(msg);
}

void KviIrcServerParser::parseCtcpReplyPing(KviCtcpMessage * msg)
{
	if(!msg->msg->haltOutput())
	{
		KviWindow * pOut = KVI_OPTION_BOOL(KviOption_boolCtcpRepliesToActiveWindow) ? msg->msg->console()->activeWindow() : msg->msg->console();

		bool bIsChannel = false;

		if(!IS_ME(msg->msg, msg->szTarget))
		{
			// Channel ctcp request!
			pOut = msg->msg->connection()->findChannel(msg->szTarget);
			if(!pOut)
			{
				pOut = msg->msg->console();
				pOut->output(KVI_OUT_SYSTEMWARNING,
				    __tr2qs("The following CTCP PING reply has unrecognized target \"%Q\""),
				    &(msg->szTarget));
			}
			else
				bIsChannel = true;
		}

		unsigned int uSecs;
		unsigned int uMSecs = 0;

		KviCString szTime;

		struct timeval tv;
		kvi_gettimeofday(&tv);

		msg->pData = extractCtcpParameter(msg->pData, szTime, true);

		bool bOk;

		if(szTime.contains('.'))
		{
			KviCString szUSecs = szTime;
			szUSecs.cutToFirst('.');
			szTime.cutFromFirst('.');

			uMSecs = szUSecs.toUInt(&bOk);
			if(!bOk)
			{
				uMSecs = 0;
				tv.tv_usec = 0;
			}
		}
		else
			tv.tv_usec = 0;

		uSecs = szTime.toUInt(&bOk);
		if(!bOk)
			pOut->output(KVI_OUT_SYSTEMWARNING,
			    __tr2qs("The following CTCP PING reply has a broken time identifier \"%S\", don't trust the displayed time"), &szTime);

		unsigned int uDiffSecs = tv.tv_sec - uSecs;

		while(uMSecs > 1000000)
			uMSecs /= 10; // precision too high?
		if(((unsigned int)tv.tv_usec) < uMSecs)
		{
			tv.tv_usec += 1000000;
			if(uDiffSecs > 0)
				uDiffSecs--;
		}
		unsigned int uDiffMSecs = (tv.tv_usec - uMSecs) / 1000;

		QString szWhat = bIsChannel ? __tr2qs("Channel CTCP") : QString("CTCP");

		pOut->output(
		    msg->bUnknown ? KVI_OUT_CTCPREPLYUNKNOWN : KVI_OUT_CTCPREPLY,
		    __tr2qs("%Q PING reply from \r!n\r%Q\r [%Q@\r!h\r%Q\r]: %u sec %u msec"),
		    &szWhat, &(msg->pSource->nick()),
		    &(msg->pSource->user()), &(msg->pSource->host()), uDiffSecs, uDiffMSecs);
	}
}

void KviIrcServerParser::parseCtcpRequestVersion(KviCtcpMessage * msg)
{
	if(!checkCtcpFlood(msg))
	{
		if(!KVI_OPTION_BOOL(KviOption_boolIgnoreCtcpVersion))
		{
			QString szVersion;

			szVersion = "KVIrc " KVI_VERSION;
			if(KviBuildInfo::buildRevision() != QString())
			{
				szVersion += " git:";
				szVersion += KviBuildInfo::buildRevision();
			}
			szVersion += " '" KVI_RELEASE_NAME "' " KVI_SOURCES_DATE " - build ";
			szVersion += KviBuildInfo::buildDate();
#if defined(COMPILE_ON_WINDOWS) || defined(COMPILE_ON_MINGW)
			szVersion.append(QString(" - %1").arg(KviRuntimeInfo::version()));
#else
			szVersion.append(QString(" - %1 (%2)").arg(KviRuntimeInfo::name(), KviRuntimeInfo::release()));
#endif
			if(!KVI_OPTION_STRING(KviOption_stringCtcpVersionPostfix).isEmpty())
			{
				QString sz = KVI_OPTION_STRING(KviOption_stringCtcpVersionPostfix);
				if(!sz.isEmpty())
				{
					szVersion.append(" :");
					szVersion.append(sz);
				}
			}
			replyCtcp(msg, szVersion);
		}
		else
			msg->bIgnored = true;
	}

	echoCtcpRequest(msg);
}

void KviIrcServerParser::parseCtcpRequestUserinfo(KviCtcpMessage * msg)
{
	if(!checkCtcpFlood(msg))
	{
		if(!KVI_OPTION_BOOL(KviOption_boolIgnoreCtcpUserinfo))
		{
			QString szReply;
			if(!KVI_OPTION_STRING(KviOption_stringCtcpUserInfoAge).isEmpty())
			{
				szReply = "Age=";
				szReply += KVI_OPTION_STRING(KviOption_stringCtcpUserInfoAge);
			}
			if(!KVI_OPTION_STRING(KviOption_stringCtcpUserInfoGender).isEmpty())
			{
				if(!szReply.isEmpty())
					szReply += "; ";
				szReply += "Gender=";
				szReply += KVI_OPTION_STRING(KviOption_stringCtcpUserInfoGender);
			}
			if(!KVI_OPTION_STRING(KviOption_stringCtcpUserInfoLocation).isEmpty())
			{
				if(!szReply.isEmpty())
					szReply += "; ";
				szReply += "Location=";
				szReply += KVI_OPTION_STRING(KviOption_stringCtcpUserInfoLocation);
			}
			if(!KVI_OPTION_STRING(KviOption_stringCtcpUserInfoLanguages).isEmpty())
			{
				if(!szReply.isEmpty())
					szReply += "; ";
				szReply += "Languages=";
				szReply += KVI_OPTION_STRING(KviOption_stringCtcpUserInfoLanguages);
			}
			if(!KVI_OPTION_STRING(KviOption_stringCtcpUserInfoOther).isEmpty())
			{
				if(!szReply.isEmpty())
					szReply += "; ";
				szReply += KVI_OPTION_STRING(KviOption_stringCtcpUserInfoOther);
			}
			if(szReply.isEmpty())
				szReply = KVI_DEFAULT_CTCP_USERINFO_REPLY;
			replyCtcp(msg, szReply);
		}
		else
			msg->bIgnored = true;
	}

	echoCtcpRequest(msg);
}

// FIXME: KEEP THIS TABLE UP TO DATE

static const char * ctcpTagTable[][2] = {
	{ "PING", "Returns given parameters without parsing them" },
	{ "VERSION", "Returns the version of this client" },
	{ "CLIENTINFO", "With no parameters, lists supported CTCP tags,"
	                " 'CLIENTINFO <tag>' describes <tag>" },
	{ "USERINFO", "Returns personal information about the current user" },
	{ "FINGER", "Returns information about the current user" },
	{ "SOURCE", "Returns the client homepage URL" },
	{ "TIME", "Returns the current local time" },
	{ "ACTION", "Used to describe actions, generates no reply" },
	{ "AVATAR", "Returns the current avatar (may trigger a DCC GET) or"
	            " sets your own on this side if sent through a NOTICE" },
	{ "DCC", "Initiates a DCC connection (XDCC,TDCC)" },
	{ "PAGE", "Leaves a message for this user" },
	{ nullptr, nullptr }
};

void KviIrcServerParser::parseCtcpRequestClientinfo(KviCtcpMessage * msg)
{
	// this is completely latin1
	if(!checkCtcpFlood(msg))
	{
		if(!KVI_OPTION_BOOL(KviOption_boolIgnoreCtcpClientinfo))
		{
			KviCString szTag;
			msg->pData = extractCtcpParameter(msg->pData, szTag, false);
			szTag.trim();
			szTag.toUpperISO88591();
			if(szTag.isEmpty())
			{
				QString reply("KVIrc " KVI_VERSION " '" KVI_RELEASE_NAME "' " KVI_SOURCES_DATE " - http://www.kvirc.net - Supported tags: ");
				for(int i = 0; ctcpTagTable[i][0]; i++)
				{
					reply += ctcpTagTable[i][0];
					if(ctcpTagTable[i + 1][0])
						reply += ",";
				}
				reply += " - Use 'CLIENTINFO <tag>' for a description of each tag";
				replyCtcp(msg, reply);
			}
			else
			{
				bool bFound = false;
				for(int i = 0; ctcpTagTable[i][0] && !bFound; i++)
				{
					if(kvi_strEqualCS(ctcpTagTable[i][0], szTag.ptr()))
					{
						KviCString reply(KviCString::Format, "%s: %s", ctcpTagTable[i][0], ctcpTagTable[i][1]);
						replyCtcp(msg, reply.ptr());
						bFound = true;
					}
				}
				if(!bFound)
				{
					msg->szTag = "ERRMSG";
					KviCString reply(KviCString::Format, "Unsupported tag %s", szTag.ptr());
					replyCtcp(msg, reply.ptr());
				}
			}
		}
		else
			msg->bIgnored = true;
	}

	echoCtcpRequest(msg);
}

void KviIrcServerParser::parseCtcpRequestFinger(KviCtcpMessage * msg)
{
	// completely latin1 atm
	if(!checkCtcpFlood(msg))
	{
		if(!KVI_OPTION_BOOL(KviOption_boolIgnoreCtcpFinger))
		{
			KviCString username = getenv("USER");
			if(username.isEmpty())
				username = getenv("LOGNAME");
			if(username.isEmpty())
				username = msg->msg->connection()->userInfo()->userName();
			// FIXME: #warning "UTSNAME ?...AND OTHER INFO ?...SYSTEM IDLE TIME ?...KVIRC IDLE TIME ?"
			KviCString reply(KviCString::Format, "%s", username.ptr());
			replyCtcp(msg, reply.ptr());
		}
		else
			msg->bIgnored = true;
	}

	echoCtcpRequest(msg);
}

void KviIrcServerParser::parseCtcpRequestSource(KviCtcpMessage * msg)
{
	if(!checkCtcpFlood(msg))
	{
		if(!KVI_OPTION_BOOL(KviOption_boolIgnoreCtcpSource))
		{
			QString version = "KVIrc " KVI_VERSION " '" KVI_RELEASE_NAME "' - http://www.kvirc.net/";
			if(!KVI_OPTION_STRING(KviOption_stringCtcpSourcePostfix).isEmpty())
			{
				version += " :";
				version += KVI_OPTION_STRING(KviOption_stringCtcpSourcePostfix);
			}
			replyCtcp(msg, version);
		}
		else
			msg->bIgnored = true;
	}

	echoCtcpRequest(msg);
}

void KviIrcServerParser::parseCtcpRequestTime(KviCtcpMessage * msg)
{
	if(!checkCtcpFlood(msg))
	{
		if(!KVI_OPTION_BOOL(KviOption_boolIgnoreCtcpTime))
		{
			QString szTmp;
			QDateTime date = QDateTime::currentDateTime();
			switch(KVI_OPTION_UINT(KviOption_uintOutputDatetimeFormat))
			{
				case 0:
					// this is the equivalent to an empty date.toString() call, but it's needed
					// to ensure qt4 will use the default() locale and not the system() one
					szTmp = QLocale().toString(date, "ddd MMM d hh:mm:ss yyyy");
					break;
				case 1:
					szTmp = date.toString(Qt::ISODate);
					break;
				case 2:
					szTmp = date.toString(Qt::SystemLocaleShortDate);
					break;
			}
			replyCtcp(msg, szTmp);
		}
		else
			msg->bIgnored = true;
	}

	echoCtcpRequest(msg);
}

void KviIrcServerParser::parseCtcpRequestPage(KviCtcpMessage * msg)
{
	if(!checkCtcpFlood(msg))
	{
		if(!KVI_OPTION_BOOL(KviOption_boolIgnoreCtcpPage))
		{
			KVI_OPTION_STRING(KviOption_stringCtcpPageReply) = KVI_OPTION_STRING(KviOption_stringCtcpPageReply).trimmed();
			if(KVI_OPTION_STRING(KviOption_stringCtcpPageReply).isEmpty())
				KVI_OPTION_STRING(KviOption_stringCtcpPageReply) = KVI_DEFAULT_CTCP_PAGE_REPLY;

			replyCtcp(msg, KVI_OPTION_STRING(KviOption_stringCtcpPageReply));

			bool bIsChannel = !IS_ME(msg->msg, msg->szTarget);

			if((KVI_OPTION_BOOL(KviOption_boolShowDialogOnCtcpPage) && !bIsChannel) || (KVI_OPTION_BOOL(KviOption_boolShowDialogOnChannelCtcpPage) && bIsChannel))
			{
				if(!g_pCtcpPageDialog)
					g_pCtcpPageDialog = new KviCtcpPageDialog();
				KviCString szData8;
				szData8 = msg->pData;
				QString szData = KviQString::toHtmlEscaped(msg->msg->connection()->decodeText(szData8.ptr()));
				g_pCtcpPageDialog->addPage(msg->pSource->nick(), msg->pSource->user(), msg->pSource->host(), szData);
				g_pCtcpPageDialog->popup();
			}
		}
		else
			msg->bIgnored = true;
	}

	echoCtcpRequest(msg);
}

#ifdef COMPILE_CRYPT_SUPPORT
#define DECRYPT_IF_NEEDED(target, txt, type, type2, buffer, retptr, retmsgtype)        \
	if(KviCryptSessionInfo * cinf = target->cryptSessionInfo())                    \
	{                                                                              \
		if(cinf->m_bDoDecrypt)                                                 \
		{                                                                      \
			switch(cinf->m_pEngine->decrypt(txt, buffer))                  \
			{                                                              \
				case KviCryptEngine::DecryptOkWasEncrypted:            \
					retptr = buffer.ptr();                         \
					retmsgtype = type2;                            \
					break;                                         \
				case KviCryptEngine::DecryptOkWasPlainText:            \
				case KviCryptEngine::DecryptOkWasEncoded:              \
					retptr = buffer.ptr();                         \
					retmsgtype = type;                             \
					break;                                         \
				default: /* also case KviCryptEngine::DecryptError: */ \
				{                                                      \
					QString szEngineError = cinf->m_pEngine->lastError(); \
					target->output(KVI_OUT_SYSTEMERROR,            \
					    __tr2qs("The following message appears to be encrypted but the encryption engine failed to decode it: %Q"), \
					    &szEngineError);                           \
					retptr = txt + 1;                              \
					retmsgtype = type;                             \
				}                                                      \
				break;                                                 \
			}                                                              \
		}                                                                      \
		else                                                                   \
			retptr = txt, retmsgtype = type;                               \
	}                                                                              \
	else                                                                           \
		retptr = txt, retmsgtype = type;
#else //COMPILE_CRYPT_SUPPORT
#define DECRYPT_IF_NEEDED(target, txt, type, type2, buffer, retptr, retmsgtype) \
	retptr = txt;                                                           \
	retmsgtype = type;
#endif //COMPILE_CRYPT_SUPPORT

void KviIrcServerParser::parseCtcpRequestAction(KviCtcpMessage * msg)
{
	KviCString szData8(msg->pData);
	// CTCP ACTION is a special exception... most clients do not encode/decode it.
	//msg->pData = extractCtcpParameter(msg->pData,szData8,false);

	KviWindow * pOut = nullptr;
	bool bIsChannel = msg->msg->connection()->serverInfo()->supportedChannelTypes().indexOf(msg->szTarget[0]) != -1;
	int msgtype = KVI_OUT_ACTION;

	// "znc.in/self-message" capability: Handle a replayed message from ourselves to someone else.
	bool bSelfMessage = IS_ME(msg->msg, msg->pSource->nick());
	QString szTargetNick, szTargetUser, szTargetHost;
	msg->msg->decodeAndSplitMask(msg->szTarget.toLatin1().data(), szTargetNick, szTargetUser, szTargetHost);
	QString szWindow = bIsChannel || bSelfMessage ? szTargetNick : msg->pSource->nick();
	const QString &szOtherNick = bSelfMessage ? szTargetNick : msg->pSource->nick();
	const QString &szOtherUser = bSelfMessage ? szTargetUser : msg->pSource->user();
	const QString &szOtherHost = bSelfMessage ? szTargetHost : msg->pSource->host();

	QString szData;

	if(bIsChannel)
	{
		KviChannelWindow * chan = msg->msg->connection()->findChannel(msg->szTarget);
		if(chan)
		{
			KviCString szBuffer;
			const char * txtptr;

			DECRYPT_IF_NEEDED(chan, szData8.ptr(), KVI_OUT_ACTION, KVI_OUT_ACTIONCRYPTED, szBuffer, txtptr, msgtype)

			szData = chan->decodeText(txtptr);

			pOut = static_cast<KviWindow *>(chan);
		}
		else
			szData = msg->msg->connection()->decodeText(szData8.ptr());
	}
	else
	{
		KviQueryWindow * query = msg->msg->connection()->findQuery(szWindow);
		if(!query)
		{
			// New query requested ?
			// FIXME: 			#warning "CHECK FOR SPAM!"
			if(KVI_OPTION_BOOL(KviOption_boolCreateQueryOnPrivmsg))
			{
				// We still want to create it
				// Give the scripter a chance to filter it out again
				if(KVS_TRIGGER_EVENT_4_HALTED(KviEvent_OnQueryWindowRequest, msg->msg->console(),
				       msg->pSource->nick(),
				       msg->pSource->user(),
				       msg->pSource->host(),
				       szData))
				{
					// check if the scripter hasn't created it
					query = msg->msg->connection()->findQuery(szWindow);
				}
				else
				{
					// no query yet, create it!
					// this will trigger OnQueryWindowCreated
					query = msg->msg->console()->connection()->createQuery(szWindow);
					query->setTarget(szOtherNick, szOtherUser, szOtherHost);
				}
				if(!KVI_OPTION_STRING(KviOption_stringOnNewQueryOpenedSound).isEmpty())
				{
					KviKvsVariantList soundParams{new KviKvsVariant{KVI_OPTION_STRING(KviOption_stringOnNewQueryOpenedSound)}};
					KviKvsScript::run("snd.play $0", nullptr, &soundParams);
				}
			}
		}
		else
		{
			if(!KVI_OPTION_STRING(KviOption_stringOnQueryMessageSound).isEmpty() && !query->hasAttention())
			{
				KviKvsVariantList soundParams(new KviKvsVariant(KVI_OPTION_STRING(KviOption_stringOnQueryMessageSound)));
				// coverity is wrong here.
				KviKvsScript::run("snd.play $0", query, &soundParams);
			}
		}

		if(query)
		{
			KviCString szBuffer;
			const char * txtptr;

			DECRYPT_IF_NEEDED(query, szData8.ptr(), KVI_OUT_ACTION, KVI_OUT_ACTIONCRYPTED, szBuffer, txtptr, msgtype)

			szData = query->decodeText(txtptr);
		}
		else
			szData = msg->msg->connection()->decodeText(szData8.ptr());

		pOut = static_cast<KviWindow *>(query);
	}

	bool bTargetFound = pOut;
	if(!pOut)
	{
		pOut = KVI_OPTION_BOOL(KviOption_boolExternalMessagesToActiveWindow) ? msg->msg->console()->activeWindow() : msg->msg->console();
	}

	//see bug ticket #220
	if(KVI_OPTION_BOOL(KviOption_boolStripMircColorsInUserMessages))
		szData = KviControlCodes::stripControlBytes(szData);

	if(KVS_TRIGGER_EVENT_7_HALTED(KviEvent_OnAction, pOut,
	       msg->pSource->nick(),
	       msg->pSource->user(),
	       msg->pSource->host(),
	       msg->szTarget,
	       szData,
	       msg->msg->messageTagsKvsHash(),
	       (kvs_int_t)(msgtype == KVI_OUT_ACTIONCRYPTED)))
	{
		msg->msg->setHaltOutput();
		return;
	}

	int type = msg->msg->console()->applyHighlighting(pOut, msgtype, msg->pSource->nick(), msg->pSource->user(), msg->pSource->host(), szData);

	if(type < 0)
		return; // event stopped the message!

	if(type == KVI_OUT_HIGHLIGHT || !bIsChannel)
	{
		if(!pOut->hasAttention(KviWindow::MainWindowIsVisible))
		{
			if(KVI_OPTION_BOOL(KviOption_boolFlashWindowOnHighlightedMessages))
				pOut->demandAttention();
			if(KVI_OPTION_BOOL(KviOption_boolPopupNotifierOnHighlightedMessages))
			{
				QString szMsg = "<b>";
				szMsg += msg->pSource->nick();
				szMsg += "</b> ";
				szMsg += KviQString::toHtmlEscaped(szData);
				//qDebug("KviIrcServerParser_ctcp.cpp:975 debug: %s",szMsg.data());
				g_pApp->notifierMessage(pOut, KVI_OPTION_MSGTYPE(msgtype).pixId(), szMsg, KVI_OPTION_UINT(KviOption_uintNotifierAutoHideTime));
			}
		}
	}

	if(bTargetFound)
	{
		QString szMsg = QString("\r!n\r%1\r ").arg(msg->pSource->nick());
		szMsg += szData;
		if(bIsChannel)
		{
			((KviChannelWindow *)pOut)->outputMessage(type, szMsg, msg->msg->serverTime());
		}
		else
		{
			pOut->outputNoFmt(type, szMsg, 0, msg->msg->serverTime());
		}
	}
	else
	{
		if(bIsChannel)
		{
			pOut->output(KVI_OUT_SYSTEMWARNING,
			    __tr2qs("The following CTCP ACTION has unrecognized target %Q"),
			    &(msg->szTarget));
		}
		KviCString buffer1, buffer2;
		pOut->output(type,
		    __tr2qs("CTCP ACTION from \r!n\r%Q\r [%Q@\r!h\r%Q\r]: %Q"),
		    &(msg->pSource->nick()), &(msg->pSource->user()),
		    &(msg->pSource->host()), &szData);
	}
}

// FIXME: #warning "UTSNAME ?...AND OTHER INFO ?...SYSTEM IDLE TIME ?...KVIRC IDLE TIME ?"
void KviIrcServerParser::parseCtcpRequestAvatar(KviCtcpMessage * msg)
{
	// AVATAR
	if(!KVI_OPTION_BOOL(KviOption_boolIgnoreCtcpAvatar))
	{
		QString szGenderTag = " ";
		if(KVI_OPTION_STRING(KviOption_stringCtcpUserInfoGender).startsWith("m", Qt::CaseInsensitive))
		{
			szGenderTag.append("M");
		}
		else if(KVI_OPTION_STRING(KviOption_stringCtcpUserInfoGender).startsWith("f", Qt::CaseInsensitive))
		{
			szGenderTag.append("F");
		}
		else
		{
			szGenderTag.append("?");
		}

		KviAvatar * a = msg->msg->console()->currentAvatar();
		if(a)
		{
			if(!checkCtcpFlood(msg))
			{
				// FIXME: #warning "OPTION FOR SETTING A FIXED BIND ADDRESS FOR OUTGOING DCC OFFERS"
				QString szUserMask;
				msg->pSource->mask(szUserMask);

				QString szReply, szFileName;
				szFileName = a->name();
				if(KVI_OPTION_BOOL(KviOption_boolDCCFileTransferReplaceOutgoingSpacesWithUnderscores))
					szFileName.replace(" ", "_");

				// escape the spaces with the right octal code
				encodeCtcpParameter(szFileName.toUtf8().data(), szReply);

				if(!a->isRemote())
				{
					if(!g_pSharedFilesManager->addSharedFile(szFileName, a->localPath(), szUserMask, KVI_OPTION_UINT(KviOption_uintAvatarOfferTimeoutInSecs)))
					{
						msg->msg->console()->output(KVI_OUT_SYSTEMWARNING, __tr2qs("Unable to add file offer for file %Q (File not readable?)"), &(a->localPath()));
					}
					else
					{
						if(_OUTPUT_VERBOSE)
						{
							msg->msg->console()->output(KVI_OUT_SYSTEMMESSAGE, __tr2qs("Added %d sec file offer for file %Q (%Q) to recipient %Q"),
							    KVI_OPTION_UINT(KviOption_uintAvatarOfferTimeoutInSecs), &(a->name()), &(a->localPath()), &szUserMask);
						}
					}
				}

				szReply.append(szGenderTag);
				replyCtcp(msg, szReply);
			}
		}
		else
		{
			// no avatar set.. ignore channel requests if the user wishes
			if(!IS_ME(msg->msg, msg->szTarget))
			{
				// channel target
				if(KVI_OPTION_BOOL(KviOption_boolIgnoreChannelAvatarRequestsWhenNoAvatarSet))
					msg->bIgnored = true;
			}
			if(!msg->bIgnored)
				replyCtcp(msg, "");
		}
	}
	else
		msg->bIgnored = true;

	echoCtcpRequest(msg);
}

void KviIrcServerParser::parseCtcpReplyAvatar(KviCtcpMessage * msg)
{
	QString szRemoteFile;
	QString szGender;
	QString decoded = msg->msg->console()->decodeText(msg->pData);

	decoded = extractCtcpParameter(decoded.toUtf8().data(), szRemoteFile, true);
	decoded = extractCtcpParameter(decoded.toUtf8().data(), szGender, true);
	szRemoteFile = szRemoteFile.trimmed();

	bool bPrivate = IS_ME(msg->msg, msg->szTarget);

	QString textLine;
	KviAvatar * avatar = nullptr;

	bool bResetAvatar = true;

	QString nickLink = QString("\r!n\r%1\r").arg(msg->pSource->nick());

	KviIrcUserEntry * e = msg->msg->connection()->userDataBase()->find(msg->pSource->nick());
	if(e)
	{
		if((szGender == "m") || (szGender == "M"))
		{
			e->setGender(KviIrcUserEntry::Male);
		}
		else if((szGender == "f") || (szGender == "F"))
		{
			e->setGender(KviIrcUserEntry::Female);
		}
		else
		{
			e->setGender(KviIrcUserEntry::Unknown);
		}
	}

	QString szWhere = bPrivate ? __tr2qs("private") : __tr2qs("channel notification:");
	QString szWhat = bPrivate ? __tr2qs("notification") : msg->szTarget;

	if(szRemoteFile.isEmpty())
	{
		// avatar unset
		textLine = QString(__tr2qs("%1 unsets avatar")).arg(nickLink);
		if(_OUTPUT_VERBOSE)
			KviQString::appendFormatted(textLine, " (%Q %Q)", &szWhere, &szWhat);
	}
	else
	{

		// FIXME: #warning "The avatar should be the one with the requested size!!"
		textLine = QString(__tr2qs("%1 changes avatar to %2")).arg(nickLink, szRemoteFile);
		if(_OUTPUT_VERBOSE)
			KviQString::appendFormatted(textLine, " (%Q %Q)", &szWhere, &szWhat);

		bool bIsUrl = ((KviQString::equalCIN("http://", szRemoteFile, 7) && (szRemoteFile.length() > 7)) || (KviQString::equalCIN("https://", szRemoteFile, 8) && (szRemoteFile.length() > 8)));
		if(!bIsUrl)
		{
			// no hacks
			KviQString::cutToLast(szRemoteFile, '/');
			KviQString::cutToLast(szRemoteFile, '\\');
		}

		avatar = g_pIconManager->getAvatar(QString(), szRemoteFile);

		if((avatar == nullptr) && e)
		{
			// we have no such file on our HD....
			bResetAvatar = false;
			// request DCC GET ?
			if(KVI_OPTION_BOOL(KviOption_boolRequestMissingAvatars))
			{
				// FIXME: #warning "Request avatars only from registered users ?"
				// FIXME: #warning "Ask before making the request ?"
				if(bIsUrl)
				{
					QString szLocalFilePath;
					QString szLocalFile = szRemoteFile;
					g_pIconManager->urlToCachedFileName(szLocalFile);
					g_pApp->getLocalKvircDirectory(szLocalFilePath, KviApplication::Avatars, szLocalFile);
					KviQString::escapeKvs(&szLocalFilePath, KviQString::EscapeSpace);
					QString szCommand = "http.get -w=nm ";
					unsigned int uMaxSize = KVI_OPTION_UINT(KviOption_uintMaximumRequestedAvatarSize);
					if(uMaxSize > 0)
						KviQString::appendFormatted(szCommand, "-m=%u ", uMaxSize);
					szRemoteFile = szRemoteFile.replace(";", "%3B");
					szRemoteFile = szRemoteFile.replace("\"", "%22");
					szRemoteFile = szRemoteFile.replace("\\", "\\\\");
					szRemoteFile = szRemoteFile.replace("$", "\\$");
					szRemoteFile = szRemoteFile.replace("%", "\\%");
					szCommand += "\"" + szRemoteFile + "\"";
					szCommand += " ";
					szCommand += szLocalFilePath;

					if(KviKvsScript::run(szCommand, msg->msg->console()))
					{
						if(_OUTPUT_VERBOSE)
						{
							KviQString::appendFormatted(textLine,
							    __tr2qs(": No valid local copy of avatar available, requesting one (HTTP GET %s)"),
							    szRemoteFile.toUtf8().data());
						}
						g_pApp->setAvatarOnFileReceived(msg->msg->console(),
						    szRemoteFile, msg->pSource->nick(), msg->pSource->user(), msg->pSource->host());
					}
					else
					{
						if(_OUTPUT_VERBOSE)
							KviQString::appendFormatted(textLine, __tr2qs(": No valid local copy of avatar available; failed to start an HTTP transfer, ignoring"));
					}
				}
				else
				{
					if(!checkCtcpFlood(msg))
					{
						if(_OUTPUT_VERBOSE)
						{
							KviQString::appendFormatted(textLine,
							    __tr2qs(": No valid local copy of avatar available, requesting one (DCC GET %s)"),
							    szRemoteFile.toUtf8().data());
						}

						QString szFName;
						encodeCtcpParameter(szRemoteFile.toUtf8().data(), szFName);
						msg->msg->connection()->sendFmtData("PRIVMSG %s :%cDCC GET %s%c",
						    msg->msg->connection()->encodeText(msg->pSource->nick()).data(), 0x01, msg->msg->connection()->encodeText(szFName.toUtf8().data()).data(), 0x01);
						g_pApp->setAvatarOnFileReceived(msg->msg->console(),
						    szRemoteFile, msg->pSource->nick(), msg->pSource->user(), msg->pSource->host());
					}
					else
					{
						if(_OUTPUT_VERBOSE)
							KviQString::appendFormatted(textLine, __tr2qs(": No valid local copy of avatar available; flood limit exceeded, ignoring"));
					}
				}
			}
			else
			{
				if(_OUTPUT_VERBOSE)
					KviQString::appendFormatted(textLine, __tr2qs(": No valid local copy of avatar available, ignoring"));
			}
		}
	}

	if(!e)
	{
		if(_OUTPUT_VERBOSE)
			KviQString::appendFormatted(textLine, __tr2qs(": No such nickname in the user database, ignoring the change"));
		msg->msg->console()->outputNoFmt(KVI_OUT_AVATAR, textLine);
		return;
	}

	if(bResetAvatar)
		e->setAvatar(avatar);

	msg->msg->console()->avatarChanged(avatar, msg->pSource->nick(), msg->pSource->user(), msg->pSource->host(),
	    msg->msg->haltOutput() ? QString() : textLine);
}

using dccModuleCtcpDccParseRoutine = void (*)(KviDccRequest *);

void KviIrcServerParser::parseCtcpRequestDcc(KviCtcpMessage * msg)
{
	KviDccRequest p;
	KviCString aux = msg->pData;
	msg->pData = extractCtcpParameter(msg->pData, p.szType, true, true);
	msg->pData = extractCtcpParameter(msg->pData, p.szParam1);
	msg->pData = extractCtcpParameter(msg->pData, p.szParam2);
	msg->pData = extractCtcpParameter(msg->pData, p.szParam3);
	msg->pData = extractCtcpParameter(msg->pData, p.szParam4);
	msg->pData = extractCtcpParameter(msg->pData, p.szParam5);
	p.ctcpMsg = msg;
	p.bIPv6 = msg->msg->console()->isIPv6Connection();
	p.pConsole = msg->msg->console();

	KviRegisteredUser * u = msg->msg->connection()->userDataBase()->registeredUser(msg->pSource->nick(), msg->pSource->user(), msg->pSource->host());

	if(u)
	{
		if(u->isIgnoreEnabledFor(KviRegisteredUser::Dcc))
		{
			if(KVI_OPTION_BOOL(KviOption_boolVerboseIgnore))
			{
				msg->msg->console()->output(KVI_OUT_DCCREQUEST,
				    __tr2qs("Ignoring DCC %S request from \r!n\r%Q\r [%Q@\r!h\r%Q\r] (%Q %S)"),
				    &p.szType, &(msg->pSource->nick()),
				    &(msg->pSource->user()), &(msg->pSource->host()),
				    &msg->szTag, &aux);
			}
			return;
		}
	}

	bool bIsFlood = checkCtcpFlood(msg);

	if(bIsFlood && ((kvi_strEqualCI(p.szType.ptr(), "SEND")) || (kvi_strEqualCI(p.szType.ptr(), "RSEND")) || (kvi_strEqualCI(p.szType.ptr(), "TSEND")) || (kvi_strEqualCI(p.szType.ptr(), "TRSEND"))))
	{
		// don't consider as flood the avatars we have requested
		if(g_pApp->findPendingAvatarChange(msg->msg->console(), msg->pSource->nick(), p.szParam1.ptr()))
			bIsFlood = false;
	}

	if(!bIsFlood)
	{
		if(!msg->msg->haltOutput())
		{
			QString decoded = msg->msg->console()->decodeText(p.szType.ptr());
			msg->msg->console()->output(KVI_OUT_DCCREQUEST,
			    __tr2qs("Processing DCC %Q request from \r!n\r%Q\r [%Q@\r!h\r%Q\r] (%s %s)"),
			    &decoded, &(msg->pSource->nick()),
			    &(msg->pSource->user()), &(msg->pSource->host()),
			    msg->szTag.toUtf8().data(),
			    msg->msg->console()->decodeText(aux).toUtf8().data());
		}

		KviModule * m = g_pModuleManager->getModule("dcc");
		if(!m)
		{
			msg->msg->console()->output(KVI_OUT_DCCERROR,
			    __tr2qs("Unable to process the above request: can't load DCC module (%Q)"), &(g_pModuleManager->lastError()));
		}
		else
		{
			dccModuleCtcpDccParseRoutine proc = (dccModuleCtcpDccParseRoutine)m->getSymbol("dccModuleCtcpDccParseRoutine");
			if(!proc)
			{
				msg->msg->console()->outputNoFmt(KVI_OUT_DCCERROR,
				    __tr2qs("Unable to process the above request: DCC module may be broken"));
			}
			else
			{
				proc(&p);
			}
		}
	}
	else
	{
		// That's flood
		echoCtcpRequest(msg);
	}
}

void KviIrcServerParser::parseCtcpReplyUserinfo(KviCtcpMessage * msg)
{
	QString szRemoteFile;
	QString szGender;
	QString decoded = msg->msg->console()->decodeText(msg->pData);

	bool bNeedToUpdateUserlist = false;
	KviIrcUserEntry * e = msg->msg->connection()->userDataBase()->find(msg->pSource->nick());
	if(e)
	{
		int pos = decoded.indexOf("Gender=", 0, Qt::CaseInsensitive);

		if(pos >= 0)
		{
			QChar c = decoded[pos + 7];
			switch(c.unicode())
			{
				case 'F':
				case 'f':
					bNeedToUpdateUserlist = true;
					e->setGender(KviIrcUserEntry::Female);
					break;
				case 'M':
				case 'm':
					bNeedToUpdateUserlist = true;
					e->setGender(KviIrcUserEntry::Male);
					break;
			}
		}
	}

	if(bNeedToUpdateUserlist)
	{
		if(KviQString::equalCS(g_pActiveWindow->metaObject()->className(), QString("KviChannelWindow")))
		{
			((KviChannelWindow *)g_pActiveWindow)->userListView()->updateArea();
		}
	}

	echoCtcpReply(msg);
}

void KviIrcServerParser::parseCtcpReplyGeneric(KviCtcpMessage * msg)
{
	echoCtcpReply(msg);
}

void KviIrcServerParser::parseCtcpReplyLagcheck(KviCtcpMessage * msg)
{
	// this is an internal CTCP used for checking lag
	KviCString szTag;
	msg->pData = extractCtcpParameter(msg->pData, szTag, true);
	if(msg->msg->console()->connection()->lagMeter())
		msg->msg->console()->connection()->lagMeter()->lagCheckComplete(szTag.ptr());
}

//ERRORMSG,ECHO,ERRMSG
//SED,DCC,SOUND/MULTIMEDIA/MM,SCRIPT


/*
Copyright (c) 2015, Apple Inc. All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

1.  Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.

2.  Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.

3.  Neither the name of the copyright holder(s) nor the names of any contributors
may be used to endorse or promote products derived from this software without
specific prior written permission. No license is granted to the trademarks of
the copyright holders even if such marks are included in this software.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

".OnlyInDocument Scene" = ".OnlyInDocument Scene";

".OnlyInDocument Scene Summary" = ".OnlyInDocument Scene Summary";

"Additional text can go here." = "Additional text can go here.";

"Appleseed" = "Appleseed";

"Audio Active Task" = "Audio Active Task";

"Boolean Question" = "Boolean Question";

"Choice 1" = "Choice 1";

"Choice 2" = "Choice 2";

"Choice 3" = "Choice 3";

"Consent" = "Consent";

"Date and Time Question" = "Date and Time Question";

"Date Question" = "Date Question";

"Example Consent" = "Example Consent";

"Field01" = "Field01";

"Field02" = "Field02";

"Fitness Check Active Task" = "Fitness Check Active Task";

"Form" = "Form";

"I agree to participate in this research study." = "I agree to participate in this research study.";

"Image Choice Question" = "Image Choice Question";

"Institution" = "Institution";

"Institution and its partners" = "Institution and its partners";

"Investigator" = "Investigator";

"Jonny" = "Jonny";

"Numeric Question" = "Numeric Question";

"Participant" = "Participant";

"Placeholder without unit." = "Placeholder without unit.";

"Round Shape" = "Round Shape";

"Sample Survey" = "Sample Survey";

"Scale Question" = "Scale Question";

"Short Walk Active Task" = "Short Walk Active Task";

"Simple Survey" = "Simple Survey";

"Spatial Span Memory Active Task" = "Spatial Span Memory Active Task";

"Square Shape" = "Square Shape";

"Text Choice Question" = "Text Choice Question";

"Text Question" = "Text Question";

"Thank you for participating in this sample survey." = "Thank you for participating in this sample survey.";

"Thanks" = "Thanks";

"Time Interval Question" = "Time Interval Question";

"Time of Day Question" = "Time of Day Question";

"Two Finger Tapping Interval Active Task" = "Two Finger Tapping Interval Active Task";

"Value Picker Choice Question" = "Value Picker Choice Question";

"Would you like to subscribe to our newsletter?" = "Would you like to subscribe to our newsletter?";

"Your description goes here." = "Your description goes here.";

"Your more specific voice instruction goes here. For example, say 'Aaaah'." = "Your more specific voice instruction goes here. For example, say 'Aaaah'.";

"Your placeholder here" = "Your placeholder here";

"Your placeholder." = "Your placeholder.";

"Your question goes here." = "Your question here.";

"High Value" = "High Value";

"Low Value" = "Low Value";

"Your sharing learn more content here." = "Your sharing learn more content here.";

"Your unit" = "Your unit";


module github.com/prometheus/client_golang

require (
	github.com/beorn7/perks v1.0.1
	github.com/cespare/xxhash/v2 v2.1.1
	github.com/golang/protobuf v1.4.2
	github.com/json-iterator/go v1.1.10
	github.com/kr/pretty v0.1.0 // indirect
	github.com/prometheus/client_model v0.2.0
	github.com/prometheus/common v0.10.0
	github.com/prometheus/procfs v0.1.3
	github.com/stretchr/testify v1.4.0 // indirect
	golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1
	gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
	gopkg.in/yaml.v2 v2.2.5 // indirect
)

go 1.11


# WTUEKN - Tumblebugs 2

[Core]
# Values set here will override the main Dolphin settings.

[EmuState]
# The Emulation State. 1 is worst, 5 is best, 0 is not set.
EmulationIssues =
EmulationStateId = 4

[OnLoad]
# Add memory patches to be loaded once on boot here.

[OnFrame]
# Add memory patches to be applied every frame here.

[ActionReplay]
# Add action replay cheats here.

[Video_Settings]
UseXFB = True
UseRealXFB = False


.. _built-dist:

****************************
Creating Built Distributions
****************************

A "built distribution" is what you're probably used to thinking of either as a
"binary package" or an "installer" (depending on your background).  It's not
necessarily binary, though, because it might contain only Python source code
and/or byte-code; and we don't call it a package, because that word is already
spoken for in Python.  (And "installer" is a term specific to the world of
mainstream desktop systems.)

A built distribution is how you make life as easy as possible for installers of
your module distribution: for users of RPM-based Linux systems, it's a binary
RPM; for Windows users, it's an executable installer; for Debian-based Linux
users, it's a Debian package; and so forth.  Obviously, no one person will be
able to create built distributions for every platform under the sun, so the
Distutils are designed to enable module developers to concentrate on their
specialty---writing code and creating source distributions---while an
intermediary species called *packagers* springs up to turn source distributions
into built distributions for as many platforms as there are packagers.

Of course, the module developer could be his own packager; or the packager could
be a volunteer "out there" somewhere who has access to a platform which the
original developer does not; or it could be software periodically grabbing new
source distributions and turning them into built distributions for as many
platforms as the software has access to.  Regardless of who they are, a packager
uses the setup script and the :command:`bdist` command family to generate built
distributions.

As a simple example, if I run the following command in the Distutils source
tree::

   python setup.py bdist

then the Distutils builds my module distribution (the Distutils itself in this
case), does a "fake" installation (also in the :file:`build` directory), and
creates the default type of built distribution for my platform.  The default
format for built distributions is a "dumb" tar file on Unix, and a simple
executable installer on Windows.  (That tar file is considered "dumb" because it
has to be unpacked in a specific location to work.)

Thus, the above command on a Unix system creates
:file:`Distutils-1.0.{plat}.tar.gz`; unpacking this tarball from the right place
installs the Distutils just as though you had downloaded the source distribution
and run ``python setup.py install``.  (The "right place" is either the root of
the filesystem or  Python's :file:`{prefix}` directory, depending on the options
given to the :command:`bdist_dumb` command; the default is to make dumb
distributions relative to :file:`{prefix}`.)

Obviously, for pure Python distributions, this isn't any simpler than just
running ``python setup.py install``\ ---but for non-pure distributions, which
include extensions that would need to be compiled, it can mean the difference
between someone being able to use your extensions or not.  And creating "smart"
built distributions, such as an RPM package or an executable installer for
Windows, is far more convenient for users even if your distribution doesn't
include any extensions.

The :command:`bdist` command has a :option:`--formats` option, similar to the
:command:`sdist` command, which you can use to select the types of built
distribution to generate: for example, ::

   python setup.py bdist --format=zip

would, when run on a Unix system, create :file:`Distutils-1.0.{plat}.zip`\
---again, this archive would be unpacked from the root directory to install the
Distutils.

The available formats for built distributions are:

+-------------+------------------------------+---------+
| Format      | Description                  | Notes   |
+=============+==============================+=========+
| ``gztar``   | gzipped tar file             | (1),(3) |
|             | (:file:`.tar.gz`)            |         |
+-------------+------------------------------+---------+
| ``ztar``    | compressed tar file          | \(3)    |
|             | (:file:`.tar.Z`)             |         |
+-------------+------------------------------+---------+
| ``tar``     | tar file (:file:`.tar`)      | \(3)    |
+-------------+------------------------------+---------+
| ``zip``     | zip file (:file:`.zip`)      | (2),(4) |
+-------------+------------------------------+---------+
| ``rpm``     | RPM                          | \(5)    |
+-------------+------------------------------+---------+
| ``pkgtool`` | Solaris :program:`pkgtool`   |         |
+-------------+------------------------------+---------+
| ``sdux``    | HP-UX :program:`swinstall`   |         |
+-------------+------------------------------+---------+
| ``wininst`` | self-extracting ZIP file for | \(4)    |
|             | Windows                      |         |
+-------------+------------------------------+---------+
| ``msi``     | Microsoft Installer.         |         |
+-------------+------------------------------+---------+

Notes:

(1)
   default on Unix

(2)
   default on Windows

(3)
   requires external utilities: :program:`tar` and possibly one of :program:`gzip`,
   :program:`bzip2`, or :program:`compress`

(4)
   requires either external :program:`zip` utility or :mod:`zipfile` module (part
   of the standard Python library since Python 1.6)

(5)
   requires external :program:`rpm` utility, version 3.0.4 or better (use ``rpm
   --version`` to find out which version you have)

You don't have to use the :command:`bdist` command with the :option:`--formats`
option; you can also use the command that directly implements the format you're
interested in.  Some of these :command:`bdist` "sub-commands" actually generate
several similar formats; for instance, the :command:`bdist_dumb` command
generates all the "dumb" archive formats (``tar``, ``ztar``, ``gztar``, and
``zip``), and :command:`bdist_rpm` generates both binary and source RPMs.  The
:command:`bdist` sub-commands, and the formats generated by each, are:

+--------------------------+-----------------------+
| Command                  | Formats               |
+==========================+=======================+
| :command:`bdist_dumb`    | tar, ztar, gztar, zip |
+--------------------------+-----------------------+
| :command:`bdist_rpm`     | rpm, srpm             |
+--------------------------+-----------------------+
| :command:`bdist_wininst` | wininst               |
+--------------------------+-----------------------+
| :command:`bdist_msi`     | msi                   |
+--------------------------+-----------------------+

The following sections give details on the individual :command:`bdist_\*`
commands.

.. .. _creating-dumb:

.. Creating dumb built distributions
.. =================================

.. XXX Need to document absolute vs. prefix-relative packages here, but first
   I have to implement it!

.. _creating-rpms:

Creating RPM packages
=====================

The RPM format is used by many popular Linux distributions, including Red Hat,
SuSE, and Mandrake.  If one of these (or any of the other RPM-based Linux
distributions) is your usual environment, creating RPM packages for other users
of that same distribution is trivial. Depending on the complexity of your module
distribution and differences between Linux distributions, you may also be able
to create RPMs that work on different RPM-based distributions.

The usual way to create an RPM of your module distribution is to run the
:command:`bdist_rpm` command::

   python setup.py bdist_rpm

or the :command:`bdist` command with the :option:`--format` option::

   python setup.py bdist --formats=rpm

The former allows you to specify RPM-specific options; the latter allows  you to
easily specify multiple formats in one run.  If you need to do both, you can
explicitly specify multiple :command:`bdist_\*` commands and their options::

   python setup.py bdist_rpm --packager="John Doe <jdoe@example.org>" \
                   bdist_wininst --target-version="2.0"

Creating RPM packages is driven by a :file:`.spec` file, much as using the
Distutils is driven by the setup script.  To make your life easier, the
:command:`bdist_rpm` command normally creates a :file:`.spec` file based on the
information you supply in the setup script, on the command line, and in any
Distutils configuration files.  Various options and sections in the
:file:`.spec` file are derived from options in the setup script as follows:

+------------------------------------------+----------------------------------------------+
| RPM :file:`.spec` file option or section | Distutils setup script option                |
+==========================================+==============================================+
| Name                                     | :option:`name`                               |
+------------------------------------------+----------------------------------------------+
| Summary (in preamble)                    | :option:`description`                        |
+------------------------------------------+----------------------------------------------+
| Version                                  | :option:`version`                            |
+------------------------------------------+----------------------------------------------+
| Vendor                                   | :option:`author` and :option:`author_email`, |
|                                          | or  --- & :option:`maintainer` and           |
|                                          | :option:`maintainer_email`                   |
+------------------------------------------+----------------------------------------------+
| Copyright                                | :option:`license`                            |
+------------------------------------------+----------------------------------------------+
| Url                                      | :option:`url`                                |
+------------------------------------------+----------------------------------------------+
| %description (section)                   | :option:`long_description`                   |
+------------------------------------------+----------------------------------------------+

Additionally, there are many options in :file:`.spec` files that don't have
corresponding options in the setup script.  Most of these are handled through
options to the :command:`bdist_rpm` command as follows:

+-------------------------------+-----------------------------+-------------------------+
| RPM :file:`.spec` file option | :command:`bdist_rpm` option | default value           |
| or section                    |                             |                         |
+===============================+=============================+=========================+
| Release                       | :option:`release`           | "1"                     |
+-------------------------------+-----------------------------+-------------------------+
| Group                         | :option:`group`             | "Development/Libraries" |
+-------------------------------+-----------------------------+-------------------------+
| Vendor                        | :option:`vendor`            | (see above)             |
+-------------------------------+-----------------------------+-------------------------+
| Packager                      | :option:`packager`          | (none)                  |
+-------------------------------+-----------------------------+-------------------------+
| Provides                      | :option:`provides`          | (none)                  |
+-------------------------------+-----------------------------+-------------------------+
| Requires                      | :option:`requires`          | (none)                  |
+-------------------------------+-----------------------------+-------------------------+
| Conflicts                     | :option:`conflicts`         | (none)                  |
+-------------------------------+-----------------------------+-------------------------+
| Obsoletes                     | :option:`obsoletes`         | (none)                  |
+-------------------------------+-----------------------------+-------------------------+
| Distribution                  | :option:`distribution_name` | (none)                  |
+-------------------------------+-----------------------------+-------------------------+
| BuildRequires                 | :option:`build_requires`    | (none)                  |
+-------------------------------+-----------------------------+-------------------------+
| Icon                          | :option:`icon`              | (none)                  |
+-------------------------------+-----------------------------+-------------------------+

Obviously, supplying even a few of these options on the command-line would be
tedious and error-prone, so it's usually best to put them in the setup
configuration file, :file:`setup.cfg`\ ---see section :ref:`setup-config`.  If
you distribute or package many Python module distributions, you might want to
put options that apply to all of them in your personal Distutils configuration
file (:file:`~/.pydistutils.cfg`).

There are three steps to building a binary RPM package, all of which are
handled automatically by the Distutils:

#. create a :file:`.spec` file, which describes the package (analogous  to the
   Distutils setup script; in fact, much of the information in the  setup script
   winds up in the :file:`.spec` file)

#. create the source RPM

#. create the "binary" RPM (which may or may not contain binary code, depending
   on whether your module distribution contains Python extensions)

Normally, RPM bundles the last two steps together; when you use the Distutils,
all three steps are typically bundled together.

If you wish, you can separate these three steps.  You can use the
:option:`--spec-only` option to make :command:`bdist_rpm` just create the
:file:`.spec` file and exit; in this case, the :file:`.spec` file will be
written to the "distribution directory"---normally :file:`dist/`, but
customizable with the :option:`--dist-dir` option.  (Normally, the :file:`.spec`
file winds up deep in the "build tree," in a temporary directory created by
:command:`bdist_rpm`.)

.. % \XXX{this isn't implemented yet---is it needed?!}
.. % You can also specify a custom \file{.spec} file with the
.. % \longprogramopt{spec-file} option; used in conjunction with
.. % \longprogramopt{spec-only}, this gives you an opportunity to customize
.. % the \file{.spec} file manually:
.. %
.. % \ begin{verbatim}
.. % > python setup.py bdist_rpm --spec-only
.. % # ...edit dist/FooBar-1.0.spec
.. % > python setup.py bdist_rpm --spec-file=dist/FooBar-1.0.spec
.. % \ end{verbatim}
.. %
.. % (Although a better way to do this is probably to override the standard
.. % \command{bdist\_rpm} command with one that writes whatever else you want
.. % to the \file{.spec} file.)

.. _creating-wininst:

Creating Windows Installers
===========================

Executable installers are the natural format for binary distributions on
Windows.  They display a nice graphical user interface, display some information
about the module distribution to be installed taken from the metadata in the
setup script, let the user select a few options, and start or cancel the
installation.

Since the metadata is taken from the setup script, creating Windows installers
is usually as easy as running::

   python setup.py bdist_wininst

or the :command:`bdist` command with the :option:`--formats` option::

   python setup.py bdist --formats=wininst

If you have a pure module distribution (only containing pure Python modules and
packages), the resulting installer will be version independent and have a name
like :file:`foo-1.0.win32.exe`.  These installers can even be created on Unix
platforms or Mac OS X.

If you have a non-pure distribution, the extensions can only be created on a
Windows platform, and will be Python version dependent. The installer filename
will reflect this and now has the form :file:`foo-1.0.win32-py2.0.exe`.  You
have to create a separate installer for every Python version you want to
support.

The installer will try to compile pure modules into :term:`bytecode` after installation
on the target system in normal and optimizing mode.  If you don't want this to
happen for some reason, you can run the :command:`bdist_wininst` command with
the :option:`--no-target-compile` and/or the :option:`--no-target-optimize`
option.

By default the installer will display the cool "Python Powered" logo when it is
run, but you can also supply your own 152x261 bitmap which must be a Windows
:file:`.bmp` file with the :option:`--bitmap` option.

The installer will also display a large title on the desktop background window
when it is run, which is constructed from the name of your distribution and the
version number.  This can be changed to another text by using the
:option:`--title` option.

The installer file will be written to the "distribution directory" --- normally
:file:`dist/`, but customizable with the :option:`--dist-dir` option.

.. _cross-compile-windows:

Cross-compiling on Windows
==========================

Starting with Python 2.6, distutils is capable of cross-compiling between
Windows platforms.  In practice, this means that with the correct tools
installed, you can use a 32bit version of Windows to create 64bit extensions
and vice-versa.

To build for an alternate platform, specify the :option:`--plat-name` option
to the build command.  Valid values are currently 'win32', 'win-amd64' and
'win-ia64'.  For example, on a 32bit version of Windows, you could execute::

   python setup.py build --plat-name=win-amd64

to build a 64bit version of your extension.  The Windows Installers also
support this option, so the command::

   python setup.py build --plat-name=win-amd64 bdist_wininst

would create a 64bit installation executable on your 32bit version of Windows.

To cross-compile, you must download the Python source code and cross-compile
Python itself for the platform you are targetting - it is not possible from a
binary installation of Python (as the .lib etc file for other platforms are
not included.)  In practice, this means the user of a 32 bit operating
system will need to use Visual Studio 2008 to open the
:file:`PCBuild/PCbuild.sln` solution in the Python source tree and build the
"x64" configuration of the 'pythoncore' project before cross-compiling
extensions is possible.

Note that by default, Visual Studio 2008 does not install 64bit compilers or
tools.  You may need to reexecute the Visual Studio setup process and select
these tools (using Control Panel->[Add/Remove] Programs is a convenient way to
check or modify your existing install.)

.. _postinstallation-script:

The Postinstallation script
---------------------------

Starting with Python 2.3, a postinstallation script can be specified with the
:option:`--install-script` option.  The basename of the script must be
specified, and the script filename must also be listed in the scripts argument
to the setup function.

This script will be run at installation time on the target system after all the
files have been copied, with ``argv[1]`` set to :option:`-install`, and again at
uninstallation time before the files are removed with ``argv[1]`` set to
:option:`-remove`.

The installation script runs embedded in the windows installer, every output
(``sys.stdout``, ``sys.stderr``) is redirected into a buffer and will be
displayed in the GUI after the script has finished.

Some functions especially useful in this context are available as additional
built-in functions in the installation script.

.. function:: directory_created(path)
              file_created(path)

   These functions should be called when a directory or file is created by the
   postinstall script at installation time.  It will register *path* with the
   uninstaller, so that it will be removed when the distribution is uninstalled.
   To be safe, directories are only removed if they are empty.

.. function:: get_special_folder_path(csidl_string)

   This function can be used to retrieve special folder locations on Windows like
   the Start Menu or the Desktop.  It returns the full path to the folder.
   *csidl_string* must be one of the following strings::

      "CSIDL_APPDATA"

      "CSIDL_COMMON_STARTMENU"
      "CSIDL_STARTMENU"

      "CSIDL_COMMON_DESKTOPDIRECTORY"
      "CSIDL_DESKTOPDIRECTORY"

      "CSIDL_COMMON_STARTUP"
      "CSIDL_STARTUP"

      "CSIDL_COMMON_PROGRAMS"
      "CSIDL_PROGRAMS"

      "CSIDL_FONTS"

   If the folder cannot be retrieved, :exc:`OSError` is raised.

   Which folders are available depends on the exact Windows version, and probably
   also the configuration.  For details refer to Microsoft's documentation of the
   :c:func:`SHGetSpecialFolderPath` function.

.. function:: create_shortcut(target, description, filename[, arguments[, workdir[, iconpath[, iconindex]]]])

   This function creates a shortcut. *target* is the path to the program to be
   started by the shortcut. *description* is the description of the shortcut.
   *filename* is the title of the shortcut that the user will see. *arguments*
   specifies the command line arguments, if any. *workdir* is the working directory
   for the program. *iconpath* is the file containing the icon for the shortcut,
   and *iconindex* is the index of the icon in the file *iconpath*.  Again, for
   details consult the Microsoft documentation for the :class:`IShellLink`
   interface.

Vista User Access Control (UAC)
===============================

Starting with Python 2.6, bdist_wininst supports a :option:`--user-access-control`
option.  The default is 'none' (meaning no UAC handling is done), and other
valid values are 'auto' (meaning prompt for UAC elevation if Python was
installed for all users) and 'force' (meaning always prompt for elevation).


Source: xcat-openstack-baremetal
Section: admin
Priority: extra
Maintainer: xCAT <xcat-user@lists.sourceforge.net>
Build-Depends: debhelper (>= 9)
Standards-Version: 3.9.4
Homepage: https://xcat.org/

Package: xcat-openstack-baremetal
Architecture: all
Depends: xcat-client
Description: Executables and data of xCAT baremetal driver for OpenStack


using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;

namespace RPCC.Data
{
    public class ApplicationDbContext : IdentityDbContext
    {
        public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
            : base(options)
        {
        }
    }
}


<?php
require "include/bittorrent.php";
dbconn();
require_once(get_langfile_path());

stdhead(PROJECTNAME);
print ("
".PROJECTNAME."
");
begin_main_frame();
begin_frame("".$lang_aboutnexus['text_version']."");
print ($lang_aboutnexus['text_version_note']);
print ("

");
tr($lang_aboutnexus['text_main_version'],$mainversion_code,1);
tr($lang_aboutnexus['text_sub_version'],$subversion_code,1);
tr($lang_aboutnexus['text_release_date'],$releasedate_code,1);
print ("
");
print ("

");
end_frame();
begin_frame("".$lang_aboutnexus['text_nexus'].PROJECTNAME."");
print (PROJECTNAME.$lang_aboutnexus['text_nexus_note']);
print ("

");
end_frame();
begin_frame("".$lang_aboutnexus['text_authorization']."");
print ($lang_aboutnexus['text_authorization_note']);
print ("

");
end_frame();
unset($ppl);
$res = sql_query("SELECT * FROM language ORDER BY trans_state") or sqlerr();
while ($arr = mysql_fetch_assoc($res))
{
	$ppl .= "

 ".$arr['lang_name']."	".
 "".$arr['trans_state']."
\n";
}
begin_frame("".$lang_aboutnexus['text_translation']."");
print (PROJECTNAME.$lang_aboutnexus['text_translation_note']);
print ("

".$lang_aboutnexus['text_flag']."	".$lang_aboutnexus['text_language']."	".$lang_aboutnexus['text_state']."
");
print ($ppl);
print ("
");
print ("

");
end_frame();
unset($ppl);
$res = sql_query("SELECT * FROM stylesheets ORDER BY id") or sqlerr();
while ($arr = mysql_fetch_assoc($res))
{
	$ppl .= "
".$arr['name']."
 ".$arr['designer']."	".
 "".$arr['comment']."
\n";
}
begin_frame("".$lang_aboutnexus['text_stylesheet']."");
print ($lang_aboutnexus['text_stylesheet_note']);
print ("

".$lang_aboutnexus['text_name']."	".$lang_aboutnexus['text_designer']."	".$lang_aboutnexus['text_comment']."
");
print ($ppl);
print ("
");
print ("

");
end_frame();
begin_frame("".$lang_aboutnexus['text_contact'].PROJECTNAME."");
print ($lang_aboutnexus['text_contact_note']);
print ("

");
tr($lang_aboutnexus['text_web_site'],$website_code ? $website_code : "N/A",1);
print ("
");
print ("

");
end_frame();
end_main_frame();
stdfoot();
?>


// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace Microsoft.IIS.Administration.WebServer
{
    using Certificates;
    using System.Collections.Generic;
    using System.Security.Cryptography.X509Certificates;

    class CertStore : ICertificateStoreConfiguration
    {
        public string Name { get; set; }
        public string Path { get; set; }
        public IEnumerable<string> Claims { get; set; }

        public StoreLocation StoreLocation {
            get {
                return StoreLocation.LocalMachine;
            }
        }
    }
}


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>CFBundleDevelopmentRegion</key>
	<string>en</string>
	<key>CFBundleExecutable</key>
	<string>$(EXECUTABLE_NAME)</string>
	<key>CFBundleIdentifier</key>
	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
	<key>CFBundleInfoDictionaryVersion</key>
	<string>6.0</string>
	<key>CFBundleName</key>
	<string>$(PRODUCT_NAME)</string>
	<key>CFBundlePackageType</key>
	<string>FMWK</string>
	<key>CFBundleShortVersionString</key>
	<string>1.0</string>
	<key>CFBundleVersion</key>
	<string>$(CURRENT_PROJECT_VERSION)</string>
	<key>NSHumanReadableCopyright</key>
	<string>Copyright © 2016年 ShinCurry. All rights reserved.</string>
	<key>NSPrincipalClass</key>
	<string></string>
</dict>
</plist>


//
// Copyright (C) 2019 Assured Information Security, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

// TIDY_EXCLUSION=-cppcoreguidelines-pro-type-reinterpret-cast
//
// Reason:
//     Although in general this is a good rule, for hypervisor level code that
//     interfaces with the kernel, and raw hardware, this rule is
//     impractical.
//

#include <hve/arch/intel_x64/vcpu.h>
#include <hve/arch/intel_x64/exit_handler.h>

// -----------------------------------------------------------------------------
// Handlers
// -----------------------------------------------------------------------------

namespace bfvmm::intel_x64
{

// -----------------------------------------------------------------------------
// Implementation
// -----------------------------------------------------------------------------

exit_handler::exit_handler(
    gsl::not_null<vcpu *> vcpu)
{ bfignored(vcpu); }

void
exit_handler::add_handler(
    ::intel_x64::vmcs::value_type reason,
    const handler_delegate_t &d)
{ m_exit_handlers_array.at(reason).push_front(d); }

void
exit_handler::add_exit_handler(
    const handler_delegate_t &d)
{ m_exit_handlers.push_front(d); }

}

extern "C"  void
handle_exit(
    vcpu_t *vcpu, exit_handler_t *exit_handler)
{
    guard_exceptions([&]() {

        for (const auto &d : exit_handler->m_exit_handlers) {
            d(vcpu);
        }

        const auto &handlers =
            exit_handler->m_exit_handlers_array.at(
                vmcs_n::exit_reason::basic_exit_reason::get()
            );

        for (const auto &d : handlers) {
            if (d(vcpu)) {
                vcpu->run();
            }
        }
    });

    vcpu->halt("unhandled vm exit");
}