// ---------------------------------------------------------------------------80 // ICQ plugin for Miranda Instant Messenger // ________________________________________ // // Copyright © 2000,2001 Richard Hughes, Roland Rabien, Tristan Van de Vreede // Copyright © 2001,2002 Jon Keating, Richard Hughes // Copyright © 2002,2003,2004 Martin Öberg, Sam Kothari, Robert Rainwater // Copyright © 2004,2005,2006 Joe Kucera // // 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. // // ----------------------------------------------------------------------------- // // File name : $Source: /cvsroot/miranda/miranda/protocols/IcqOscarJ/chan_04close.c,v $ // Revision : $Revision: 3316 $ // Last change on : $Date: 2006-07-12 20:26:25 +0400 (Ср, 12 июл 2006) $ // Last change by : $Author: jokusoftware $ // // DESCRIPTION: // // Describe me here please... // // ----------------------------------------------------------------------------- #include "icqoscar.h" extern HANDLE hServerConn; extern HANDLE hServerPacketRecver; static void handleMigration(serverthread_info *info); static int connectNewServer(serverthread_info *info); static void handleRuntimeError(WORD wError); static void handleSignonError(WORD wError); static void NetLib_SafeCloseHandle(HANDLE *hConnection); void handleCloseChannel(unsigned char *buf, WORD datalen, serverthread_info *info) { oscar_tlv_chain* chain = NULL; WORD wError; // Parse server reply, prepare reconnection if (!info->bLoggedIn && datalen && !info->newServerReady) handleLoginReply(buf, datalen, info); if (info->isMigrating) handleMigration(info); if (!info->bLoggedIn && info->newServerReady) { if (!connectNewServer(info)) { // Connecting failed if (info->isMigrating) icq_LogUsingErrorCode(LOG_ERROR, GetLastError(), "Unable to connect to migrated ICQ communication server"); else icq_LogUsingErrorCode(LOG_ERROR, GetLastError(), "Unable to connect to ICQ communication server"); info->isMigrating = 0; } info->newServerReady = 0; return; } if (chain = readIntoTLVChain(&buf, datalen, 0)) { // TLV 9 errors (runtime errors?) wError = getWordFromChain(chain, 0x09, 1); if (wError) { SetCurrentStatus(ID_STATUS_OFFLINE); handleRuntimeError(wError); } disposeChain(&chain); } // Server closed connection on error, or sign off NetLib_SafeCloseHandle(&hServerConn); } static int IsGatewayModeActive() { return ICQGetContactSettingByte(NULL, "UseGateway", 0) && ICQGetContactSettingByte(NULL, "NLUseProxy", 0); } static void NetLib_SafeCloseHandle(HANDLE *hConnection) { if (*hConnection) Netlib_MyCloseHandle(*hConnection); *hConnection = NULL; } void handleLoginReply(unsigned char *buf, WORD datalen, serverthread_info *info) { oscar_tlv_chain* chain = NULL; WORD wError; icq_sendCloseConnection(); // imitate icq5 behaviour if (!(chain = readIntoTLVChain(&buf, datalen, 0))) { NetLog_Server("Error: Missing chain on close channel"); NetLib_SafeCloseHandle(&hServerConn); return; // Invalid data } // TLV 8 errors (signon errors?) wError = getWordFromChain(chain, 0x08, 1); if (wError) { handleSignonError(wError); // we return only if the server did not gave us cookie (possible to connect with soft error) if (!getLenFromChain(chain, 0x06, 1)) { disposeChain(&chain); NetLib_SafeCloseHandle(&hServerConn); return; // Failure } } // We are in the login phase and no errors were reported. // Extract communication server info. info->newServer = getStrFromChain(chain, 0x05, 1); info->cookieData = getStrFromChain(chain, 0x06, 1); info->cookieDataLen = getLenFromChain(chain, 0x06, 1); // We dont need this anymore disposeChain(&chain); if (!info->newServer || !info->cookieData) { icq_LogMessage(LOG_FATAL, "You could not sign on because the server returned invalid data. Try again."); SAFE_FREE(&info->newServer); SAFE_FREE(&info->cookieData); info->cookieDataLen = 0; NetLib_SafeCloseHandle(&hServerConn); return; // Failure } NetLog_Server("Authenticated."); info->newServerReady = 1; return; } static int connectNewServer(serverthread_info *info) { WORD servport; NETLIBOPENCONNECTION nloc = {0}; int res = 0; if (!IsGatewayModeActive()) { // close connection only if not in gateway mode Netlib_CloseHandle(hServerConn); hServerConn = NULL; } /* Get the ip and port */ parseServerAddress(info->newServer, &servport); nloc.cbSize = sizeof(nloc); nloc.flags = 0; nloc.szHost = info->newServer; nloc.wPort = servport; if (!IsGatewayModeActive()) { { /* Time to release packet receiver, connection already closed */ Netlib_CloseHandle(hServerPacketRecver); hServerPacketRecver = NULL; // clear the variable NetLog_Server("Closed connection to login server"); } NetLog_Server("Connecting to %s", info->newServer); hServerConn = NetLib_OpenConnection(ghServerNetlibUser, &nloc); if (hServerConn) { /* Time to recreate the packet receiver */ hServerPacketRecver = (HANDLE)CallService(MS_NETLIB_CREATEPACKETRECVER, (WPARAM)hServerConn, 8192); if (!hServerPacketRecver) { NetLog_Server("Error: Failed to create packet receiver."); } else // we need to reset receiving structs { info->bReinitRecver = 1; res = 1; } } } else { // TODO: We should really do some checks here NetLog_Server("Walking in Gateway to %s", info->newServer); // TODO: This REQUIRES more work (most probably some kind of mid-netlib module) icq_httpGatewayWalkTo(hServerConn, &nloc); res = 1; } if (!res) SAFE_FREE(&info->cookieData); // Free allocated memory // NOTE: "cookie" will get freed when we have connected to the communication server. SAFE_FREE(&info->newServer); return res; } static void handleMigration(serverthread_info *info) { // Check the data that was saved when the migration was announced NetLog_Server("Migrating to %s", info->newServer); if (!info->newServer || !info->cookieData) { icq_LogMessage(LOG_FATAL, "You have been disconnected from the ICQ network because the current server shut down."); SAFE_FREE(&info->newServer); SAFE_FREE(&info->cookieData); info->newServerReady = 0; info->isMigrating = 0; } } static void handleSignonError(WORD wError) { switch (wError) { case 0x01: // Unregistered uin case 0x04: // Incorrect uin or password case 0x05: // Mismatch uin or password case 0x06: // Internal Client error (bad input to authorizer) case 0x07: // Invalid account ICQBroadcastAck(NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGINERR_WRONGPASSWORD); icq_LogFatalParam("Connection failed.\nYour ICQ number or password was rejected (%d).", wError); break; case 0x02: // Service temporarily unavailable case 0x0D: // Bad database status case 0x10: // Service temporarily offline case 0x14: // Reservation map error case 0x15: // Reservation link error case 0x1A: // Reservation timeout icq_LogFatalParam("Connection failed.\nThe server is temporarily unavailable (%d).", wError); break; case 0x16: // The users num connected from this IP has reached the maximum case 0x17: // The users num connected from this IP has reached the maximum (reserved) icq_LogFatalParam("Connection failed.\nServer has too many connections from your IP (%d).", wError); break; case 0x18: // Rate limit exceeded (reserved) case 0x1D: // Rate limit exceeded icq_LogFatalParam("Connection failed.\nYou have connected too quickly,\nplease wait and retry 10 to 20 minutes later (%d).", wError); break; case 0x1B: // You are using an older version of ICQ. Upgrade required icq_LogMessage(LOG_FATAL, "Connection failed.\nThe server did not accept this client version."); break; case 0x1C: // You are using an older version of ICQ. Upgrade recommended icq_LogMessage(LOG_WARNING, "The server sent warning, this version is getting old.\nTry to look for a new one."); break; case 0x1E: // Can't register on the ICQ network icq_LogMessage(LOG_FATAL, "Connection failed.\nYou were rejected by the server for an unknown reason.\nThis can happen if the UIN is already connected."); break; case 0x0C: // Invalid database fields, MD5 login not supported icq_LogMessage(LOG_FATAL, "Connection failed.\nSecure (MD5) login is not supported on this account."); break; case 0: break; case 0x08: // Deleted account case 0x09: // Expired account case 0x0A: // No access to database case 0x0B: // No access to resolver case 0x0E: // Bad resolver status case 0x11: // Suspended account case 0x19: // User too heavily warned case 0x22: // Account suspended due to your age case 0x2A: // Blocked account default: icq_LogFatalParam("Connection failed.\nUnknown error during sign on: 0x%02x", wError); break; } } static void handleRuntimeError(WORD wError) { switch (wError) { case 0x01: { ICQBroadcastAck(NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGINERR_OTHERLOCATION); icq_LogMessage(LOG_FATAL, "You have been disconnected from the ICQ network because you logged on from another location using the same ICQ number."); break; } default: icq_LogFatalParam("Unknown runtime error: 0x%02x", wError); break; } }