/*
 * This file is a part of VyQChat.
 *
 * Copyright (C) 2002-2004 Pawel Stolowski <yogin@linux.bydg.org>
 *
 * VyQChat is free software; you can redestribute it and/or modify it
 * under terms of GNU General Public License by Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY. See GPL for more details.
 */

#include "vcprotocol.h"
#include "packetdata.h"
#include "uuid.h"
#include <qsocketnotifier.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <time.h>
#include <iostream>

//
// this macro should be used with PacketData operator<<
#define PROTOVERSION VY_PROTOMINOR << 0 << VY_PROTOMAJOR << 0
#define VC_PLATFORM "Linux"
#define VC_SOFTWARE "VyQChat"

#define	VY_WHOHERE '0'
#define	VY_IMHERE  '1'
#define	VY_SAY  '2'
#define	VY_NICKCHANGE '3'
#define	VY_JOIN   '4'
#define	VY_LEAVE  '5'
#define	VY_MESSAGE  '6'
#define	VY_MASSMESSAGE 'E'
#define	VY_MESSAGEACK  '7'
#define	VY_REMOTEEXEC  '8'
#define	VY_EXECACK  '9'
#define	VY_ME  'A'
#define	VY_NEWTOPIC  'B'
#define	VY_CURRTOPIC  'C'
#define	VY_CHANGESTAT  'D'
#define	VY_INFOREQ  'F'
#define	VY_INFOREQACK  'G'
#define	VY_BEEP  'H'
#define VY_BEEP_SIGNAL '0'
#define VY_BEEP_ACK '1'
#define	VY_SOUNDREQ  'I'
#define	VY_PRIVATE  'J'
#define	VY_PRIVATE_JOIN  '0'
#define	VY_PRIVATE_LEAVE  '1'
#define	VY_PRIVATE_SAY  '2'
#define	VY_PRIVATE_ME  '3'
#define	VY_HEREREQ  'L'
#define	VY_HEREACK  'K'
#define	VY_CHANGEACTIV  'M'
#define	VY_REQCHANNELS  'N'
#define	VY_REQCHANNELSACK  'O'
#define VY_PING 'P'
#define VY_PINGREQ '0'
#define VY_PINGACK '1'
#define VY_FLOODNOTIFY 'Z'

VCProtocol::VCProtocol(QTextCodec *c, const UUID &uuid, bool useutf): ansicodec(c), wnotifier(NULL), rnotifier(NULL) /*{{{*/
{
	this->uuid = uuid;
	utfcodec = QTextCodec::codecForName("UTF-8");
	setUseUTF(useutf);
	srandom(time(NULL));
}/*}}}*/

VCProtocol::~VCProtocol()/*{{{*/
{
	closeSockets();
}/*}}}*/

void VCProtocol::setAnsiCodec(QTextCodec *c)/*{{{*/
{
	ansicodec = c;
}/*}}}*/

void VCProtocol::setUseUTF(bool f)/*{{{*/
{
	sndcodec = ((useutf = f)) ? utfcodec : ansicodec;
}/*}}}*/

bool VCProtocol::setupBroadcastSockets(const QHostAddress &ip, const QHostAddress &broadcast, unsigned int port)/*{{{*/
{
	sockaddr_in saddr;
	int flags;
	int opt = 1;
	
	/*if ((sendsd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
	{
		perror("socket()");
		return false;
	}*/
			
	if ((recvsd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
	{
		perror("socket()");
		return false;
	}
	
	if ((setsockopt(recvsd, SOL_SOCKET, SO_BROADCAST, &opt, sizeof(int)) < 0) || (setsockopt(recvsd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int)) < 0))
	{
		perror("setsockopt()");
		return false;
	}

	//setsockopt(recvsd, SOL_SOCKET, SO_NO_CHECK, &opt, sizeof(int));

	//
	// set sockets non-blocking
	/*if ((flags = fcntl(sendsd, F_GETFL, 0)) < 0)
	{
		perror("fcntl()");
		return false;
	}
	flags |= O_NONBLOCK;
	if (fcntl(sendsd, F_SETFL, flags) < 0)
		return false;*/
	if ((flags = fcntl(recvsd, F_GETFL, 0)) < 0)
		return false;
	flags |= O_NONBLOCK;
	if (fcntl(recvsd, F_SETFL, flags) < 0)
		return false;

	//
	// bind receiving socket
	bzero(&saddr, sizeof(saddr));
	saddr.sin_family = AF_INET;
	saddr.sin_addr.s_addr = htonl(INADDR_ANY); //htonl(ip.toIPv4Address());
	saddr.sin_port = htons(port);
	if (bind(recvsd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0)
	{
		std::cout << ip.toString() << std::endl;
		perror("1 bind()");
		return false;
	}

	this->broadcast = broadcast;
	this->port = port;

	rnotifier = new QSocketNotifier(recvsd, QSocketNotifier::Read, this);
	connect(rnotifier, SIGNAL(activated(int)), this, SLOT(dataReceived()));
	
	return true;
}/*}}}*/

bool VCProtocol::setupMulticastSockets(const QHostAddress &ip, const QHostAddress &multicastgroup, unsigned char ttl, unsigned int port)/*{{{*/
{
	struct sockaddr_in saddr;
	int flags;
	int opt = 1;
	struct ip_mreq mcast;
	
	/*if ((sendsd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
	{
		perror("socket()");
		return false;
	}*/
			
	if ((recvsd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
	{
		perror("socket()");
		return false;
	}
	
	if (/*(setsockopt(sendsd, SOL_SOCKET, SO_BROADCAST, &opt, sizeof(int)) < 0) ||*/
			(setsockopt(recvsd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl))<0) || (setsockopt(recvsd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int)) < 0))
	{
		perror("setsockopt()");
		return false;
	}

	//setsockopt(recvsd, SOL_SOCKET, SO_NO_CHECK, &opt, sizeof(int));

	//
	// set multicast group
	mcast.imr_multiaddr.s_addr = htonl(multicastgroup.toIPv4Address());
	mcast.imr_interface.s_addr = htonl(ip.toIPv4Address());
	if (setsockopt(recvsd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mcast, sizeof(mcast)) < 0)
	{
		perror("can't set multicast group");
		return false;
	}

	//
	// set sockets non-blocking
	/*if ((flags = fcntl(sendsd, F_GETFL, 0)) < 0)
	{
		perror("fcntl()");
		return false;
	}
	flags |= O_NONBLOCK;
	if (fcntl(sendsd, F_SETFL, flags) < 0)
		return false;*/
	if ((flags = fcntl(recvsd, F_GETFL, 0)) < 0)
		return false;
	flags |= O_NONBLOCK;
	if (fcntl(recvsd, F_SETFL, flags) < 0)
		return false;

	//
	// bind receiving socket
	bzero(&saddr, sizeof(saddr));
	saddr.sin_family = AF_INET;
	saddr.sin_addr.s_addr = htonl(INADDR_ANY); //htonl(add.toIPv4Address());
	saddr.sin_port = htons(port);
	if (bind(recvsd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0)
	{
		std::cout << ip.toString() << std::endl;
		perror("1 bind()");
		return false;
	}
	
	this->broadcast = multicastgroup;
	this->port = port;

	rnotifier = new QSocketNotifier(recvsd, QSocketNotifier::Read, this);
	connect(rnotifier, SIGNAL(activated(int)), this, SLOT(dataReceived()));

	return true;
}/*}}}*/

void VCProtocol::closeSockets()/*{{{*/
{
	//close(sendsd);
	close(recvsd);
	if (rnotifier)
		delete rnotifier;
	if (wnotifier)
		delete wnotifier;
}/*}}}*/

void VCProtocol::dataReceived()/*{{{*/
{
	QHostAddress source;
	sockaddr_in saddr;
	socklen_t slen = sizeof(struct sockaddr_in);

	char buffer[VY_MAXPACKETSIZE];
	int len = recvfrom(recvsd, buffer, VY_MAXPACKETSIZE, 0, (struct sockaddr *)&saddr, &slen);
	source.setAddress(ntohl(saddr.sin_addr.s_addr));
	std::cout << source.toString() << std::endl;
	decode(buffer, len, source);
}/*}}}*/

QTextCodec *VCProtocol::setUserCodecUTF(const QHostAddress &addr, char f)/*{{{*/
{
	return setUserCodecUTF(addr, f == '1');
}/*}}}*/

QTextCodec *VCProtocol::setUserCodecUTF(const QHostAddress &addr, bool f)/*{{{*/
{
	 return (usercodec[addr.toIPv4Address()] = f) ? utfcodec : ansicodec;
}/*}}}*/

QTextCodec *VCProtocol::codecForUser(const QHostAddress &addr)/*{{{*/
{
	const Q_INT32 a = addr.toIPv4Address();
	if (!usercodec.contains(a))
		return ansicodec;
	return (usercodec[a]) ? utfcodec : ansicodec;
}/*}}}*/

bool VCProtocol::get(QCString *str, char *buffer, int &pos, const int p_len)/*{{{*/
{
	const int spos = pos;
	for (;pos<p_len; pos++)
	{
		if (buffer[pos] == 0)
		{
			*str = QCString(buffer + spos);
			++pos;
			return true;
		}
	}
	return false;
}/*}}}*/

bool VCProtocol::get(QString *str, char *buffer, int &pos, const int p_len, QTextCodec *tc)/*{{{*/
{
	const int spos = pos;
	for (;pos<p_len; pos++)
	{
		if (buffer[pos] == 0)
		{
			*str = tc->toUnicode(reinterpret_cast<char *>(buffer + spos));
			++pos;
			return true;
		}
	}
	return false;
}/*}}}*/

bool VCProtocol::get(char *c, char *buffer, int &pos, const int p_len)/*{{{*/
{
	if (pos < p_len)
	{
		*c = buffer[pos++];
		return true;
	}
	return false;
}/*}}}*/

bool VCProtocol::get(UUID *uuid, char *buffer, int &pos, const int p_len)/*{{{*/
{
	pos += UUID_LEN;
	if (pos < p_len)
	{
		uuid->set(buffer);
		return true;
	}
	return false;
}/*}}}*/

bool VCProtocol::get(Q_INT16 *s, char *buffer, int &pos, const int p_len)/*{{{*/
{
	if ((pos + sizeof(Q_INT16)) < p_len)
	{
		*s = buffer[pos++]<<8;
		*s |= buffer[pos++];
		return true;
	}
	return false;
}/*}}}*/

bool VCProtocol::get(Q_INT32 *s, char *buffer, int &pos, const int p_len)/*{{{*/
{
	if ((pos + sizeof(Q_INT32)) < p_len)
	{
		*s = buffer[pos++]<<24;
		*s |= buffer[pos++]<<16;
		*s |= buffer[pos++]<<8;
		*s |= buffer[pos++];
		return true;
	}
	return false;
}/*}}}*/

bool VCProtocol::decode(char *p_data, int p_len, const QHostAddress &source)/*{{{*/
{
	if (*p_data != 'X') //check if it is vypress chat packet
		return false;

	const char *p_id = p_data;  //packet signature start
	p_data += 10; 		    //skip packet id
	char b = *(p_data++);

	std::cout << b << std::endl;
	
	QString from, to, channel, text, host, computer, ip, name, channels, software, time;
	char status, gender, codepage, ractive, c;
	UUID uuid;
	Q_INT32 version, color;
	int pos = 0;
	
	QTextCodec *codec = codecForUser(source);
	
	switch (b)
	{
		/* WHO HERE
	 	* updater */
		case VY_WHOHERE:
			if (!get(&from, p_data, pos, p_len, codec))
				return false;
			emit sig_vc_whohere(source, from);
			break;
		/* IM HERE
		 * updater :: responder :: status + remote active :: (version + gender + uuid :: 0000 + cp + color)*/
		case VY_IMHERE:
			get(&to, p_data, pos, p_len, codec);
			get(&from, p_data, pos, p_len, codec);
			get(&status, p_data, pos, p_len);
			if (!get(&ractive, p_data, pos, p_len))
				return false;
			get(&c, p_data, pos, p_len); //zero byte
			if (get(&version, p_data, pos, p_len)) //vc 1.9.x
			{
				Q_INT16 tmp;
				get(&gender, p_data, pos, p_len);
				get(&uuid, p_data, pos, p_len);
				get(&c, p_data, pos, p_len); //zero byte
				get(&tmp, p_data, pos, p_len); //reserverd dword
				get(&tmp, p_data, pos, p_len); //...
				get(&codepage, p_data, pos, p_len);
				get(&color, p_data, pos, p_len);
				setUserCodecUTF(source, codepage);
				emit sig_vc_imhere(source, to, from, status, ractive, gender, color);
			}
			else
				emit sig_vc_imhere(source, to, from, status, ractive);
			break;
		/* SIMPLE CHAT
		 * channel :: author :: text */
		case VY_SAY: 
			get(&channel, p_data, pos, p_len, codec);
			get(&from, p_data, pos, p_len, codec);
			if (get(&text, p_data, pos, p_len, codec))
				emit sig_vc_say(channel, from, text);
			break;
		/* ME
		 * channel :: author :: text */
		case VY_ME:
			get(&channel, p_data, pos, p_len, codec);
			get(&from, p_data, pos, p_len, codec);
			if (!get(&text, p_data, pos, p_len, codec))
				return false;
			emit sig_vc_me(channel, from, text);
			break;
		/* NICK CHANGE
		 * old :: new :: gender */
		case VY_NICKCHANGE: 
			get(&from, p_data, pos, p_len, codec);
			get(&to, p_data, pos, p_len, codec);
			if (!get(&c, p_data, pos, p_len))
				return false;
			emit sig_vc_nickchange(from, to , gender);
			break;
		/* BEEP
		*/
		case VY_BEEP:
			  b = *(p_data++);
			  switch (b)
			  {
				  /* BEEP SIGNAL
				   * to :: from  */
				  case VY_BEEP_SIGNAL: 
					  get(&to, p_data, pos, p_len, codec);
					  if (!get(&from, p_data, pos, p_len, codec))
						  return false;
					  emit sig_vc_beep(source, to, from);
					  break;
				  /* BEEP ACK
				   * to :: from :: gender */
				  case VY_BEEP_ACK:
					  get(&to, p_data, pos, p_len, codec);
					  get(&from, p_data, pos, p_len, codec);
					  if (!get(&gender, p_data, pos, p_len))
						  return false;
					  emit sig_vc_beepack(to, from, gender);
					  break;
				  default: break;
			  }
			  break;
		/* JOIN
		 * who :: channel :: status + gender */
		case VY_JOIN: {
			  QCString a_from, a_channel; //raw ansi strings, we don't know codec yet
			  get(&a_from, p_data, pos, p_len);
			  get(&a_channel, p_data, pos, p_len);
			  get(&status, p_data, pos, p_len);
			  if (!get(&gender, p_data, pos, p_len))
				  return false;
			  if (get(&c, p_data, pos, p_len)) //succeeds for vypresschat >= 1.9.x
			  {
				 if (channel == MAIN_CHANNEL)
				 {
					 get(&version, p_data, pos, p_len);
					 get(&uuid, p_data, pos, p_len);
					 get(&codepage, p_data, pos, p_len);
					 if (!get(&color, p_data, pos, p_len))
						 return false;
				 }
				 else
				 {
					 if (!get(&codepage, p_data, pos, p_len))
						 return false;
				 }
				 codec = setUserCodecUTF(source, codepage);
			  }
			  emit sig_vc_join(source, codec->toUnicode(a_from), codec->toUnicode(a_channel), status, gender);
			  break;
			}
		/* LEAVE
		 * who :: channel :: gender */
		case VY_LEAVE:
			  get(&from, p_data, pos, p_len, codec);
			  get(&channel, p_data, pos, p_len, codec);
			  if (!get(&gender, p_data, pos, p_len))
				  return false;
			  emit sig_vc_leave(from, channel, gender);
			  break;	  
		/* PRIVATE */
		case VY_PRIVATE:
			  b = *(p_data++);
			  get(&from, p_data, pos, p_len, codec);
			  get(&to, p_data, pos, p_len, codec);
			  switch (b)
			  {
				  /* PRIVATE JOIN, PRIVATE_LEAVE
				   * from :: to :: gender */
				  case VY_PRIVATE_JOIN:	
						if (!get(&gender, p_data, pos, p_len))
							return false;
						emit sig_vc_private_join(from, to, gender);
						break;
				  case VY_PRIVATE_LEAVE:
						if (!get(&gender, p_data, pos, p_len))
							return false;
						emit sig_vc_private_leave(from, to, gender);
						break;
				  /* PRIVATE_SAY
				   * from :: to :: text */
				  case VY_PRIVATE_SAY:
						if (!get(&text, p_data, pos, p_len, codec))
							return false;
						emit sig_vc_private_say(from, to, text);
						break;
				  /* PRIVATE ME
				   * from :: to :: text */
				  case VY_PRIVATE_ME:  
						if (!get(&text, p_data, pos, p_len, codec))
							return false;
						emit sig_vc_private_me(from, to, text);
						break;
				  default: break;
			  }
			  break;
		/* MESSAGE
		 * from :: to :: text */
		case VY_MESSAGE: {
			  QCString sig;
			  get(&from, p_data, pos, p_len, codec);
			  get(&to, p_data, pos, p_len, codec);
			  if (!get(&text, p_data, pos, p_len, codec))
				  return false;
			  sig.duplicate(p_id, 10);
			  emit sig_vc_message(source, sig, from, to, text);
			  break;
			}
		/* MESSAGEACK
		 * status + whorec :: towhoack :: gender + currentaa :: (sig)*/	  
		case VY_MESSAGEACK: {
			  QCString sig;
			  get(&status, p_data, pos, p_len);
			  get(&from, p_data, pos, p_len, codec);
			  get(&to, p_data, pos, p_len, codec);
			  get(&gender, p_data, pos, p_len);
			  get(&text, p_data, pos, p_len, codec);
			  if (get(&sig, p_data, pos, p_len))
				emit sig_vc_messageack(status, from, to, gender, text);
			  else
			  	emit sig_vc_messageack(sig, status, from, to, gender, text);
			  break;
			 }
		/* MASSMESSAGE
		 * from :: to :: text */
		case VY_MASSMESSAGE: {
			  QCString sig;
			  get(&from, p_data, pos, p_len, codec);
			  get(&to, p_data, pos, p_len, codec);
			  if (!get(&text, p_data, pos, p_len, codec))
				  return false;
			  sig.duplicate(p_id, 10);
			  emit sig_vc_massmessage(source, sig, from, to, text); 
			  break;
			}
		/* HERE REQUEST
		 * from :: channel */
		case VY_HEREREQ:
			  get(&from, p_data, pos, p_len, codec);
			  if (!get(&channel, p_data, pos, p_len, codec))
				  return false;
			  emit sig_vc_herereq(source, from, channel);
			  break;
		/* HERE ACK 
		 * to :: channel :: from :: remote active */
		case VY_HEREACK:
			  get(&to, p_data, pos, p_len, codec);
			  get(&channel, p_data, pos, p_len, codec);
			  get(&from, p_data, pos, p_len, codec);
			  if (!get(&ractive, p_data, pos, p_len))
				  return false;
			  emit sig_vc_hereack(to, channel, from, ractive);
			  break;
		/* PING / PONG 
		 * to :: from :: time */
		case VY_PING:
			  b = *(p_data++);
			  get(&to, p_data, pos, p_len, codec);
			  get(&from, p_data, pos, p_len, codec);
			  get(&time, p_data, pos, p_len, codec);
			  if (b == VY_PINGREQ)
				  emit sig_vc_ping(source, to, from, time);
			  else if (b == VY_PINGACK)
				  emit sig_vc_pong(to, from, time);
			  break;
		/* INFO REQUEST
		 * to :: from */
		case VY_INFOREQ: 
			  get(&to, p_data, pos, p_len, codec);
			  if (!get(&from, p_data, pos, p_len, codec))
				  return false;
			  emit sig_vc_inforeq(to, from);
			  break;
		/* INFO REQUEST ACK
		 * to :: from :: host :: name :: ip :: channels :: answer :: (platform :: soft )*/
		case VY_INFOREQACK:  
			  get(&to, p_data, pos, p_len, codec);
			  get(&from, p_data, pos, p_len, codec);
			  get(&host, p_data, pos, p_len, codec);
			  get(&name, p_data, pos, p_len, codec);
			  get(&ip, p_data, pos, p_len, codec);
			  get(&channels, p_data, pos, p_len, codec);
			  if (!get(&text, p_data, pos, p_len, codec))
				  return false;
			  // for vypress chat >= 1.9.x
			  get(&computer, p_data, pos, p_len, codec);
			  get(&software, p_data, pos, p_len, codec);
			  emit sig_vc_inforeqack(source, to, from, host, name, ip, channels, text, computer, software, codecForUser(source) == utfcodec);
			  break;
		/* CURRENT TOPIC
		 * towho :: channel :: topic */
		case VY_CURRTOPIC: 
			  get(&to, p_data, pos, p_len, codec);
			  get(&channel, p_data, pos, p_len, codec);
			  if (!get(&text, p_data, pos, p_len, codec))
				  return false;
			  emit sig_vc_currenttopic(to, channel, text);
			  break;
		/* NEW TOPIC
		 * channel :: topic */
		case VY_NEWTOPIC:
			  get(&channel, p_data, pos, p_len, codec);
			  if (!get(&text, p_data, pos, p_len, codec))
				  return false;
			  emit sig_vc_newtopic(channel, text);
			  break;
		/* CHANGE STATUS
		 * who :: status + gender + aa */
		case VY_CHANGESTAT:
			  get(&from, p_data, pos, p_len, codec);
			  get(&status, p_data, pos, p_len);
			  get(&gender, p_data, pos, p_len);
			  if (!get(&text, p_data, pos, p_len, codec))
				  return false;
			  emit sig_vc_changestatus(from, status, gender, text);
			  break;
		/* FLOOD NOTIFY
		 * to :: from :: seconds */
		case VY_FLOODNOTIFY:
			  get(&to, p_data, pos, p_len, codec);
			  get(&from, p_data, pos, p_len, codec);
			  if (!get(&text, p_data, pos, p_len, codec))
				  return false;
			  emit sig_vc_floodnotify(to, from, text);
		default:
			break;
	}
	return true;
}/*}}}*/

void VCProtocol::send(const QHostAddress &addr, PacketData *packet)/*{{{*/
{
	if (packet->isOk())
	{
		sockaddr_in saddr;
		
		bzero(&saddr, sizeof(saddr));
		saddr.sin_family = AF_INET;
		saddr.sin_addr.s_addr = htonl(addr.toIPv4Address());
		saddr.sin_port = htons(port);

		int s = sendto(recvsd, packet->dataPtr(), packet->dataLength(), 0, (sockaddr *)&saddr, sizeof(saddr));
		if (s<0)
			perror("send(): ");
		//else
		//	std::cout << "=" << s << std::endl;
	}
}/*}}}*/

void VCProtocol::vc_say(const QString &channel, const QString &nick, const QString &text)/*{{{*/
{
	PacketData p;
	p << VY_SAY << sndcodec->fromUnicode(channel) << 0 << sndcodec->fromUnicode(nick) << 0 << sndcodec->fromUnicode(text) << 0;
	send(broadcast, &p);
}/*}}}*/

void VCProtocol::vc_me(const QString &channel, const QString &nick, const QString &text)/*{{{*/
{
	PacketData p;
	p << VY_ME << sndcodec->fromUnicode(channel) << 0 << sndcodec->fromUnicode(nick) << 0 << sndcodec->fromUnicode(text) << 0;
	send(broadcast, &p);
}/*}}}*/

void VCProtocol::vc_whohere(const QString &updater)/*{{{*/
{
	PacketData p;
	p << VY_WHOHERE << sndcodec->fromUnicode(updater) << 0 << (useutf ? '1' : '0') << 0;
	send(broadcast, &p);
}/*}}}*/

void VCProtocol::vc_imhere(const QHostAddress &addr, const QString &updater, const QString &responder, char status, char gender, char winstate)/*{{{*/
{
	PacketData p;
	p << VY_IMHERE << sndcodec->fromUnicode(updater) << 0 << sndcodec->fromUnicode(responder) << 0 << status << winstate << 0 <<
		PROTOVERSION << gender << uuid << 0 
		<< 0 << 0 << 0 << 0 //reserved dword
		<< (useutf ? '1' : '0') 
		<< 0 << 0 << 0 << 0 //color
		<< 0;
	send(addr, &p);
}/*}}}*/

void VCProtocol::vc_beep(const QHostAddress &addr, const QString& to, const QString& from)/*{{{*/
{
	PacketData p;
	p << VY_BEEP << VY_BEEP_SIGNAL << sndcodec->fromUnicode(to) << 0 << sndcodec->fromUnicode(from) << 0;
	send(addr, &p);
}/*}}}*/

void VCProtocol::vc_beepack(const QHostAddress &addr, const QString& to, const QString& from, char gender)/*{{{*/
{
	PacketData p;
	p << VY_BEEP << VY_BEEP_ACK << sndcodec->fromUnicode(from) << 0 << sndcodec->fromUnicode(to) << 0 << gender;
	send(addr, &p);
}/*}}}*/

void VCProtocol::vc_changeactivity(const QString &who, char winstate)/*{{{*/
{
	PacketData p;
	p << VY_CHANGEACTIV << sndcodec->fromUnicode(who) << 0 << winstate;
	send(broadcast, &p);
}/*}}}*/

void VCProtocol::vc_join(const QString &who, const QString &channel, char status, char gender)/*{{{*/
{
	PacketData p;
	if (channel == MAIN_CHANNEL)
		p << VY_JOIN << sndcodec->fromUnicode(who) << 0 << sndcodec->fromUnicode(channel) << 0 << status << gender << 0 <<
			PROTOVERSION << uuid << (useutf ? '1' : '0') 
			<< 0xff << 0xff << 0xff << 0xff //color
			<< 0;
	else
		p << VY_JOIN << sndcodec->fromUnicode(who) << 0 << sndcodec->fromUnicode(channel) << 0 << status << gender << 0 
			<< (useutf ? '1' : '0') << 0;
	send(broadcast, &p);
}/*}}}*/

void VCProtocol::vc_leave(const QString &who, const QString &channel, char gender)/*{{{*/
{
	PacketData p;
	p << VY_LEAVE << sndcodec->fromUnicode(who) << 0 << sndcodec->fromUnicode(channel) << 0 << gender;
	send(broadcast, &p);
}/*}}}*/

void VCProtocol::vc_private_join(const QHostAddress &addr, const QString &from, const QString &to, char gender)/*{{{*/
{
	PacketData p;
	p << VY_PRIVATE << VY_PRIVATE_JOIN << sndcodec->fromUnicode(from) << 0 << sndcodec->fromUnicode(to) << 0 << gender;
	send(addr, &p);
}/*}}}*/

void VCProtocol::vc_private_leave(const QHostAddress &addr, const QString &from, const QString &to, char gender)/*{{{*/
{
	PacketData p;
	p << VY_PRIVATE << VY_PRIVATE_LEAVE << sndcodec->fromUnicode(from) << 0 << sndcodec->fromUnicode(to) << 0 << gender;
	send(addr, &p);
}/*}}}*/

void VCProtocol::vc_private_say(const QHostAddress &addr, const QString &from, const QString &to, const QString &text)/*{{{*/
{
	PacketData p;
	p << VY_PRIVATE << VY_PRIVATE_SAY << sndcodec->fromUnicode(from) << 0 << sndcodec->fromUnicode(to) << 0 << sndcodec->fromUnicode(text) << 0;
	send(addr, &p);
}/*}}}*/

void VCProtocol::vc_private_me(const QHostAddress &addr, const QString &from, const QString &to, const QString &text)/*{{{*/
{
	PacketData p;
	p << VY_PRIVATE << VY_PRIVATE_ME << sndcodec->fromUnicode(from) << 0 << sndcodec->fromUnicode(to) << 0 << sndcodec->fromUnicode(text) << 0;
	send(addr, &p);
}/*}}}*/

void VCProtocol::vc_message(const QHostAddress &addr, const QString &from, const QString &to, const QString &text)/*{{{*/
{
	PacketData p;
	p << VY_MESSAGE << sndcodec->fromUnicode(from) << 0 << sndcodec->fromUnicode(to) << 0 << sndcodec->fromUnicode(text) << 0;
	send(addr, &p);
}/*}}}*/

void VCProtocol::vc_massmessage(const QHostAddress &addr, const QString &from, const QString &to, const QString &text)/*{{{*/
{
	PacketData p;
	p << VY_MASSMESSAGE << sndcodec->fromUnicode(from) << 0 << sndcodec->fromUnicode(to) << 0 << sndcodec->fromUnicode(text) << 0;
	send(addr, &p);
}/*}}}*/

void VCProtocol::vc_messageack(const QHostAddress &addr, const QByteArray &sig, char status, const QString &from, const QString &to, char gender, const QString &answer)/*{{{*/
{
	PacketData p;
	p << VY_MESSAGEACK << status << sndcodec->fromUnicode(from) << 0 << sndcodec->fromUnicode(to) << 0 
		<< gender << sndcodec->fromUnicode(answer) << 0 << sig << 0;
	send(addr, &p);
}/*}}}*/

void VCProtocol::vc_herereq(const QString &from, const QString &channel)/*{{{*/
{
	PacketData p;
	p << VY_HEREREQ << sndcodec->fromUnicode(from) << 0 << sndcodec->fromUnicode(channel) << 0;
	send(broadcast, &p);
}/*}}}*/

void VCProtocol::vc_hereack(const QHostAddress &addr, const QString &to, const QString &channel, const QString &from, char ra)/*{{{*/
{
	PacketData p;
	p << VY_HEREACK << sndcodec->fromUnicode(to) << 0 << sndcodec->fromUnicode(channel) << 0 << sndcodec->fromUnicode(from) << 0 << ra;
	send(addr, &p);
}/*}}}*/

void VCProtocol::vc_inforeq(const QHostAddress &addr, const QString &to, const QString &from)/*{{{*/
{
	PacketData p;
	p << VY_INFOREQ << sndcodec->fromUnicode(to) << 0 << sndcodec->fromUnicode(from) << 0;
	send(addr, &p);
}/*}}}*/

void VCProtocol::vc_inforeqack(const QHostAddress &addr, const QString &to, const QString &from, const QString &comp, const QString &name, const QString &ip, const QString &channels, const QString &answer)/*{{{*/
{
	PacketData p;
	p << VY_INFOREQACK << sndcodec->fromUnicode(to) << 0 << sndcodec->fromUnicode(from) << 0 << sndcodec->fromUnicode(comp) << 0 <<
			sndcodec->fromUnicode(name) << 0 << sndcodec->fromUnicode(ip) << 0 << sndcodec->fromUnicode(channels) << 0 <<
			sndcodec->fromUnicode(answer) << 0 << sndcodec->fromUnicode(VC_PLATFORM) << 0 << sndcodec->fromUnicode(VC_SOFTWARE) << 0;
	send(addr, &p);
}/*}}}*/

void VCProtocol::vc_nickchange(const QString &from, const QString &to, char gender)/*{{{*/
{
	PacketData p;
	p << VY_NICKCHANGE << sndcodec->fromUnicode(from) << 0 << sndcodec->fromUnicode(to) << 0 << gender;
	send(broadcast, &p);
}/*}}}*/

void VCProtocol::vc_changestatus(const QString &who, char status, char gender, const QString &answer)/*{{{*/
{
	PacketData p;
	p << VY_CHANGESTAT << sndcodec->fromUnicode(who) << 0 << status << gender << sndcodec->fromUnicode(answer) << 0;
	send(broadcast, &p);
}/*}}}*/

void VCProtocol::vc_currenttopic(const QHostAddress &addr, const QString &to, const QString &channel, const QString &topic)/*{{{*/
{
	PacketData p;
	p << VY_CURRTOPIC << sndcodec->fromUnicode(to) << 0 << sndcodec->fromUnicode(channel) << 0 << sndcodec->fromUnicode(topic) << 0;
	send(addr, &p);
}/*}}}*/

void VCProtocol::vc_newtopic(const QString &channel, const QString &topic, const QString &who)/*{{{*/
{
	PacketData p;
	p << VY_NEWTOPIC << sndcodec->fromUnicode(channel) << 0 << sndcodec->fromUnicode(topic) << " (" << sndcodec->fromUnicode(who) << ')' << 0;
	send(broadcast, &p);
}/*}}}*/

void VCProtocol::vc_ping(const QHostAddress &addr, const QString &to, const QString &from, const QString &time)/*{{{*/
{
	PacketData p;
	p << VY_PING << VY_PINGREQ << sndcodec->fromUnicode(to) << 0 << sndcodec->fromUnicode(from) << 0 << sndcodec->fromUnicode(time) << 0;
	send(addr, &p);
}/*}}}*/

void VCProtocol::vc_pong(const QHostAddress &addr, const QString &to, const QString &from, const QString &time)/*{{{*/
{
	PacketData p;
	p << VY_PING << VY_PINGACK << sndcodec->fromUnicode(to) << 0 << sndcodec->fromUnicode(from) << 0 << sndcodec->fromUnicode(time) << 0;
	send(addr, &p);
}/*}}}*/

void VCProtocol::vc_floodnotify(const QHostAddress &addr, const QString &to, const QString &from, int seconds)/*{{{*/
{
	PacketData p;
	p << VY_FLOODNOTIFY << sndcodec->fromUnicode(to) << 0 << sndcodec->fromUnicode(from) << sndcodec->fromUnicode(QString::number(seconds)) << 0;
	send(addr, &p);
}/*}}}*/



syntax highlighted by Code2HTML, v. 0.9.1