/* 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" #include static volatile LONG g_msgid = 1; static int CompareSessions(const CDccSession* p1, const CDccSession* p2) { return INT_PTR(p1->di->hContact) - INT_PTR(p2->di->hContact); } CIrcProto::CIrcProto(const char* szModuleName, const wchar_t* tszUserName) : PROTO(szModuleName, tszUserName), m_dcc_chats(10, CompareSessions), m_dcc_xfers(10, CompareSessions), m_ignoreItems(10), vUserhostReasons(10), vWhoInProgress(10) { m_evWndCreate = ::CreateEvent(nullptr, FALSE, FALSE, nullptr); CreateProtoService(PS_GETMYAWAYMSG, &CIrcProto::GetMyAwayMsg); CreateProtoService(PS_CREATEACCMGRUI, &CIrcProto::SvcCreateAccMgrUI); CreateProtoService(PS_JOINCHAT, &CIrcProto::OnJoinChat); CreateProtoService(PS_LEAVECHAT, &CIrcProto::OnLeaveChat); CreateProtoService(IRC_JOINCHANNEL, &CIrcProto::OnJoinMenuCommand); CreateProtoService(IRC_QUICKCONNECT, &CIrcProto::OnQuickConnectMenuCommand); CreateProtoService(IRC_CHANGENICK, &CIrcProto::OnChangeNickMenuCommand); CreateProtoService(IRC_SHOWLIST, &CIrcProto::OnShowListMenuCommand); CreateProtoService(IRC_SHOWSERVER, &CIrcProto::OnShowServerMenuCommand); CreateProtoService(IRC_UM_CHANSETTINGS, &CIrcProto::OnMenuChanSettings); CreateProtoService(IRC_UM_WHOIS, &CIrcProto::OnMenuWhois); CreateProtoService(IRC_UM_DISCONNECT, &CIrcProto::OnMenuDisconnect); CreateProtoService(IRC_UM_IGNORE, &CIrcProto::OnMenuIgnore); CreateProtoService("/DblClickEvent", &CIrcProto::OnDoubleclicked); CreateProtoService("/InsertRawIn", &CIrcProto::Scripting_InsertRawIn); CreateProtoService("/InsertRawOut", &CIrcProto::Scripting_InsertRawOut); CreateProtoService("/InsertGuiIn", &CIrcProto::Scripting_InsertGuiIn); CreateProtoService("/InsertGuiOut", &CIrcProto::Scripting_InsertGuiOut); CreateProtoService("/GetIrcData", &CIrcProto::Scripting_GetIrcData); codepage = CP_ACP; InitPrefs(); CList_SetAllOffline(true); IRC_MAP_ENTRY("PING", PING) IRC_MAP_ENTRY("JOIN", JOIN) IRC_MAP_ENTRY("QUIT", QUIT) IRC_MAP_ENTRY("KICK", KICK) IRC_MAP_ENTRY("MODE", MODE) IRC_MAP_ENTRY("NICK", NICK) IRC_MAP_ENTRY("PART", PART) IRC_MAP_ENTRY("PRIVMSG", PRIVMSG) IRC_MAP_ENTRY("TOPIC", TOPIC) IRC_MAP_ENTRY("NOTICE", NOTICE) IRC_MAP_ENTRY("PING", PINGPONG) IRC_MAP_ENTRY("PONG", PINGPONG) IRC_MAP_ENTRY("CAP", CAP) IRC_MAP_ENTRY("AUTHENTICATE", AUTHENTICATE) IRC_MAP_ENTRY("INVITE", INVITE) IRC_MAP_ENTRY("ERROR", ERROR) IRC_MAP_ENTRY("001", WELCOME) IRC_MAP_ENTRY("002", YOURHOST) IRC_MAP_ENTRY("005", SUPPORT) IRC_MAP_ENTRY("223", WHOIS_OTHER) //CodePage info IRC_MAP_ENTRY("254", NOOFCHANNELS) IRC_MAP_ENTRY("263", TRYAGAIN) IRC_MAP_ENTRY("264", WHOIS_OTHER) //Encryption info (SSL connect) IRC_MAP_ENTRY("301", WHOIS_AWAY) IRC_MAP_ENTRY("302", USERHOST_REPLY) IRC_MAP_ENTRY("305", BACKFROMAWAY) IRC_MAP_ENTRY("306", SETAWAY) IRC_MAP_ENTRY("307", WHOIS_AUTH) IRC_MAP_ENTRY("310", WHOIS_OTHER) IRC_MAP_ENTRY("311", WHOIS_NAME) IRC_MAP_ENTRY("312", WHOIS_SERVER) IRC_MAP_ENTRY("313", WHOIS_OTHER) IRC_MAP_ENTRY("315", WHO_END) IRC_MAP_ENTRY("317", WHOIS_IDLE) IRC_MAP_ENTRY("318", WHOIS_END) IRC_MAP_ENTRY("319", WHOIS_CHANNELS) IRC_MAP_ENTRY("320", WHOIS_AUTH) IRC_MAP_ENTRY("321", LISTSTART) IRC_MAP_ENTRY("322", LIST) IRC_MAP_ENTRY("323", LISTEND) IRC_MAP_ENTRY("324", MODEQUERY) IRC_MAP_ENTRY("330", WHOIS_AUTH) IRC_MAP_ENTRY("332", INITIALTOPIC) IRC_MAP_ENTRY("333", INITIALTOPICNAME) IRC_MAP_ENTRY("352", WHO_REPLY) IRC_MAP_ENTRY("353", NAMES) IRC_MAP_ENTRY("366", ENDNAMES) IRC_MAP_ENTRY("367", BANLIST) IRC_MAP_ENTRY("368", BANLISTEND) IRC_MAP_ENTRY("346", BANLIST) IRC_MAP_ENTRY("347", BANLISTEND) IRC_MAP_ENTRY("348", BANLIST) IRC_MAP_ENTRY("349", BANLISTEND) IRC_MAP_ENTRY("371", WHOIS_OTHER) IRC_MAP_ENTRY("376", ENDMOTD) IRC_MAP_ENTRY("401", WHOIS_NO_USER) IRC_MAP_ENTRY("403", JOINERROR) IRC_MAP_ENTRY("416", WHOTOOLONG) IRC_MAP_ENTRY("421", UNKNOWN) IRC_MAP_ENTRY("422", ENDMOTD) IRC_MAP_ENTRY("433", NICK_ERR) IRC_MAP_ENTRY("471", JOINERROR) IRC_MAP_ENTRY("473", JOINERROR) IRC_MAP_ENTRY("474", JOINERROR) IRC_MAP_ENTRY("475", JOINERROR) IRC_MAP_ENTRY("671", WHOIS_OTHER) //Encryption info (SSL connect) IRC_MAP_ENTRY("903", AUTH_OK) IRC_MAP_ENTRY("904", AUTH_FAIL) IRC_MAP_ENTRY("905", AUTH_FAIL) IRC_MAP_ENTRY("906", AUTH_FAIL) } CIrcProto::~CIrcProto() { if (con) { Netlib_CloseHandle(con); con = nullptr; } Netlib_CloseHandle(m_hNetlibUser); m_hNetlibUser = nullptr; Netlib_CloseHandle(hNetlibDCC); hNetlibDCC = nullptr; mir_free(m_alias); CloseHandle(m_evWndCreate); KillChatTimer(OnlineNotifTimer); KillChatTimer(OnlineNotifTimer3); } //////////////////////////////////////////////////////////////////////////////////////// // OnModulesLoaded - performs hook registration static int sttCheckPerform(const char *szSetting, void *lParam) { if (!_strnicmp(szSetting, "PERFORM:", 8)) { CMStringA s = szSetting; s.MakeUpper(); if (s != szSetting) { OBJLIST* p = (OBJLIST*)lParam; p->insert(new CMStringA(szSetting)); } } return 0; } int CIrcProto::OnModulesLoaded(WPARAM, LPARAM) { wchar_t name[128]; mir_snwprintf(name, TranslateT("%s server connection"), m_tszUserName); db_unset(NULL, m_szModuleName, "JTemp"); NETLIBUSER nlu = {}; nlu.flags = NUF_OUTGOING | NUF_INCOMING | NUF_HTTPCONNS | NUF_UNICODE; nlu.szSettingsModule = m_szModuleName; nlu.szDescriptiveName.w = name; m_hNetlibUser = Netlib_RegisterUser(&nlu); nlu.flags = NUF_OUTGOING | NUF_INCOMING | NUF_HTTPCONNS | NUF_UNICODE; char szTemp2[256]; mir_snprintf(szTemp2, "%s DCC", m_szModuleName); nlu.szSettingsModule = szTemp2; mir_snwprintf(name, TranslateT("%s client-to-client connections"), m_tszUserName); nlu.szDescriptiveName.w = name; hNetlibDCC = Netlib_RegisterUser(&nlu); GCREGISTER gcr = {}; gcr.dwFlags = GC_CHANMGR | GC_BOLD | GC_ITALICS | GC_UNDERLINE | GC_COLOR | GC_BKGCOLOR; gcr.ptszDispName = m_tszUserName; gcr.pszModule = m_szModuleName; Chat_Register(&gcr); HookProtoEvent(ME_GC_EVENT, &CIrcProto::GCEventHook); HookProtoEvent(ME_GC_BUILDMENU, &CIrcProto::GCMenuHook); Chat_NewSession(GCW_SERVER, m_szModuleName, SERVERWINDOW, _A2T(m_network)); if (m_useServer && !m_hideServerWindow) Chat_Control(m_szModuleName, SERVERWINDOW, WINDOW_VISIBLE); else Chat_Control(m_szModuleName, SERVERWINDOW, WINDOW_HIDDEN); wchar_t szTemp[MAX_PATH]; mir_snwprintf(szTemp, L"%%miranda_path%%\\Plugins\\%S_perform.ini", m_szModuleName); wchar_t *szLoadFileName = Utils_ReplaceVarsW(szTemp); char* pszPerformData = IrcLoadFile(szLoadFileName); if (pszPerformData != nullptr) { char *p1 = pszPerformData, *p2 = pszPerformData; while ((p1 = strstr(p2, "NETWORK: ")) != nullptr) { p1 += 9; p2 = strchr(p1, '\n'); CMStringA sNetwork(p1, int(p2 - p1 - 1)); sNetwork.MakeUpper(); p1 = p2; p2 = strstr(++p1, "\nNETWORK: "); if (!p2) p2 = p1 + mir_strlen(p1) - 1; if (p1 == p2) break; *p2++ = 0; setString(("PERFORM:" + sNetwork).c_str(), rtrim(p1)); } delete[] pszPerformData; ::_wremove(szLoadFileName); } mir_free(szLoadFileName); if (!getByte("PerformConversionDone", 0)) { OBJLIST performToConvert(10); db_enum_settings(NULL, sttCheckPerform, m_szModuleName, &performToConvert); for (int i = 0; i < performToConvert.getCount(); i++) { CMStringA s = performToConvert[i]; DBVARIANT dbv; if (!getWString(s, &dbv)) { db_unset(NULL, m_szModuleName, s); s.MakeUpper(); setWString(s, dbv.ptszVal); db_free(&dbv); } } setByte("PerformConversionDone", 1); } InitIgnore(); HookProtoEvent(ME_USERINFO_INITIALISE, &CIrcProto::OnInitUserInfo); HookProtoEvent(ME_OPT_INITIALISE, &CIrcProto::OnInitOptionsPages); if (m_nick[0]) { wchar_t szBuf[40]; if (mir_wstrlen(m_alternativeNick) == 0) { mir_snwprintf(szBuf, L"%s%u", m_nick, rand() % 9999); setWString("AlernativeNick", szBuf); mir_wstrncpy(m_alternativeNick, szBuf, 30); } if (mir_wstrlen(m_name) == 0) { mir_snwprintf(szBuf, L"Miranda%u", rand() % 9999); setWString("Name", szBuf); mir_wstrncpy(m_name, szBuf, 200); } } return 0; } //////////////////////////////////////////////////////////////////////////////////////// // AddToList - adds a contact to the contact list MCONTACT __cdecl CIrcProto::AddToList(int, PROTOSEARCHRESULT* psr) { if (m_iStatus == ID_STATUS_OFFLINE || m_iStatus == ID_STATUS_CONNECTING) return 0; wchar_t *id = psr->id.w ? psr->id.w : psr->nick.w; id = psr->flags & PSR_UNICODE ? mir_wstrdup((wchar_t*)id) : mir_a2u((char*)id); CONTACT user = { id, nullptr, nullptr, true, false, false }; MCONTACT hContact = CList_AddContact(&user, true, false); if (hContact) { DBVARIANT dbv1; CMStringW S = L"S"; if (getByte(hContact, "AdvancedMode", 0) == 0) { S += user.name; DoUserhostWithReason(1, S, true, user.name); } else { if (!getWString(hContact, "UWildcard", &dbv1)) { S += dbv1.ptszVal; DoUserhostWithReason(2, S, true, dbv1.ptszVal); db_free(&dbv1); } else { S += user.name; DoUserhostWithReason(2, S, true, user.name); } } if (getByte("MirVerAutoRequest", 1)) PostIrcMessage(L"/PRIVMSG %s \001VERSION\001", user.name); } mir_free(id); return hContact; } //////////////////////////////////////////////////////////////////////////////////////// // AuthAllow - processes the successful authorization int __cdecl CIrcProto::Authorize(MEVENT) { return 0; } //////////////////////////////////////////////////////////////////////////////////////// // AuthDeny - handles the unsuccessful authorization int __cdecl CIrcProto::AuthDeny(MEVENT, const wchar_t*) { return 0; } //////////////////////////////////////////////////////////////////////////////////////// // FileAllow - starts a file transfer HANDLE __cdecl CIrcProto::FileAllow(MCONTACT, HANDLE hTransfer, const wchar_t* szPath) { DCCINFO* di = (DCCINFO*)hTransfer; if (!IsConnected()) { delete di; return (HANDLE)szPath; } di->sPath = szPath; di->sFileAndPath = di->sPath + di->sFile; CDccSession *dcc = new CDccSession(this, di); AddDCCSession(di, dcc); dcc->Connect(); return di; } //////////////////////////////////////////////////////////////////////////////////////// // FileCancel - cancels a file transfer int __cdecl CIrcProto::FileCancel(MCONTACT, HANDLE hTransfer) { DCCINFO* di = (DCCINFO*)hTransfer; CDccSession *dcc = FindDCCSession(di); if (dcc) { InterlockedExchange(&dcc->dwWhatNeedsDoing, (long)FILERESUME_CANCEL); SetEvent(dcc->hEvent); dcc->Disconnect(); } return 0; } //////////////////////////////////////////////////////////////////////////////////////// // FileDeny - denies a file transfer int __cdecl CIrcProto::FileDeny(MCONTACT, HANDLE hTransfer, const wchar_t*) { DCCINFO* di = (DCCINFO*)hTransfer; delete di; return 0; } //////////////////////////////////////////////////////////////////////////////////////// // FileResume - processes file renaming etc int __cdecl CIrcProto::FileResume(HANDLE hTransfer, int* action, const wchar_t** szFilename) { DCCINFO* di = (DCCINFO*)hTransfer; long i = (long)*action; CDccSession *dcc = FindDCCSession(di); if (dcc) { InterlockedExchange(&dcc->dwWhatNeedsDoing, i); if (*action == FILERESUME_RENAME) { wchar_t* szTemp = wcsdup(*szFilename); InterlockedExchangePointer((PVOID*)&dcc->NewFileName, szTemp); } if (*action == FILERESUME_RESUME) { unsigned __int64 dwPos = 0; struct _stati64 statbuf; if (_wstat64(di->sFileAndPath.c_str(), &statbuf) == 0 && (statbuf.st_mode & _S_IFDIR) == 0) dwPos = statbuf.st_size; CMStringW sFileWithQuotes = di->sFile; // if spaces in the filename surround witrh quotes if (sFileWithQuotes.Find(' ', 0) != -1) { sFileWithQuotes.Insert(0, L"\""); sFileWithQuotes.Insert(sFileWithQuotes.GetLength(), L"\""); } if (di->bReverse) PostIrcMessage(L"/PRIVMSG %s \001DCC RESUME %s 0 %I64u %s\001", di->sContactName.c_str(), sFileWithQuotes.c_str(), dwPos, dcc->di->sToken.c_str()); else PostIrcMessage(L"/PRIVMSG %s \001DCC RESUME %s %u %I64u\001", di->sContactName.c_str(), sFileWithQuotes.c_str(), di->iPort, dwPos); return 0; } SetEvent(dcc->hEvent); } return 0; } //////////////////////////////////////////////////////////////////////////////////////// // GetCaps - return protocol capabilities bits DWORD_PTR __cdecl CIrcProto::GetCaps(int type, MCONTACT) { switch (type) { case PFLAGNUM_1: return PF1_BASICSEARCH | PF1_MODEMSG | PF1_FILE | PF1_CHAT | PF1_CANRENAMEFILE | PF1_PEER2PEER | PF1_IM; case PFLAGNUM_2: return PF2_ONLINE | PF2_SHORTAWAY; case PFLAGNUM_3: return PF2_SHORTAWAY; case PFLAGNUM_4: return PF4_NOAUTHDENYREASON | PF4_NOCUSTOMAUTH; case PFLAG_UNIQUEIDTEXT: return (DWORD_PTR)Translate("Nickname"); case PFLAG_MAXLENOFMESSAGE: return 400; case PFLAG_UNIQUEIDSETTING: return (DWORD_PTR) "Nick"; } return 0; } //////////////////////////////////////////////////////////////////////////////////////// // SearchBasic - searches the contact by JID struct AckBasicSearchParam { wchar_t buf[50]; }; void __cdecl CIrcProto::AckBasicSearch(void *arg) { Thread_SetName("IRC: AckBasicSearch"); AckBasicSearchParam *param = (AckBasicSearchParam*)arg; PROTOSEARCHRESULT psr = { sizeof(psr) }; psr.flags = PSR_UNICODE; psr.id.w = psr.nick.w = param->buf; ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_DATA, (HANDLE)1, (LPARAM)& psr); ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, (HANDLE)1, 0); delete param; } HANDLE __cdecl CIrcProto::SearchBasic(const wchar_t* szId) { if (szId) { if (m_iStatus != ID_STATUS_OFFLINE && m_iStatus != ID_STATUS_CONNECTING && szId && szId[0] && !IsChannel(szId)) { AckBasicSearchParam* param = new AckBasicSearchParam; mir_wstrncpy(param->buf, szId, 50); ForkThread(&CIrcProto::AckBasicSearch, param); return (HANDLE)1; } } return nullptr; } //////////////////////////////////////////////////////////////////////////////////////// // SendFile - sends a file HANDLE __cdecl CIrcProto::SendFile(MCONTACT hContact, const wchar_t*, wchar_t** ppszFiles) { DCCINFO* dci = nullptr; int iPort = 0; int index = 0; unsigned __int64 size = 0; // do not send to channels :-P if (isChatRoom(hContact)) return nullptr; // stop if it is an active type filetransfer and the user's IP is not known unsigned long ulAdr = 0; if (m_manualHost) ulAdr = ConvertIPToInteger(m_mySpecifiedHostIP); else ulAdr = ConvertIPToInteger(m_IPFromServer ? m_myHost : m_myLocalHost); if (!m_DCCPassive && !ulAdr) { DoEvent(GC_EVENT_INFORMATION, nullptr, m_info.sNick.c_str(), TranslateT("DCC ERROR: Unable to automatically resolve external IP"), nullptr, nullptr, NULL, true, false); return nullptr; } if (ppszFiles[index]) { //get file size while (ppszFiles[index]) { struct _stati64 statbuf; if (_wstat64(ppszFiles[index], &statbuf) == 0 && (statbuf.st_mode & _S_IFDIR) == 0) { size = statbuf.st_size; break; } index++; } if (size == 0) { DoEvent(GC_EVENT_INFORMATION, nullptr, m_info.sNick.c_str(), TranslateT("DCC ERROR: No valid files specified"), nullptr, nullptr, NULL, true, false); return nullptr; } DBVARIANT dbv; if (!getWString(hContact, "Nick", &dbv)) { // set up a basic DCCINFO struct and pass it to a DCC object dci = new DCCINFO; dci->sFileAndPath = ppszFiles[index]; int i = dci->sFileAndPath.ReverseFind('\\'); if (i != -1) { dci->sPath = dci->sFileAndPath.Mid(0, i + 1); dci->sFile = dci->sFileAndPath.Mid(i + 1); } CMStringW sFileWithQuotes = dci->sFile; // if spaces in the filename surround witrh quotes if (sFileWithQuotes.Find(' ', 0) != -1) { sFileWithQuotes.Insert(0, L"\""); sFileWithQuotes.Insert(sFileWithQuotes.GetLength(), L"\""); } dci->hContact = hContact; dci->sContactName = dbv.ptszVal; dci->iType = DCC_SEND; dci->bReverse = m_DCCPassive ? true : false; dci->bSender = true; dci->dwSize = size; // create new dcc object CDccSession *dcc = new CDccSession(this, dci); // keep track of all objects created AddDCCSession(dci, dcc); // need to make sure that %'s are doubled to avoid having chat interpret as color codes CMStringW sFileCorrect = dci->sFile; sFileCorrect.Replace(L"%", L"%%"); // is it an reverse filetransfer (receiver acts as server) if (dci->bReverse) { wchar_t szTemp[256]; PostIrcMessage(L"/CTCP %s DCC SEND %s 200 0 %I64u %u", dci->sContactName.c_str(), sFileWithQuotes.c_str(), dci->dwSize, dcc->iToken); mir_snwprintf(szTemp, TranslateT("DCC reversed file transfer request sent to %s [%s]"), dci->sContactName.c_str(), sFileCorrect.c_str()); DoEvent(GC_EVENT_INFORMATION, nullptr, m_info.sNick.c_str(), szTemp, nullptr, nullptr, NULL, true, false); if (m_sendNotice) { mir_snwprintf(szTemp, L"/NOTICE %s I am sending the file '\002%s\002' (%I64u kB) to you, please accept it. [Reverse transfer]", dci->sContactName.c_str(), sFileCorrect.c_str(), dci->dwSize / 1024); PostIrcMessage(szTemp); } } else { // ... normal filetransfer. iPort = dcc->Connect(); if (iPort) { wchar_t szTemp[256]; PostIrcMessage(L"/CTCP %s DCC SEND %s %u %u %I64u", dci->sContactName.c_str(), sFileWithQuotes.c_str(), ulAdr, iPort, dci->dwSize); mir_snwprintf(szTemp, TranslateT("DCC file transfer request sent to %s [%s]"), dci->sContactName.c_str(), sFileCorrect.c_str()); DoEvent(GC_EVENT_INFORMATION, nullptr, m_info.sNick.c_str(), szTemp, nullptr, nullptr, NULL, true, false); if (m_sendNotice) { mir_snwprintf(szTemp, L"/NOTICE %s I am sending the file '\002%s\002' (%I64u kB) to you, please accept it. [IP: %s]", dci->sContactName.c_str(), sFileCorrect.c_str(), dci->dwSize / 1024, (wchar_t*)_A2T(ConvertIntegerToIP(ulAdr))); PostIrcMessage(szTemp); } } else DoEvent(GC_EVENT_INFORMATION, nullptr, m_info.sNick.c_str(), TranslateT("DCC ERROR: Unable to bind local port"), nullptr, nullptr, NULL, true, false); } // fix for sending multiple files index++; while (ppszFiles[index]) { if (_waccess(ppszFiles[index], 0) == 0) { PostIrcMessage(L"/DCC SEND %s %S", dci->sContactName.c_str(), ppszFiles[index]); } index++; } db_free(&dbv); } } if (dci) return dci; return nullptr; } //////////////////////////////////////////////////////////////////////////////////////// // SendMessage - sends a message struct TFakeAckParam { __inline TFakeAckParam(MCONTACT _hContact, int _msgid) : hContact(_hContact), msgid(_msgid) {} MCONTACT hContact; int msgid; }; void __cdecl CIrcProto::AckMessageFail(void *info) { Thread_SetName("IRC: AckMessageFail"); ProtoBroadcastAck((UINT_PTR)info, ACKTYPE_MESSAGE, ACKRESULT_FAILED, nullptr, (LPARAM)Translate("The protocol is not online")); } void __cdecl CIrcProto::AckMessageFailDcc(void *info) { Thread_SetName("IRC: AckMessageFailDcc"); ProtoBroadcastAck((UINT_PTR)info, ACKTYPE_MESSAGE, ACKRESULT_FAILED, nullptr, (LPARAM)Translate("The dcc chat connection is not active")); } void __cdecl CIrcProto::AckMessageSuccess(void *info) { Thread_SetName("IRC: AckMessageSuccess"); TFakeAckParam *param = (TFakeAckParam*)info; ProtoBroadcastAck(param->hContact, ACKTYPE_MESSAGE, ACKRESULT_SUCCESS, (HANDLE)param->msgid, 0); delete param; } int __cdecl CIrcProto::SendMsg(MCONTACT hContact, int, const char* pszSrc) { BYTE bDcc = getByte(hContact, "DCC", 0); WORD wStatus = getWord(hContact, "Status", ID_STATUS_OFFLINE); if (bDcc && wStatus != ID_STATUS_ONLINE) { ForkThread(&CIrcProto::AckMessageFailDcc, (void*)hContact); return 0; } if (!bDcc && (m_iStatus == ID_STATUS_OFFLINE || m_iStatus == ID_STATUS_CONNECTING)) { ForkThread(&CIrcProto::AckMessageFail, (void*)hContact); return 0; } wchar_t *result; mir_utf8decode(NEWSTR_ALLOCA(pszSrc), &result); PostIrcMessageWnd(nullptr, hContact, result); mir_free(result); int seq = InterlockedIncrement(&g_msgid); ForkThread(&CIrcProto::AckMessageSuccess, new TFakeAckParam(hContact, seq)); return seq; } //////////////////////////////////////////////////////////////////////////////////////// // SetStatus - sets the protocol status int __cdecl CIrcProto::SetStatus(int iNewStatus) { return SetStatusInternal(iNewStatus, false); } int CIrcProto::SetStatusInternal(int iNewStatus, bool bIsInternal) { if (iNewStatus != ID_STATUS_OFFLINE && !m_network[0]) { if (m_nick[0] && !m_disableDefaultServer) { if (m_quickDlg == nullptr) { m_quickDlg = new CQuickDlg(this); m_quickComboSelection = m_serverComboSelection + 1; m_quickDlg->Show(); } HWND hwnd = m_quickDlg->GetHwnd(); SetWindowTextA(hwnd, "Miranda IRC"); SetDlgItemText(hwnd, IDC_TEXT, TranslateT("Please choose an IRC-network to go online. This network will be the default.")); SetDlgItemText(hwnd, IDC_CAPTION, TranslateT("Default network")); Window_SetIcon_IcoLib(hwnd, GetIconHandle(IDI_MAIN)); ShowWindow(hwnd, SW_SHOW); SetActiveWindow(hwnd); } return 0; } if (iNewStatus != ID_STATUS_OFFLINE && !m_nick[0] || !m_userID[0] || !m_name[0]) { Clist_TrayNotifyW(m_szModuleName, TranslateT("IRC error"), TranslateT("Connection cannot be established! You have not completed all necessary fields (Nickname, User ID and m_name)."), NIIF_ERROR, 15000); return 0; } if (!bIsInternal) m_iDesiredStatus = iNewStatus; if ((iNewStatus == ID_STATUS_ONLINE || iNewStatus == ID_STATUS_AWAY || iNewStatus == ID_STATUS_FREECHAT) && !IsConnected()) //go from offline to online { if (!m_bConnectThreadRunning) ConnectToServer(); } else if ((iNewStatus == ID_STATUS_ONLINE || iNewStatus == ID_STATUS_FREECHAT) && IsConnected() && m_iStatus == ID_STATUS_AWAY) //go to online while connected { m_statusMessage = L""; PostIrcMessage(L"/AWAY"); return 0; } else if (iNewStatus == ID_STATUS_OFFLINE && IsConnected()) //go from online/away to offline DisconnectFromServer(); else if (iNewStatus == ID_STATUS_OFFLINE && !IsConnected()) //offline to offline { return 0; } else if (iNewStatus == ID_STATUS_AWAY && IsConnected()) //go to away while connected { PostIrcMessage(L"/AWAY %s", m_statusMessage.Mid(0, 450).c_str()); return 0; } else if (iNewStatus == ID_STATUS_ONLINE && IsConnected()) //already online return 0; else SetStatusInternal(ID_STATUS_AWAY, true); return 0; } //////////////////////////////////////////////////////////////////////////////////////// // GetAwayMsg - returns a contact's away message HANDLE __cdecl CIrcProto::GetAwayMsg(MCONTACT hContact) { WhoisAwayReply = L""; DBVARIANT dbv; // bypass chat contacts. if (!isChatRoom(hContact)) { if (hContact && !getWString(hContact, "Nick", &dbv)) { int i = getWord(hContact, "Status", ID_STATUS_OFFLINE); if (i != ID_STATUS_AWAY) { db_free(&dbv); return nullptr; } CMStringW S = L"WHOIS "; S += dbv.ptszVal; if (IsConnected()) SendIrcMessage(S.c_str(), false); db_free(&dbv); } } return (HANDLE)1; } //////////////////////////////////////////////////////////////////////////////////////// // SetAwayMsg - sets the away status message int __cdecl CIrcProto::SetAwayMsg(int status, const wchar_t* msg) { switch (status) { case ID_STATUS_ONLINE: case ID_STATUS_INVISIBLE: case ID_STATUS_FREECHAT: case ID_STATUS_CONNECTING: case ID_STATUS_OFFLINE: break; default: CMStringW newStatus = msg; newStatus.Replace(L"\r\n", L" "); if (m_statusMessage.IsEmpty() || msg == nullptr || m_statusMessage != newStatus) { if (msg == nullptr || *msg == 0) m_statusMessage = STR_AWAYMESSAGE; else m_statusMessage = newStatus; if (m_iStatus == ID_STATUS_AWAY) PostIrcMessage(L"/AWAY %s", m_statusMessage.Mid(0, 450).c_str()); } } return 0; } ///////////////////////////////////////////////////////////////////////////////////////// // OnEvent - maintain protocol events int __cdecl CIrcProto::OnEvent(PROTOEVENTTYPE eventType, WPARAM wParam, LPARAM lParam) { switch (eventType) { case EV_PROTO_ONLOAD: return OnModulesLoaded(0, 0); case EV_PROTO_ONEXIT: return OnPreShutdown(0, 0); case EV_PROTO_ONOPTIONS: return OnInitOptionsPages(wParam, lParam); case EV_PROTO_ONMENU: InitMainMenus(); break; case EV_PROTO_ONCONTACTDELETED: return OnContactDeleted(wParam, lParam); case EV_PROTO_DBSETTINGSCHANGED: return OnDbSettingChanged(wParam, lParam); } return 1; }