/*
IRC plugin for Miranda IM

Copyright (C) 2003-05 Jurgen Persson
Copyright (C) 2007-09 George Hazan

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/

#include "stdafx.h"
#include "version.h"

#define NICKSUBSTITUTE L"!_nick_!"

void CIrcProto::FormatMsg(CMStringW& text)
{
	wchar_t temp[30];
	mir_wstrncpy(temp, GetWord(text.c_str(), 0).c_str(), 29);
	CharLower(temp);
	CMStringW command = temp;
	CMStringW S = L"";
	if (command == L"/quit" || command == L"/away")
		S = GetWord(text.c_str(), 0) + L" :" + GetWordAddress(text.c_str(), 1);
	else if (command == L"/privmsg" || command == L"/part" || command == L"/topic" || command == L"/notice") {
		S = GetWord(text.c_str(), 0) + L" " + GetWord(text.c_str(), 1) + L" :";
		if (!GetWord(text.c_str(), 2).IsEmpty())
			S += CMStringW(GetWordAddress(text.c_str(), 2));
	}
	else if (command == L"/kick") {
		S = GetWord(text.c_str(), 0) + L" " + GetWord(text.c_str(), 1) + L" " + GetWord(text.c_str(), 2) + L" :" + GetWordAddress(text.c_str(), 3);
	}
	else if (command == L"/nick") {
		if (!wcsstr(GetWord(text.c_str(), 1).c_str(), NICKSUBSTITUTE)) {
			sNick4Perform = GetWord(text.c_str(), 1);
			S = GetWordAddress(text.c_str(), 0);
		}
		else {
			CMStringW sNewNick = GetWord(text.c_str(), 1);
			if (sNick4Perform == L"") {
				DBVARIANT dbv;
				if (!getWString("PNick", &dbv)) {
					sNick4Perform = dbv.ptszVal;
					db_free(&dbv);
				}
			}

			sNewNick.Replace(NICKSUBSTITUTE, sNick4Perform.c_str());
			S = GetWord(text.c_str(), 0) + L" " + sNewNick;
		}
	}
	else S = GetWordAddress(text.c_str(), 0);

	S.Delete(0, 1);
	text = S;
}

static void AddCR(CMStringW& text)
{
	text.Replace(L"\n", L"\r\n");
	text.Replace(L"\r\r", L"\r");
}

CMStringW CIrcProto::DoAlias(const wchar_t *text, wchar_t *window)
{
	CMStringW Messageout = L"";
	const wchar_t* p1 = text;
	const wchar_t* p2 = text;
	bool LinebreakFlag = false, hasAlias = false;
	p2 = wcsstr(p1, L"\r\n");
	if (!p2)
		p2 = wcschr(p1, 0);
	if (p1 == p2)
		return (CMStringW)text;

	do {
		if (LinebreakFlag)
			Messageout += L"\r\n";

		wchar_t* line = new wchar_t[p2 - p1 + 1];
		mir_wstrncpy(line, p1, p2 - p1 + 1);
		wchar_t* test = line;
		while (*test == ' ')
			test++;
		if (*test == '/') {
			mir_wstrncpy(line, GetWordAddress(line, 0), p2 - p1 + 1);
			CMStringW S = line;
			delete[] line;
			line = new wchar_t[S.GetLength() + 2];
			mir_wstrncpy(line, S.c_str(), S.GetLength() + 1);
			CMStringW alias(m_alias);
			const wchar_t* p3 = wcsstr(alias.c_str(), (GetWord(line, 0) + L" ").c_str());
			if (p3 != alias.c_str()) {
				CMStringW str = L"\r\n";
				str += GetWord(line, 0) + L" ";
				p3 = wcsstr(alias.c_str(), str.c_str());
				if (p3)
					p3 += 2;
			}
			if (p3 != nullptr) {
				hasAlias = true;
				const wchar_t* p4 = wcsstr(p3, L"\r\n");
				if (!p4)
					p4 = wcschr(p3, 0);

				*(wchar_t*)p4 = 0;
				CMStringW str = p3;
				str.Replace(L"##", window);
				str.Replace(L"$?", L"%question");

				wchar_t buf[5];
				for (int index = 1; index < 8; index++) {
					mir_snwprintf(buf, L"#$%u", index);
					if (!GetWord(line, index).IsEmpty() && IsChannel(GetWord(line, index)))
						str.Replace(buf, GetWord(line, index).c_str());
					else {
						CMStringW S1 = L"#";
						S1 += GetWord(line, index);
						str.Replace(buf, S1.c_str());
					}
				}
				for (int index2 = 1; index2 < 8; index2++) {
					mir_snwprintf(buf, L"$%u-", index2);
					str.Replace(buf, GetWordAddress(line, index2));
				}
				for (int index3 = 1; index3 < 8; index3++) {
					mir_snwprintf(buf, L"$%u", index3);
					str.Replace(buf, GetWord(line, index3).c_str());
				}
				Messageout += GetWordAddress(str.c_str(), 1);
			}
			else Messageout += line;
		}
		else Messageout += line;

		p1 = p2;
		if (*p1 == '\r')
			p1 += 2;
		p2 = wcsstr(p1, L"\r\n");
		if (!p2)
			p2 = wcschr(p1, 0);
		delete[] line;
		LinebreakFlag = true;
	}
	while (*p1 != 0);

	return hasAlias ? DoIdentifiers(Messageout, window) : Messageout;
}

CMStringW CIrcProto::DoIdentifiers(CMStringW text, const wchar_t*)
{
	SYSTEMTIME time;
	wchar_t str[2];

	GetLocalTime(&time);
	text.Replace(L"%mnick", m_nick);
	text.Replace(L"%anick", m_alternativeNick);
	text.Replace(L"%awaymsg", m_statusMessage.c_str());
	text.Replace(L"%module", _A2T(m_szModuleName));
	text.Replace(L"%name", m_name);
	text.Replace(L"%newl", L"\r\n");
	text.Replace(L"%network", m_info.sNetwork.c_str());
	text.Replace(L"%me", m_info.sNick.c_str());

	char mirver[100];
	Miranda_GetVersionText(mirver, _countof(mirver));
	text.Replace(L"%mirver", _A2T(mirver));
	text.Replace(L"%version", _A2T(__VERSION_STRING_DOTS));

	str[0] = 3; str[1] = 0;
	text.Replace(L"%color", str);

	str[0] = 2;
	text.Replace(L"%bold", str);

	str[0] = 31;
	text.Replace(L"%underline", str);

	str[0] = 22;
	text.Replace(L"%italics", str);
	return text;
}

static void __stdcall sttSetTimerOn(void* _pro)
{
	CIrcProto *ppro = (CIrcProto*)_pro;
	ppro->DoEvent(GC_EVENT_INFORMATION, nullptr, ppro->m_info.sNick.c_str(), TranslateT("The buddy check function is enabled"), nullptr, nullptr, NULL, true, false);
	ppro->SetChatTimer(ppro->OnlineNotifTimer, 500, OnlineNotifTimerProc);
	if (ppro->m_channelAwayNotification)
		ppro->SetChatTimer(ppro->OnlineNotifTimer3, 1500, OnlineNotifTimerProc3);
}

static void __stdcall sttSetTimerOff(void* _pro)
{
	CIrcProto *ppro = (CIrcProto*)_pro;
	ppro->DoEvent(GC_EVENT_INFORMATION, nullptr, ppro->m_info.sNick.c_str(), TranslateT("The buddy check function is disabled"), nullptr, nullptr, NULL, true, false);
	ppro->KillChatTimer(ppro->OnlineNotifTimer);
	ppro->KillChatTimer(ppro->OnlineNotifTimer3);
}

BOOL CIrcProto::DoHardcodedCommand(CMStringW text, wchar_t *window, MCONTACT hContact)
{
	CMStringW command(GetWord(text, 0)); command.MakeLower();
	CMStringW one = GetWord(text, 1);
	CMStringW two = GetWord(text, 2);
	CMStringW three = GetWord(text, 3);
	CMStringW therest = GetWordAddress(text, 4);

	if (command == L"/servershow" || command == L"/serverhide") {
		if (m_useServer)
			Chat_Control(m_szModuleName, SERVERWINDOW, command == L"/servershow" ? WINDOW_VISIBLE : WINDOW_HIDDEN);

		return true;
	}

	else if (command == L"/sleep" || command == L"/wait") {
		if (!one.IsEmpty()) {
			int ms;
			if (swscanf(one.c_str(), L"%d", &ms) == 1 && ms > 0 && ms <= 4000)
				Sleep(ms);
			else
				DoEvent(GC_EVENT_INFORMATION, nullptr, m_info.sNick.c_str(), TranslateT("Incorrect parameters. Usage: /sleep [ms], ms should be greater than 0 and less than 4000."), nullptr, nullptr, NULL, true, false);
		}
		return true;
	}

	if (command == L"/clear") {
		CMStringW S;
		if (!one.IsEmpty()) {
			if (one == L"server")
				S = SERVERWINDOW;
			else
				S = MakeWndID(one.c_str());
		}
		else if (mir_wstrcmpi(window, SERVERWINDOW) == 0)
			S = window;
		else
			S = MakeWndID(window);

		Chat_Control(m_szModuleName, S, WINDOW_CLEARLOG);
		return true;
	}

	if (command == L"/ignore") {
		if (IsConnected()) {
			CMStringW IgnoreFlags;
			wchar_t temp[500];
			if (one.IsEmpty()) {
				if (m_ignore)
					DoEvent(GC_EVENT_INFORMATION, nullptr, m_info.sNick.c_str(), TranslateT("Ignore system is enabled"), nullptr, nullptr, NULL, true, false);
				else
					DoEvent(GC_EVENT_INFORMATION, nullptr, m_info.sNick.c_str(), TranslateT("Ignore system is disabled"), nullptr, nullptr, NULL, true, false);
				return true;
			}
			if (!mir_wstrcmpi(one.c_str(), L"on")) {
				m_ignore = 1;
				DoEvent(GC_EVENT_INFORMATION, nullptr, m_info.sNick.c_str(), TranslateT("Ignore system is enabled"), nullptr, nullptr, NULL, true, false);
				return true;
			}
			if (!mir_wstrcmpi(one.c_str(), L"off")) {
				m_ignore = 0;
				DoEvent(GC_EVENT_INFORMATION, nullptr, m_info.sNick.c_str(), TranslateT("Ignore system is disabled"), nullptr, nullptr, NULL, true, false);
				return true;
			}
			if (!wcschr(one.c_str(), '!') && !wcschr(one.c_str(), '@'))
				one += L"!*@*";

			if (!two.IsEmpty() && two[0] == '+') {
				if (wcschr(two.c_str(), 'q'))
					IgnoreFlags += 'q';
				if (wcschr(two.c_str(), 'n'))
					IgnoreFlags += 'n';
				if (wcschr(two.c_str(), 'i'))
					IgnoreFlags += 'i';
				if (wcschr(two.c_str(), 'd'))
					IgnoreFlags += 'd';
				if (wcschr(two.c_str(), 'c'))
					IgnoreFlags += 'c';
				if (wcschr(two.c_str(), 'm'))
					IgnoreFlags += 'm';
			}
			else IgnoreFlags = L"qnidc";

			CMStringW szNetwork;
			if (three.IsEmpty())
				szNetwork = m_info.sNetwork;
			else
				szNetwork = three;

			AddIgnore(one.c_str(), IgnoreFlags.c_str(), szNetwork.c_str());

			mir_snwprintf(temp, TranslateT("%s on %s is now ignored (+%s)"), one.c_str(), szNetwork.c_str(), IgnoreFlags.c_str());
			DoEvent(GC_EVENT_INFORMATION, nullptr, m_info.sNick.c_str(), temp, nullptr, nullptr, NULL, true, false);
		}
		return true;
	}

	if (command == L"/unignore") {
		if (!wcschr(one.c_str(), '!') && !wcschr(one.c_str(), '@'))
			one += L"!*@*";

		wchar_t temp[500];
		if (RemoveIgnore(one.c_str()))
			mir_snwprintf(temp, TranslateT("%s is not ignored now"), one.c_str());
		else
			mir_snwprintf(temp, TranslateT("%s was not ignored"), one.c_str());
		DoEvent(GC_EVENT_INFORMATION, nullptr, m_info.sNick.c_str(), temp, nullptr, nullptr, NULL, true, false);
		return true;
	}

	if (command == L"/userhost") {
		if (one.IsEmpty())
			return true;

		DoUserhostWithReason(1, L"U", false, command);
		return false;
	}

	if (command == L"/joinx") {
		if (!one.IsEmpty()) {
			for (int i = 1;; i++) {
				CMStringW tmp = GetWord(text, i);
				if (tmp.IsEmpty())
					break;

				AddToJTemp('X', tmp);
			}

			PostIrcMessage(L"/JOIN %s", GetWordAddress(text, 1));
		}
		return true;
	}

	if (command == L"/joinm") {
		if (!one.IsEmpty()) {
			for (int i = 1;; i++) {
				CMStringW tmp = GetWord(text, i);
				if (tmp.IsEmpty())
					break;

				AddToJTemp('M', tmp);
			}

			PostIrcMessage(L"/JOIN %s", GetWordAddress(text, 1));
		}
		return true;
	}

	if (command == L"/nusers") {
		wchar_t szTemp[40];
		CMStringW S = MakeWndID(window);
		GC_INFO gci = { 0 };
		gci.Flags = GCF_BYID | GCF_NAME | GCF_COUNT;
		gci.pszModule = m_szModuleName;
		gci.pszID = S.c_str();
		if (!Chat_GetInfo(&gci))
			mir_snwprintf(szTemp, L"users: %u", gci.iCount);

		DoEvent(GC_EVENT_INFORMATION, nullptr, m_info.sNick.c_str(), szTemp, nullptr, nullptr, NULL, true, false);
		return true;
	}

	if (command == L"/echo") {
		if (one.IsEmpty())
			return true;

		if (!mir_wstrcmpi(one.c_str(), L"on")) {
			bEcho = TRUE;
			DoEvent(GC_EVENT_INFORMATION, nullptr, m_info.sNick.c_str(), TranslateT("Outgoing commands are shown"), nullptr, nullptr, NULL, true, false);
		}

		if (!mir_wstrcmpi(one.c_str(), L"off")) {
			DoEvent(GC_EVENT_INFORMATION, nullptr, m_info.sNick.c_str(), TranslateT("Outgoing commands are not shown"), nullptr, nullptr, NULL, true, false);
			bEcho = FALSE;
		}

		return true;
	}

	if (command == L"/buddycheck") {
		if (one.IsEmpty()) {
			if ((m_autoOnlineNotification && !bTempDisableCheck) || bTempForceCheck)
				DoEvent(GC_EVENT_INFORMATION, nullptr, m_info.sNick.c_str(), TranslateT("The buddy check function is enabled"), nullptr, nullptr, NULL, true, false);
			else
				DoEvent(GC_EVENT_INFORMATION, nullptr, m_info.sNick.c_str(), TranslateT("The buddy check function is disabled"), nullptr, nullptr, NULL, true, false);
			return true;
		}
		if (!mir_wstrcmpi(one.c_str(), L"on")) {
			bTempForceCheck = true;
			bTempDisableCheck = false;
			CallFunctionAsync(sttSetTimerOn, this);
		}
		if (!mir_wstrcmpi(one.c_str(), L"off")) {
			bTempForceCheck = false;
			bTempDisableCheck = true;
			CallFunctionAsync(sttSetTimerOff, this);
		}
		if (!mir_wstrcmpi(one.c_str(), L"time") && !two.IsEmpty()) {
			m_iTempCheckTime = _wtoi(two.c_str());
			if (m_iTempCheckTime < 10 && m_iTempCheckTime != 0)
				m_iTempCheckTime = 10;

			if (m_iTempCheckTime == 0)
				DoEvent(GC_EVENT_INFORMATION, nullptr, m_info.sNick.c_str(), TranslateT("The time interval for the buddy check function is now at default setting"), nullptr, nullptr, NULL, true, false);
			else {
				wchar_t temp[200];
				mir_snwprintf(temp, TranslateT("The time interval for the buddy check function is now %u seconds"), m_iTempCheckTime);
				DoEvent(GC_EVENT_INFORMATION, nullptr, m_info.sNick.c_str(), temp, nullptr, nullptr, NULL, true, false);
			}
		}
		return true;
	}

	if (command == L"/whois") {
		if (one.IsEmpty())
			return false;
		m_manualWhoisCount++;
		return false;
	}

	if (command == L"/channelmanager") {
		if (window && !hContact && IsChannel(window)) {
			if (IsConnected()) {
				if (m_managerDlg != nullptr) {
					SetActiveWindow(m_managerDlg->GetHwnd());
					m_managerDlg->Close();
				}
				else {
					m_managerDlg = new CManagerDlg(this);
					m_managerDlg->Show();
					m_managerDlg->InitManager(1, window);
				}
			}
		}

		return true;
	}

	if (command == L"/who") {
		if (one.IsEmpty())
			return true;

		DoUserhostWithReason(2, L"U", false, L"%s", one.c_str());
		return false;
	}

	if (command == L"/hop") {
		if (!IsChannel(window))
			return true;

		PostIrcMessage(L"/PART %s", window);

		if ((one.IsEmpty() || !IsChannel(one))) {
			CHANNELINFO *wi = (CHANNELINFO *)Chat_GetUserInfo(m_szModuleName, window);
			if (wi && wi->pszPassword)
				PostIrcMessage(L"/JOIN %s %s", window, wi->pszPassword);
			else
				PostIrcMessage(L"/JOIN %s", window);
			return true;
		}

		Chat_Terminate(m_szModuleName, MakeWndID(window));

		PostIrcMessage(L"/JOIN %s", GetWordAddress(text, 1));
		return true;
	}

	if (command == L"/list") {
		if (m_listDlg == nullptr) {
			m_listDlg = new CListDlg(this);
			m_listDlg->Show();
		}
		SetActiveWindow(m_listDlg->GetHwnd());
		int minutes = (int)m_noOfChannels / 4000;
		int minutes2 = (int)m_noOfChannels / 9000;

		CMStringW szMsg(FORMAT, TranslateT("This command is not recommended on a network of this size!\r\nIt will probably cause high CPU usage and/or high bandwidth\r\nusage for around %u to %u minute(s).\r\n\r\nDo you want to continue?"), minutes2, minutes);
		if (m_noOfChannels < 4000 || (m_noOfChannels >= 4000 && MessageBox(nullptr, szMsg, TranslateT("IRC warning"), MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON2) == IDYES)) {
			ListView_DeleteAllItems(GetDlgItem(m_listDlg->GetHwnd(), IDC_INFO_LISTVIEW));
			PostIrcMessage(L"/lusers");
			return false;
		}
		m_listDlg->m_status.SetText(TranslateT("Aborted"));
		return true;
	}

	if (command == L"/me") {
		if (one.IsEmpty())
			return true;

		wchar_t szTemp[4000];
		mir_snwprintf(szTemp, L"\001ACTION %s\001", GetWordAddress(text.c_str(), 1));
		PostIrcMessageWnd(window, hContact, szTemp);
		return true;
	}

	if (command == L"/ame") {
		if (one.IsEmpty())
			return true;

		CMStringW S = L"/ME " + DoIdentifiers(GetWordAddress(text.c_str(), 1), window);
		S.Replace(L"%", L"%%");
		Chat_SendUserMessage(m_szModuleName, nullptr, S);
		return true;
	}

	if (command == L"/amsg") {
		if (one.IsEmpty())
			return true;

		CMStringW S = DoIdentifiers(GetWordAddress(text.c_str(), 1), window);
		S.Replace(L"%", L"%%");
		Chat_SendUserMessage(m_szModuleName, nullptr, S);
		return true;
	}

	if (command == L"/msg") {
		if (one.IsEmpty() || two.IsEmpty())
			return true;

		wchar_t szTemp[4000];
		mir_snwprintf(szTemp, L"/PRIVMSG %s", GetWordAddress(text.c_str(), 1));

		PostIrcMessageWnd(window, hContact, szTemp);
		return true;
	}

	if (command == L"/query") {
		if (one.IsEmpty() || IsChannel(one.c_str()))
			return true;

		CONTACT user = { one, nullptr, nullptr, false, false, false };
		MCONTACT hContact2 = CList_AddContact(&user, false, false);
		if (hContact2) {
			if (getByte(hContact, "AdvancedMode", 0) == 0)
				DoUserhostWithReason(1, (L"S" + one).c_str(), true, one.c_str());
			else {
				DBVARIANT dbv1;
				if (!getWString(hContact, "UWildcard", &dbv1)) {
					CMStringW S = L"S";
					S += dbv1.ptszVal;
					DoUserhostWithReason(2, S.c_str(), true, dbv1.ptszVal);
					db_free(&dbv1);
				}
				else {
					CMStringW S = L"S";
					S += one;
					DoUserhostWithReason(2, S.c_str(), true, one.c_str());
				}
			}

			CallService(MS_MSG_SENDMESSAGE, (WPARAM)hContact2, 0);
		}

		if (!two.IsEmpty()) {
			wchar_t szTemp[4000];
			mir_snwprintf(szTemp, L"/PRIVMSG %s", GetWordAddress(text.c_str(), 1));
			PostIrcMessageWnd(window, hContact, szTemp);
		}
		return true;
	}

	if (command == L"/ctcp") {
		if (one.IsEmpty() || two.IsEmpty())
			return true;

		wchar_t szTemp[1000];
		unsigned long ulAdr = 0;
		if (m_manualHost)
			ulAdr = ConvertIPToInteger(m_mySpecifiedHostIP);
		else
			ulAdr = ConvertIPToInteger(m_IPFromServer ? m_myHost : m_myLocalHost);

		// if it is not dcc or if it is dcc and a local ip exist
		if (mir_wstrcmpi(two.c_str(), L"dcc") != 0 || ulAdr) {
			if (mir_wstrcmpi(two.c_str(), L"ping") == 0)
				mir_snwprintf(szTemp, L"/PRIVMSG %s \001%s %u\001", one.c_str(), two.c_str(), time(nullptr));
			else
				mir_snwprintf(szTemp, L"/PRIVMSG %s \001%s\001", one.c_str(), GetWordAddress(text.c_str(), 2));
			PostIrcMessageWnd(window, hContact, szTemp);
		}

		if (mir_wstrcmpi(two.c_str(), L"dcc") != 0) {
			mir_snwprintf(szTemp, TranslateT("CTCP %s request sent to %s"), two.c_str(), one.c_str());
			DoEvent(GC_EVENT_INFORMATION, SERVERWINDOW, m_info.sNick.c_str(), szTemp, nullptr, nullptr, NULL, true, false);
		}

		return true;
	}

	if (command == L"/dcc") {
		if (one.IsEmpty() || two.IsEmpty())
			return true;

		if (mir_wstrcmpi(one.c_str(), L"send") == 0) {
			wchar_t szTemp[1000];
			unsigned long ulAdr = 0;

			if (m_manualHost)
				ulAdr = ConvertIPToInteger(m_mySpecifiedHostIP);
			else
				ulAdr = ConvertIPToInteger(m_IPFromServer ? m_myHost : m_myLocalHost);

			if (ulAdr) {
				CONTACT user = { two, nullptr, nullptr, false, false, true };
				MCONTACT ccNew = CList_AddContact(&user, false, false);
				if (ccNew) {
					CMStringW s;

					if (getByte(ccNew, "AdvancedMode", 0) == 0)
						DoUserhostWithReason(1, (L"S" + two).c_str(), true, two.c_str());
					else {
						DBVARIANT dbv1;
						CMStringW S = L"S";
						if (!getWString(ccNew, "UWildcard", &dbv1)) {
							S += dbv1.ptszVal;
							DoUserhostWithReason(2, S.c_str(), true, dbv1.ptszVal);
							db_free(&dbv1);
						}
						else {
							S += two;
							DoUserhostWithReason(2, S.c_str(), true, two.c_str());
						}
					}

					if (three.IsEmpty())
						CallService(MS_FILE_SENDFILE, ccNew, 0);
					else {
						CMStringW temp = GetWordAddress(text.c_str(), 3);
						wchar_t* pp[2];
						wchar_t* p = (wchar_t*)temp.c_str();
						pp[0] = p;
						pp[1] = nullptr;
						CallService(MS_FILE_SENDSPECIFICFILEST, ccNew, (LPARAM)pp);
					}
				}
			}
			else {
				mir_snwprintf(szTemp, TranslateT("DCC ERROR: Unable to automatically resolve external IP"));
				DoEvent(GC_EVENT_INFORMATION, nullptr, m_info.sNick.c_str(), szTemp, nullptr, nullptr, NULL, true, false);
			}
			return true;
		}

		if (mir_wstrcmpi(one.c_str(), L"chat") == 0) {
			wchar_t szTemp[1000];

			unsigned long ulAdr = 0;
			if (m_manualHost)
				ulAdr = ConvertIPToInteger(m_mySpecifiedHostIP);
			else
				ulAdr = ConvertIPToInteger(m_IPFromServer ? m_myHost : m_myLocalHost);

			if (ulAdr) {
				CMStringW contact = two;  contact += DCCSTRING;
				CONTACT user = { contact, nullptr, nullptr, false, false, true };
				MCONTACT ccNew = CList_AddContact(&user, false, false);
				setByte(ccNew, "DCC", 1);

				int iPort = 0;
				if (ccNew) {
					DCCINFO* dci = new DCCINFO;
					dci->hContact = ccNew;
					dci->sContactName = two;
					dci->iType = DCC_CHAT;
					dci->bSender = true;

					CDccSession *dcc = new CDccSession(this, dci);
					CDccSession* olddcc = FindDCCSession(ccNew);
					if (olddcc)
						olddcc->Disconnect();
					AddDCCSession(ccNew, dcc);
					iPort = dcc->Connect();
				}

				if (iPort != 0) {
					PostIrcMessage(L"/CTCP %s DCC CHAT chat %u %u", two.c_str(), ulAdr, iPort);
					mir_snwprintf(szTemp, TranslateT("DCC CHAT request sent to %s"), two.c_str(), one.c_str());
					DoEvent(GC_EVENT_INFORMATION, nullptr, m_info.sNick.c_str(), szTemp, nullptr, nullptr, NULL, true, false);
				}
				else {
					mir_snwprintf(szTemp, TranslateT("DCC ERROR: Unable to bind port"));
					DoEvent(GC_EVENT_INFORMATION, nullptr, m_info.sNick.c_str(), szTemp, nullptr, nullptr, NULL, true, false);
				}
			}
			else {
				mir_snwprintf(szTemp, TranslateT("DCC ERROR: Unable to automatically resolve external IP"));
				DoEvent(GC_EVENT_INFORMATION, nullptr, m_info.sNick.c_str(), szTemp, nullptr, nullptr, NULL, true, false);
			}
		}
		return true;
	}
	return false;
}

/////////////////////////////////////////////////////////////////////////////////////////

struct DoInputRequestParam
{
	DoInputRequestParam(CIrcProto* _pro, const wchar_t* _str) :
	ppro(_pro),
	str(mir_wstrdup(_str))
	{}

	CIrcProto *ppro;
	wchar_t* str;
};

static void __stdcall DoInputRequestAliasApcStub(void* _par)
{
	DoInputRequestParam* param = (DoInputRequestParam*)_par;
	CIrcProto *ppro = param->ppro;
	wchar_t* str = param->str;

	wchar_t* infotext = nullptr;
	wchar_t* title = nullptr;
	wchar_t* defaulttext = nullptr;
	CMStringW command = (wchar_t*)str;
	wchar_t* p = wcsstr((wchar_t*)str, L"%question");
	if (p[9] == '=' && p[10] == '\"') {
		infotext = &p[11];
		p = wcschr(infotext, '\"');
		if (p) {
			*p = 0;
			p++;
			if (*p == ',' && p[1] == '\"') {
				p++; p++;
				title = p;
				p = wcschr(title, '\"');
				if (p) {
					*p = 0;
					p++;
					if (*p == ',' && p[1] == '\"') {
						p++; p++;
						defaulttext = p;
						p = wcschr(defaulttext, '\"');
						if (p)
							*p = 0;
					}
				}
			}
		}
	}

	CQuestionDlg* dlg = new CQuestionDlg(ppro);
	dlg->Show();
	HWND question_hWnd = dlg->GetHwnd();

	if (title)
		SetDlgItemText(question_hWnd, IDC_CAPTION, title);
	else
		SetDlgItemText(question_hWnd, IDC_CAPTION, TranslateT("Input command"));

	if (infotext)
		SetDlgItemText(question_hWnd, IDC_TEXT, infotext);
	else
		SetDlgItemText(question_hWnd, IDC_TEXT, TranslateT("Please enter the reply"));

	if (defaulttext)
		SetDlgItemText(question_hWnd, IDC_EDIT, defaulttext);

	SetDlgItemText(question_hWnd, IDC_HIDDENEDIT, command.c_str());
	dlg->Activate();

	mir_free(str);
	delete param;
}

bool CIrcProto::PostIrcMessage(const wchar_t* fmt, ...)
{
	if (!fmt || mir_wstrlen(fmt) < 1 || mir_wstrlen(fmt) > 4000)
		return 0;

	va_list marker;
	va_start(marker, fmt);
	static wchar_t szBuf[4 * 1024];
	mir_vsnwprintf(szBuf, _countof(szBuf), fmt, marker);
	va_end(marker);

	return PostIrcMessageWnd(nullptr, NULL, szBuf);
}

bool CIrcProto::PostIrcMessageWnd(wchar_t *window, MCONTACT hContact, const wchar_t *szBuf)
{
	DBVARIANT dbv;
	wchar_t windowname[256];
	BYTE bDCC = 0;

	if (hContact)
		bDCC = getByte(hContact, "DCC", 0);

	if (!IsConnected() && !bDCC || !szBuf || mir_wstrlen(szBuf) < 1)
		return 0;

	if (hContact && !getWString(hContact, "Nick", &dbv)) {
		mir_wstrncpy(windowname, dbv.ptszVal, 255);
		db_free(&dbv);
	}
	else if (window)
		mir_wstrncpy(windowname, window, 255);
	else
		mir_wstrncpy(windowname, SERVERWINDOW, 255);

	if (mir_wstrcmpi(window, SERVERWINDOW) != 0) {
		wchar_t* p1 = wcschr(windowname, ' ');
		if (p1)
			*p1 = 0;
	}

	// remove unecessary linebreaks, and do the aliases
	CMStringW Message = szBuf;
	AddCR(Message);
	RemoveLinebreaks(Message);
	if (!hContact && IsConnected()) {
		Message = DoAlias(Message.c_str(), windowname);

		if (Message.Find(L"%question") != -1) {
			CallFunctionAsync(DoInputRequestAliasApcStub, new DoInputRequestParam(this, Message));
			return 1;
		}

		Message.Replace(L"%newl", L"\r\n");
		RemoveLinebreaks(Message);
	}

	if (Message.IsEmpty())
		return 0;

	CHANNELINFO *wi = (CHANNELINFO *)Chat_GetUserInfo(m_szModuleName, windowname);
	int cp = (wi) ? wi->codepage : getCodepage();

	// process the message
	while (!Message.IsEmpty()) {
		// split the text into lines, and do an automatic textsplit on long lies as well
		bool flag = false;
		CMStringW DoThis = L"";
		int index = Message.Find(L"\r\n", 0);
		if (index == -1)
			index = Message.GetLength();

		if (index > 464)
			index = 432;
		DoThis = Message.Mid(0, index);
		Message.Delete(0, index);
		if (Message.Find(L"\r\n", 0) == 0)
			Message.Delete(0, 2);

		//do this if it's a /raw
		if (IsConnected() && (GetWord(DoThis.c_str(), 0) == L"/raw" || GetWord(DoThis.c_str(), 0) == L"/quote")) {
			if (GetWord(DoThis.c_str(), 1).IsEmpty())
				continue;

			CMStringW S = GetWordAddress(DoThis.c_str(), 1);
			SendIrcMessage(S.c_str(), true, cp);
			continue;
		}

		// Do this if the message is not a command
		if ((GetWord(DoThis.c_str(), 0)[0] != '/') ||													// not a command
			((GetWord(DoThis.c_str(), 0)[0] == '/') && (GetWord(DoThis.c_str(), 0)[1] == '/')) ||		// or double backslash at the beginning
			hContact) {
			CMStringW S = L"/PRIVMSG ";
			if (mir_wstrcmpi(window, SERVERWINDOW) == 0 && !m_info.sServerName.IsEmpty())
				S += m_info.sServerName + L" " + DoThis;
			else
				S += CMStringW(windowname) + L" " + DoThis;

			DoThis = S;
			flag = true;
		}

		// and here we send it unless the command was a hardcoded one that should not be sent
		if (DoHardcodedCommand(DoThis, windowname, hContact))
			continue;

		if (!IsConnected() && !bDCC)
			continue;

		if (!flag && IsConnected())
			DoThis = DoIdentifiers(DoThis, windowname);

		if (hContact) {
			if (flag && bDCC) {
				CDccSession *dcc = FindDCCSession(hContact);
				if (dcc) {
					FormatMsg(DoThis);
					CMStringW mess = GetWordAddress(DoThis.c_str(), 2);
					if (mess[0] == ':')
						mess.Delete(0, 1);
					mess += '\n';
					dcc->SendStuff(mess.c_str());
				}
			}
			else if (IsConnected()) {
				FormatMsg(DoThis);
				SendIrcMessage(DoThis.c_str(), false, cp);
			}
		}
		else {
			FormatMsg(DoThis);
			SendIrcMessage(DoThis.c_str(), true, cp);
		}
	}

	return 1;
}