Main Page | Namespace List | Class Hierarchy | Class List | Directories | File List | Namespace Members | Class Members | File Members

ChatSession.cpp

Go to the documentation of this file.
00001 //
00002 // ChatSession.cpp
00003 //
00004 //      Date:                   "$Date: 2005/10/04 02:42:09 $"
00005 //      Revision:               "$Revision: 1.21 $"
00006 //  Last change by:     "$Author: mogthecat $"
00007 //
00008 // Copyright (c) Shareaza Development Team, 2002-2005.
00009 // This file is part of SHAREAZA (www.shareaza.com)
00010 //
00011 // Shareaza is free software; you can redistribute it
00012 // and/or modify it under the terms of the GNU General Public License
00013 // as published by the Free Software Foundation; either version 2 of
00014 // the License, or (at your option) any later version.
00015 //
00016 // Shareaza is distributed in the hope that it will be useful,
00017 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00018 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019 // GNU General Public License for more details.
00020 //
00021 // You should have received a copy of the GNU General Public License
00022 // along with Shareaza; if not, write to the Free Software
00023 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00024 //
00025 
00026 #include "StdAfx.h"
00027 #include "Shareaza.h"
00028 #include "Settings.h"
00029 #include "GProfile.h"
00030 #include "G2Packet.h"
00031 #include "EDPacket.h"
00032 #include "EDClient.h"
00033 #include "EDClients.h"
00034 #include "Transfers.h"
00035 #include "Network.h"
00036 #include "Buffer.h"
00037 #include "XML.h"
00038 
00039 #include "ChatCore.h"
00040 #include "ChatSession.h"
00041 
00042 #include "ChatWindows.h"
00043 #include "CtrlChatFrame.h"
00044 #include "CtrlPrivateChatFrame.h"
00045 // #include "CtrlPublicChatFrame.h"
00046 
00047 typedef unsigned int UINT_PTR;
00048 #include <mmsystem.h>
00049 
00050 #ifdef _DEBUG
00051 #undef THIS_FILE
00052 static char THIS_FILE[]=__FILE__;
00053 #define new DEBUG_NEW
00054 #endif
00055 
00056 
00058 // CChatSession construction
00059 
00060 CChatSession::CChatSession(CChatFrame* pFrame)
00061 {
00062         m_bGUID                 = FALSE;
00063         
00064         m_nState                = cssNull;
00065         m_nProtocol             = PROTOCOL_NULL;
00066         m_bOld                  = FALSE;
00067         m_bMustPush             = FALSE;
00068         m_tPushed               = 0;
00069         m_pProfile              = NULL;
00070         
00071         m_pWndPrivate   = NULL;
00072         m_pWndPublic    = NULL;
00073 
00074         m_bUnicode              = FALSE;
00075         m_nClientID             = 0;
00076         m_pServer.sin_addr.S_un.S_addr  = 0;
00077         m_pServer.sin_port                              = 0;
00078         
00079         if ( pFrame != NULL )
00080         {
00081                 m_pWndPrivate = ( pFrame->IsKindOf( RUNTIME_CLASS(CPrivateChatFrame) ) )
00082                         ? reinterpret_cast<CPrivateChatFrame*>(pFrame) : NULL;
00083 //              m_pWndPublic = ( pFrame->IsKindOf( RUNTIME_CLASS(CPublicChatFrame) ) )
00084 //                      ? reinterpret_cast<CPublicChatFrame*>(pFrame) : NULL;
00085         }
00086         
00087         ChatCore.Add( this );
00088 }
00089 
00090 CChatSession::~CChatSession()
00091 {
00092         ASSERT( m_hSocket == INVALID_SOCKET );
00093         
00094         if ( m_pProfile != NULL ) delete m_pProfile;
00095         
00096         ChatCore.Remove( this );
00097 }
00098 
00100 // CChatSession setup
00101 
00102 void CChatSession::Setup(GGUID* pGUID, SOCKADDR_IN* pHost, BOOL bMustPush)
00103 {
00104         CSingleLock pLock( &ChatCore.m_pSection, TRUE );
00105         
00106         if ( pGUID != NULL )
00107         {
00108                 m_bGUID = TRUE;
00109                 m_pGUID = *pGUID;
00110         }
00111         
00112         m_pHost         = *pHost;
00113         m_bMustPush     = bMustPush;
00114         
00115         m_sUserNick = inet_ntoa( m_pHost.sin_addr );
00116 }
00117 
00119 // CChatSession connect
00120 
00121 BOOL CChatSession::Connect()
00122 {
00123         CSingleLock pLock1( &ChatCore.m_pSection, TRUE );
00124 
00125         // ED2K Clients have their connection controlled by ED2KClient. (One connection used for many things)
00126         if ( m_nProtocol == PROTOCOL_ED2K ) 
00127         {
00128                 return TRUE;
00129         }
00130                 
00131         
00132         // If we are already connected/handshaking/connecting, don't try again.
00133         if ( m_nState > cssNull ) return FALSE;
00134 
00135         if ( m_bMustPush )
00136         {
00137                 if ( ! SendPush( FALSE ) )
00138                 {
00139                         StatusMessage( 1, IDS_CHAT_CANT_PUSH, (LPCTSTR)CString( inet_ntoa( m_pHost.sin_addr ) ) );
00140                         return FALSE;
00141                 }
00142         }
00143         else
00144         {
00145                 if ( CConnection::ConnectTo( &m_pHost ) )
00146                 {
00147                         ChatCore.Add( this );
00148                         StatusMessage( 0, IDS_CHAT_CONNECTING_TO, (LPCTSTR)m_sAddress );
00149                 }
00150                 else
00151                 {
00152                         StatusMessage( 1, IDS_CHAT_CANT_CONNECT, (LPCTSTR)m_sAddress );
00153                         return FALSE;
00154                 }
00155         }
00156         
00157         m_nState = cssConnecting;
00158         
00159         return TRUE;
00160 }
00161 
00162 TRISTATE CChatSession::GetConnectedState() const
00163 {
00164         if ( m_nState == cssNull ) return TS_FALSE;
00165         if ( m_nState == cssActive ) return TS_TRUE;
00166         return TS_UNKNOWN;
00167 }
00168 
00170 // CChatSession handle an incoming ED2K Message
00171 
00172 void CChatSession::OnED2KMessage(CEDPacket* pPacket)
00173 {
00174         CSingleLock pLock( &ChatCore.m_pSection, TRUE );
00175 
00176         // Open a window (if one is not already open)
00177         PostOpenWindow();
00178 
00179         // Put the packet into the input buffer so it can be 'received' (and displayed) later.
00180         if ( pPacket && m_pInput ) pPacket->ToBuffer( m_pInput );
00181 
00182         // If this client wasn't active, it is now.
00183         if ( m_nState != cssActive )
00184         {
00185                 StatusMessage( 2, IDS_CHAT_PRIVATE_ONLINE, (LPCTSTR)m_sUserNick );
00186                 m_nState = cssActive;
00187                 m_tConnected = GetTickCount();          
00188         }
00189 }
00190 
00192 // CChatSession attach to (accept) an incoming connection
00193 
00194 void CChatSession::AttachTo(CConnection* pConnection)
00195 {       
00196         CConnection::AttachTo( pConnection );
00197 
00198         m_nState = cssRequest1;
00199         ChatCore.Add( this );   
00200 }
00201 
00203 // CChatSession push functionality
00204 
00205 BOOL CChatSession::SendPush(BOOL bAutomatic)
00206 {
00207         if ( ! m_bGUID ) return FALSE;
00208 
00209         if ( m_nProtocol == PROTOCOL_ED2K ) return FALSE;
00210         
00211         if ( Network.SendPush( &m_pGUID, 0 ) )
00212         {
00213                 m_nState = cssNull;
00214                 CConnection::Close();
00215                 
00216                 m_tConnected = m_tPushed = GetTickCount();
00217                 StatusMessage( 0, IDS_CHAT_PUSH_SENT, (LPCTSTR)CString( inet_ntoa( m_pHost.sin_addr ) ) );
00218                 
00219                 return TRUE;
00220         }
00221         else
00222         {
00223                 return FALSE;
00224         }
00225 }
00226 
00227 BOOL CChatSession::OnPush(GGUID* pGUID, CConnection* pConnection)
00228 {
00229         if ( m_tPushed == 0 ) return FALSE;
00230         if ( ! m_bGUID || m_pGUID != *pGUID ) return FALSE;
00231         if ( m_nState > cssConnecting ) return FALSE;
00232         if ( m_nProtocol == PROTOCOL_ED2K ) return FALSE;
00233         
00234         if ( m_nState > cssNull ) Close();
00235         
00236         CConnection::AttachTo( pConnection );
00237         
00238         StatusMessage( 0, IDS_CHAT_PUSH_DONE, (LPCTSTR)m_sAddress );
00239         ChatCore.Add( this );
00240         OnConnected();
00241         
00242         return TRUE;
00243 }
00244 
00246 // CChatSession close
00247 
00248 void CChatSession::Close()
00249 {
00250         // ED2K Clients have their connection controlled by ED2KClient. 
00251         if ( m_nProtocol == PROTOCOL_ED2K ) return;
00252 
00253         CSingleLock pLock( &ChatCore.m_pSection );
00254         pLock.Lock( 250 );
00255         
00256         if ( m_nState != cssNull )
00257         {
00258                 m_nState = cssNull;
00259                 StatusMessage( 0, IDS_CHAT_CLOSED );
00260                 StatusMessage( 0, 0 );
00261         }
00262         
00263         CConnection::Close();
00264         
00265         if ( m_pWndPrivate == NULL && m_pWndPublic == NULL ) delete this;
00266 }
00267 
00269 // CChatSession connection handler
00270 
00271 BOOL CChatSession::OnConnected()
00272 {
00273         StatusMessage( 0, IDS_CHAT_CONNECTED );
00274 
00275         if ( m_nProtocol == PROTOCOL_ED2K ) 
00276         {
00277                 // ED2K connections aren't handled here- they are in ED2KClient
00278         }
00279         else
00280         {
00281                 CConnection::OnConnected();
00282 
00283                 m_pOutput->Print( "CHAT CONNECT/0.2\r\n" );
00284                 m_pOutput->Print( "Accept: text/plain,application/x-gnutella2\r\n" );
00285                 m_pOutput->Print( "User-Agent: " );
00286                 m_pOutput->Print( Settings.SmartAgent() );
00287                 m_pOutput->Print( "\r\n" );
00288                 if ( m_bInitiated ) SendMyAddress();
00289                 m_pOutput->Print( "\r\n" );
00290                 
00291                 m_nState                = cssRequest2;
00292                 m_tConnected    = GetTickCount();
00293                 
00294                 OnWrite();
00295         }
00296         
00297         return TRUE;
00298 }
00299 
00301 // CChatSession disconnection handler
00302 
00303 void CChatSession::OnDropped(BOOL bError)
00304 {
00305         if ( m_hSocket == INVALID_SOCKET ) return;
00306         
00307         if ( m_nState == cssConnecting )
00308         {
00309                 StatusMessage( 1, IDS_CHAT_CANT_CONNECT, (LPCTSTR)m_sAddress );
00310                 if ( m_tPushed == 0 && SendPush( TRUE ) ) return;
00311         }
00312         else
00313         {
00314                 StatusMessage( 1, IDS_CHAT_DROPPED, (LPCTSTR)m_sAddress );
00315         }
00316         
00317         Close();
00318 }
00319 
00321 // CChatSession run handler
00322 
00323 BOOL CChatSession::OnRun()
00324 {               
00325         if ( m_nProtocol == PROTOCOL_ED2K )
00326         {
00327                 // ed2k chat sessions don't have real connections, ED2K Client just puts the packets into
00328                 // the buffer.
00329                 return ( ReadPacketsED2K() && SendPacketsED2K() );
00330         }
00331         else if ( m_nState > cssNull && m_nState < cssActive )
00332         {
00333                 DWORD nDelay = GetTickCount() - m_tConnected;
00334                 
00335                 if ( nDelay >= ( m_nState == cssConnecting ?
00336                         Settings.Connection.TimeoutConnect : Settings.Connection.TimeoutHandshake ) )
00337                 {
00338                         theApp.Message( MSG_ERROR, IDS_HANDSHAKE_TIMEOUT, (LPCTSTR)m_sAddress );
00339                         Close();
00340                         return FALSE;
00341                 }
00342         }
00343 
00344         return TRUE;
00345 }
00346 
00348 // CChatSession read handler
00349 
00350 BOOL CChatSession::OnRead()
00351 {
00352         // ED2K connections aren't handled here, this shouldn't ever be called for ed2k sessions
00353         if ( m_nProtocol == PROTOCOL_ED2K ) return TRUE; 
00354 
00355         CConnection::OnRead();
00356         
00357         switch ( m_nState )
00358         {
00359         case cssRequest1:
00360         case cssRequest2:
00361         case cssRequest3:
00362                 return ReadHandshake();
00363         case cssHeaders1:
00364         case cssHeaders2:
00365         case cssHeaders3:
00366                 return ReadHeaders();
00367         case cssHandshake:
00368         case cssActive:
00369                 if ( m_nProtocol == PROTOCOL_G2 )
00370                         return ReadPackets();
00371                 else
00372                         return ReadText();
00373         }
00374         
00375         return TRUE;
00376 }
00377 
00379 // CChatSession handshake processer
00380 
00381 BOOL CChatSession::ReadHandshake()
00382 {
00383         CString strLine;
00384 
00385         if ( ! m_pInput->ReadLine( strLine ) ) return TRUE;
00386         if ( strLine.IsEmpty() ) return TRUE;
00387         
00388         theApp.Message( MSG_DEBUG, _T("CHAT HANDSHAKE: %s: %s"),
00389                 (LPCTSTR)m_sAddress, (LPCTSTR)strLine );
00390         
00391         m_bOld = strLine.Find( _T("/0.1") ) > 0;
00392         
00393         if ( StartsWith( strLine, _T("CHAT CONNECT/") ) && m_nState == cssRequest1 )
00394         {
00395                 m_nState = cssHeaders1;
00396                 return TRUE;
00397         }
00398         else if ( StartsWith( strLine, _T("CHAT/") ) )
00399         {
00400                 if ( _tcsistr( strLine, _T("200 OK") ) )
00401                 {
00402                         if ( m_nState == cssRequest2 )
00403                         {
00404                                 m_nState = cssHeaders2;
00405                                 return TRUE;
00406                         }
00407                         else if ( m_nState == cssRequest3 )
00408                         {
00409                                 m_nState = cssHeaders3;
00410                                 return TRUE;
00411                         }
00412                 }
00413                 else
00414                 {
00415                         // Rejected
00416                 }
00417         }
00418         
00419         StatusMessage( 1, IDS_CHAT_PRIVATE_REFUSED, (LPCTSTR)m_sAddress );
00420         Close();
00421         
00422         return FALSE;
00423 }
00424 
00426 // CChatSession header processer
00427 
00428 BOOL CChatSession::OnHeaderLine(CString& strHeader, CString& strValue)
00429 {
00430         ASSERT ( m_nProtocol != PROTOCOL_ED2K );
00431 
00432         theApp.Message( MSG_DEBUG, _T("CHAT HEADER: %s: %s: %s"),
00433                 (LPCTSTR)m_sAddress, (LPCTSTR)strHeader, (LPCTSTR)strValue );
00434         
00435         if ( strHeader.CompareNoCase( _T("User-Agent") ) == 0 )
00436         {
00437                 m_sUserAgent = strValue;
00438         }
00439         else if ( strHeader.CompareNoCase( _T("Accept") ) == 0 )
00440         {
00441                 if ( strValue.Find( _T("application/x-gnutella2") ) >= 0 )
00442                         m_nProtocol = PROTOCOL_G2;
00443         }
00444         else if ( strHeader.CompareNoCase( _T("X-Nickname") ) == 0 )
00445         {
00446                 m_sUserNick = strValue;
00447         }
00448         else if (       strHeader.CompareNoCase( _T("X-My-Address") ) == 0 ||
00449                                 strHeader.CompareNoCase( _T("Listen-IP") ) == 0 ||
00450                                 strHeader.CompareNoCase( _T("X-Node") ) == 0 ||
00451                                 strHeader.CompareNoCase( _T("Node") ) == 0 )
00452         {
00453                 int nColon = strValue.Find( ':' );
00454                 
00455                 if ( ! m_bInitiated && nColon > 0 )
00456                 {
00457                         int nPort = GNUTELLA_DEFAULT_PORT;
00458                         
00459                         if ( _stscanf( strValue.Mid( nColon + 1 ), _T("%i"), &nPort ) == 1 && nPort != 0 )
00460                         {
00461                                 m_pHost.sin_port = htons( nPort );
00462                         }
00463                 }
00464         }
00465         
00466         return TRUE;
00467 }
00468 
00469 BOOL CChatSession::OnHeadersComplete()
00470 {
00471         if ( m_nState != cssHeaders3 )
00472         {
00473                 m_pOutput->Print( "CHAT/0.2 200 OK\r\n" );
00474                 
00475                 if ( m_nProtocol == PROTOCOL_G2 )
00476                 {
00477                         m_pOutput->Print( "Accept: application/x-gnutella2\r\n" );
00478                         m_pOutput->Print( "Content-Type: application/x-gnutella2\r\n" );
00479                 }
00480                 else if ( MyProfile.IsValid() )
00481                 {
00482                         m_pOutput->Print( "X-Nickname: " );
00483                         m_pOutput->Print( MyProfile.GetNick().Left( 255 ) );
00484                         m_pOutput->Print( "\r\n" );
00485                 }
00486                 
00487                 m_pOutput->Print( "User-Agent: " );
00488                 m_pOutput->Print( Settings.SmartAgent() );
00489                 m_pOutput->Print( "\r\n" );
00490                 m_pOutput->Print( "\r\n" );
00491                 
00492                 OnWrite();
00493         }
00494         
00495         if ( m_nState == cssHeaders1 )
00496         {
00497                 // Sent second handshake
00498                 m_nState = cssRequest3;
00499                 return TRUE;
00500         }
00501         else if ( m_nState == cssHeaders2 )
00502         {
00503                 // Sent third handshake
00504                 m_nState = cssHandshake;
00505                 return OnEstablished();
00506         }
00507         else if ( m_nState == cssHeaders3 )
00508         {
00509                 // Connected
00510                 m_nState = cssHandshake;
00511                 return OnEstablished();
00512         }
00513         
00514         ASSERT( FALSE );
00515         return FALSE;
00516 }
00517 
00519 // CChatSession startup
00520 
00521 BOOL CChatSession::OnEstablished()
00522 {
00523         ASSERT ( m_nProtocol != PROTOCOL_ED2K );
00524 
00525         m_tConnected = GetTickCount();
00526         
00527         if (  m_nProtocol == PROTOCOL_G2  )
00528         {
00529                 StatusMessage( 0, IDS_CHAT_HANDSHAKE_G2 );
00530                 Send( CG2Packet::New( G2_PACKET_PROFILE_CHALLENGE ) );
00531         }
00532         else
00533         {
00534                 m_nState = cssActive;
00535                 StatusMessage( 2, IDS_CHAT_HANDSHAKE_G1, m_bOld ? _T("0.1") : _T("0.2") );
00536                 if ( m_pWndPrivate != NULL ) m_pWndPrivate->OnProfileReceived();
00537                 StatusMessage( 0, 0 );
00538                 PostOpenWindow();
00539         }
00540         
00541         return TRUE;
00542 }
00543 
00545 // CChatSession ED2K packet interface
00546 
00547 // The ED2K packets are put into the input/output buffers, then handled by ReadPacketsED2K() and
00548 // SendPacketsED2K(). Those functions are called by the OnRun(), since there isn't a valid
00549 // connection/socket associated with an ED2K client chat session. (It's handled by the EDClient)
00550 
00551 BOOL CChatSession::ReadPacketsED2K()
00552 {
00553         BOOL bSuccess = TRUE;
00554         CEDPacket* pPacket;
00555 
00556         ASSERT( m_pInput != NULL );
00557         
00558         while ( pPacket = CEDPacket::ReadBuffer( m_pInput, ED2K_PROTOCOL_EMULE ) )
00559         {
00560                 try
00561                 {
00562                         // Note: This isn't a "real" packet parser. Message packets are simply dumped into 
00563                         // the input buffer by the EDClient, so all packets should be valid ED2K chat messages.
00564                         if ( ( pPacket->m_nEdProtocol == ED2K_PROTOCOL_EDONKEY ) &&
00565                                 ( pPacket->m_nType == ED2K_C2C_MESSAGE ) )
00566                         {
00567                                 bSuccess = OnChatMessage( pPacket );
00568                         }
00569                         else
00570                         {
00571                                 CString str;
00572                                 str.Format( _T("Unrecognised packet - IP: %s - protocol: 0x%x - opcode: 0x%x - in CChatSession::ReadPacketsED2K"), int( pPacket->m_nEdProtocol ), int( pPacket->m_nType ) );
00573                                         LPCTSTR( m_sAddress ), 
00574                                 theApp.Message( MSG_ERROR, LPCTSTR( str ) );
00575                         }
00576                 }
00577                 catch ( CException* pException )
00578                 {
00579                         pException->Delete();
00580                         if ( ! m_bGUID ) bSuccess = FALSE;
00581                 }
00582                 
00583                 pPacket->Release();
00584                 if ( ! bSuccess ) break;
00585         }
00586         
00587         return bSuccess;
00588 }
00589 
00590 BOOL CChatSession::SendPacketsED2K()
00591 {
00592         CEDPacket* pPacket;
00593 
00594         ASSERT( m_pOutput != NULL );
00595 
00596         while ( pPacket = CEDPacket::ReadBuffer( m_pOutput, ED2K_PROTOCOL_EMULE ) )
00597         {
00598                 ASSERT ( pPacket != NULL );
00599 
00600                 // Send the message to the appropriate ED2K Client
00601                 if ( SendChatMessage ( pPacket ) )
00602                 {
00603                         // Packet was sent (or exired and should be removed from the queue). 
00604                         // Release it and continue processing other packets.
00605                         pPacket->Release();
00606                 }
00607                 else
00608                 {
00609                         // The packet could not be sent. Either a lock couldn't be made, or the
00610                         // client is currently connecting.
00611 
00612                         // Put the packet back into the buffer until we are ready to deal with it
00613                         pPacket->ToBuffer( m_pOutput );
00614                         // We're done with the packet (for now), so release it.
00615                         pPacket->Release();
00616                         // Exit this function now. We can't do anything futher, so would get stuck in a loop
00617                         return TRUE;
00618                 }
00619         }
00620 
00621         return TRUE;
00622 }
00623 
00625 // CChatSession ED2K packet handlers
00626 
00627 BOOL CChatSession::SendChatMessage(CEDPacket* pPacket)
00628 {
00629         // Lock the transfers while we send a message (We need the EDClient)
00630         CSingleLock pLock( &Transfers.m_pSection );
00631         if ( ! pLock.Lock( 250 ) ) return FALSE;
00632 
00633         // Try to find an ed2k client
00634         CEDClient* pClient = EDClients.GetByIP( &m_pHost.sin_addr );
00635 
00636         if ( ( pClient ) && ( pClient->m_pGUID == m_pGUID ) )   // Found a client
00637         {       
00638                 
00639                 if ( pClient->IsOnline() )      // We found a client that's ready to go
00640                 {       
00641 
00642                         // If this client wasn't active, it is now.
00643                         if ( m_nState != cssActive )
00644                         {
00645                                 StatusMessage( 2, IDS_CHAT_PRIVATE_ONLINE, (LPCTSTR)m_sUserNick );
00646                                 m_nState = cssActive;
00647                                 m_tConnected = GetTickCount();          
00648                         }
00649                                 
00650                         // Send the packet to the ed2k client, and report the packet should be removed
00651                         pClient->Send ( pPacket, FALSE );
00652                         return TRUE;
00653                 }
00654                 else if ( ( m_nState != cssConnecting ) && ( pClient->Connect() ) )     // We found a client we need to connect to
00655                 {       
00656                         // Set the 'connection' state while we wait for EDClient to do it's job
00657                         m_nState = cssConnecting;
00658                         m_tConnected = GetTickCount();  
00659                         StatusMessage( 0, IDS_CHAT_CONNECTING_TO, (LPCTSTR)pClient->m_sAddress );
00660                         // Return false to out the packet back into the buffer until we're ready to send it
00661                         return FALSE;
00662                 }
00663                 else                                                    
00664                 {       // We found a client but couldn't start a connection.
00665 
00666                         if ( m_nState == cssConnecting )                // If we are connecting
00667                         {
00668                                 // Check time-out
00669                                 if ( ( GetTickCount() - m_tConnected ) >= Settings.Connection.TimeoutConnect )
00670                                 {
00671                                         // We've timed out. Display an error and drop the message
00672                                         StatusMessage( 1, IDS_CHAT_CANT_CONNECT, (LPCTSTR)pClient->m_sAddress );
00673                                         m_nState = cssNull;
00674                                         return TRUE;
00675                                 }
00676                                 else
00677                                 {
00678                                         // Waiting to connect. Put the packet back into the buffer and try later.
00679                                         return FALSE;
00680                                 }
00681                         }
00682                         else                                                                    // We can't connect
00683                         {
00684                                 // There is a problem.  Inform the user and drop the message.
00685                                 StatusMessage( 1, IDS_CHAT_CANT_CONNECT, (LPCTSTR)pClient->m_sAddress );
00686                                 m_nState = cssNull;
00687                                 return TRUE;
00688                         }
00689                 }                                       
00690         }
00691         else // We don't seem to have a client that matches.    
00692         {       
00693 /*
00694                 // Make a new client/connection if we can
00695                 if ( m_nState != cssConnecting )
00696                 {
00697                         // If we aren't connecting, try making a new connection
00698                         // First, lock the section to prevent a problem with other threads
00699                         CSingleLock pLock( &Transfers.m_pSection );
00700                         if ( ! pLock.Lock( 250 ) ) return NULL;
00701 
00702                         // We need to connect to them, so either find or create an EDClient
00703                         if ( m_bMustPush )
00704                                 pClient = EDClients.Connect(m_pHost.sin_addr.S_un.S_addr, m_pHost.sin_port, &m_pServer.sin_addr, m_pServer.sin_port, &m_pGUID );
00705                         else
00706                                 pClient = EDClients.Connect(m_pHost.sin_addr.S_un.S_addr, m_pHost.sin_port, NULL, 0, &m_pGUID );
00707                         // If we weren't able to create a client (Low-id and no server), then exit.
00708 
00709 
00710                         if ( ( pClient ) && ( pClient->Connect() ) )
00711                         {       
00712                                 pClient->OpenChat();
00713                                 pLock.Unlock();
00714                                 // Set the 'connection' state while we wait for EDClient to do it's job
00715                                 m_nState = cssConnecting;
00716                                 m_tConnected = GetTickCount();  
00717                                 StatusMessage( 0, IDS_CHAT_NOT_CONNECTED_1 );
00718                                 // Return false to out the packet back into the buffer until we're ready to send it
00719                                 return FALSE;
00720                         }
00721                 }
00722 */
00723                 // Inform the user and drop the message.
00724                 StatusMessage( 1, IDS_CHAT_DROPPED );
00725                 m_nState = cssNull;
00726                 return TRUE;
00727         }
00728 }
00729 
00730 BOOL CChatSession::OnChatMessage(CEDPacket* pPacket)
00731 {
00732         DWORD nMessageLength;
00733         CString sMessage;
00734 
00735         // Note: The message packet has already been validated by the EDClient.
00736 
00737         // Read message length
00738         nMessageLength = pPacket->ReadShortLE();
00739 
00740         // Read in message
00741         if ( m_bUnicode )
00742                 sMessage = pPacket->ReadStringUTF8( nMessageLength );
00743         else
00744                 sMessage = pPacket->ReadString( nMessageLength );
00745 
00746         // Display message
00747         if ( m_pWndPrivate != NULL ) m_pWndPrivate->OnRemoteMessage( FALSE, sMessage.GetBuffer() );
00748         return TRUE;
00749 }
00750 
00752 // CChatSession text interface
00753 
00754 void CChatSession::Print(LPCTSTR pszString)
00755 {
00756         ASSERT( m_nProtocol != PROTOCOL_G2  );
00757         ASSERT( m_nState >= cssHandshake );
00758         
00759         m_pOutput->Print( pszString );
00760         OnWrite();
00761 }
00762 
00763 BOOL CChatSession::ReadText()
00764 {
00765         CString strLine;
00766         
00767         while ( m_pInput->ReadLine( strLine ) )
00768         {
00769                 if ( ! OnText( strLine ) )
00770                 {
00771                         Close();
00772                         return FALSE;
00773                 }
00774         }
00775         
00776         return TRUE;
00777 }
00778 
00779 BOOL CChatSession::OnText(const CString& str)
00780 {
00781         if ( m_pWndPrivate == NULL ) return TRUE;
00782         
00783         if ( m_bOld )
00784         {
00785                 if ( StartsWith( str, _T("\001ACTION ") ) )
00786                 {
00787                         m_pWndPrivate->OnRemoteMessage( TRUE, str.Mid( 8 ) );
00788                 }
00789                 else
00790                 {
00791                         m_pWndPrivate->OnRemoteMessage( FALSE, str );
00792                 }
00793         }
00794         else if ( StartsWith( str, _T("MESSAGE ") ) )
00795         {
00796                 if ( StartsWith( str, _T("MESSAGE \001ACTION ") ) )
00797                 {
00798                         m_pWndPrivate->OnRemoteMessage( TRUE, str.Mid( 16 ) );
00799                 }
00800                 else
00801                 {
00802                         m_pWndPrivate->OnRemoteMessage( FALSE, str.Mid( 8 ) );
00803                 }
00804         }
00805         else if ( StartsWith( str, _T("NICK ") ) )
00806         {
00807                 // New nick is : str.Mid( 5 )
00808         }
00809         
00810         return TRUE;
00811 }
00812 
00814 // CChatSession Gnutella2 packet interface
00815 
00816 void CChatSession::Send(CG2Packet* pPacket, BOOL bRelease)
00817 {
00818         ASSERT( m_nProtocol == PROTOCOL_G2 );
00819         ASSERT( pPacket != NULL );
00820         ASSERT( m_nState >= cssHandshake );
00821         
00822         pPacket->ToBuffer( m_pOutput );
00823         if ( bRelease ) pPacket->Release();
00824         
00825         OnWrite();
00826 }
00827 
00828 BOOL CChatSession::ReadPackets()
00829 {
00830     BOOL bSuccess = TRUE;
00831         for ( ; bSuccess && m_pInput->m_nLength ; )
00832         {
00833                 BYTE nInput = *( m_pInput->m_pBuffer );
00834                 
00835                 if ( nInput == 0 )
00836                 {
00837                         m_pInput->Remove( 1 );
00838                         continue;
00839                 }
00840                 
00841                 BYTE nLenLen    = ( nInput & 0xC0 ) >> 6;
00842                 BYTE nTypeLen   = ( nInput & 0x38 ) >> 3;
00843                 BYTE nFlags             = ( nInput & 0x07 );
00844                 
00845                 if ( nLenLen == 0 )
00846                 {
00847                         Close();
00848                         return FALSE;
00849                 }
00850                 
00851                 if ( (DWORD)m_pInput->m_nLength < (DWORD)nLenLen + nTypeLen + 2 ) break;
00852                 
00853                 DWORD nLength = 0;
00854                 
00855                 if ( nFlags & G2_FLAG_BIG_ENDIAN )
00856                 {
00857                         BYTE* pLenIn = m_pInput->m_pBuffer + 1;
00858                         
00859                         for ( BYTE nIt = nLenLen ; nIt ; nIt-- )
00860                         {
00861                                 nLength <<= 8;
00862                                 nLength |= *pLenIn++;
00863                         }
00864                 }
00865                 else
00866                 {
00867                         BYTE* pLenIn    = m_pInput->m_pBuffer + 1;
00868                         BYTE* pLenOut   = (BYTE*)&nLength;
00869                         for ( BYTE nLenCnt = nLenLen ; nLenCnt-- ; ) *pLenOut++ = *pLenIn++;
00870                 }
00871                 
00872                 if ( nLength >= Settings.Gnutella1.MaximumPacket )
00873                 {
00874                         Close();
00875                         return FALSE;
00876                 }
00877                 
00878                 if ( (DWORD)m_pInput->m_nLength < (DWORD)nLength + nLenLen + nTypeLen + 2 ) break;
00879                 
00880                 CG2Packet* pPacket = CG2Packet::New( m_pInput->m_pBuffer );
00881                 
00882                 m_pInput->Remove( nLength + nLenLen + nTypeLen + 2 );
00883                 
00884                 try
00885                 {
00886                         bSuccess = OnPacket( pPacket );
00887                 }
00888                 catch ( CException* pException )
00889                 {
00890                         pException->Delete();
00891                         bSuccess = TRUE;
00892                 }
00893                 
00894                 pPacket->Release();
00895         }
00896         
00897         if ( ! bSuccess ) Close();
00898         
00899         return bSuccess;
00900 }
00901 
00903 // CChatSession Gnutella2 packet handlers
00904 
00905 BOOL CChatSession::OnPacket(CG2Packet* pPacket)
00906 {
00907         if ( pPacket->IsType( "CMSG" ) )
00908         {
00909                 return OnChatMessage( pPacket );
00910         }
00911         else if ( pPacket->IsType( G2_PACKET_PROFILE_CHALLENGE ) )
00912         {
00913                 return OnProfileChallenge( pPacket );
00914         }
00915         else if ( pPacket->IsType( G2_PACKET_PROFILE_DELIVERY ) )
00916         {
00917                 return OnProfileDelivery( pPacket );
00918         }
00919         else if ( pPacket->IsType( "CHATREQ" ) )
00920         {
00921                 return OnChatRequest( pPacket );
00922         }
00923         else if ( pPacket->IsType( "CHATANS" ) )
00924         {
00925                 return OnChatAnswer( pPacket );
00926         }
00927         
00928         return TRUE;
00929 }
00930 
00931 BOOL CChatSession::OnProfileChallenge(CG2Packet* pPacket)
00932 {
00933         if ( ! MyProfile.IsValid() ) return TRUE;
00934         
00935         CG2Packet* pProfile = CG2Packet::New( G2_PACKET_PROFILE_DELIVERY, TRUE );
00936         CString strXML = MyProfile.GetXML( NULL, TRUE )->ToString( TRUE );
00937         
00938         pProfile->WritePacket( "XML", pProfile->GetStringLen( strXML ) );
00939         pProfile->WriteString( strXML, FALSE );
00940         
00941         Send( pProfile, TRUE );
00942         
00943         return TRUE;
00944 }
00945 
00946 BOOL CChatSession::OnProfileDelivery(CG2Packet* pPacket)
00947 {
00948         if ( ! pPacket->m_bCompound ) return TRUE;
00949         
00950         if ( m_pProfile != NULL ) delete m_pProfile;
00951         m_pProfile = NULL;
00952         
00953         CHAR szType[9];
00954         DWORD nLength;
00955         
00956         while ( pPacket->ReadPacket( szType, nLength ) )
00957         {
00958                 DWORD nOffset = pPacket->m_nPosition + nLength;
00959                 
00960                 if ( strcmp( szType, "XML" ) == 0 )
00961                 {
00962                         CXMLElement* pXML = CXMLElement::FromString( pPacket->ReadString( nLength ), TRUE );
00963                         
00964                         if ( pXML != NULL )
00965                         {
00966                                 m_pProfile = new CGProfile();
00967                                 
00968                                 if ( ! m_pProfile->FromXML( pXML ) || ! m_pProfile->IsValid() )
00969                                 {
00970                                         delete pXML;
00971                                         delete m_pProfile;
00972                                         m_pProfile = NULL;
00973                                 }
00974                         }
00975                 }
00976                 
00977                 pPacket->m_nPosition = nOffset;
00978         }
00979         
00980         if ( m_pProfile == NULL ) return TRUE;
00981         
00982         m_sUserNick = m_pProfile->GetNick();
00983         
00984         if ( m_bGUID )
00985         {
00986                 if ( m_pGUID != m_pProfile->GUID )
00987                 {
00988                         // ERROR: Its someone else !!
00989                         m_pGUID = m_pProfile->GUID;
00990                 }
00991         }
00992         else
00993         {
00994                 m_bGUID = TRUE;
00995                 m_pGUID = m_pProfile->GUID;
00996         }
00997         
00998         if ( m_pWndPrivate != NULL )
00999         {
01000                 m_pWndPrivate->OnProfileReceived();
01001                 
01002                 CG2Packet* pPacket = CG2Packet::New( "CHATREQ", TRUE );
01003                 pPacket->WritePacket( "USERGUID", 16 );
01004                 pPacket->Write( &m_pGUID, 16 );
01005                 
01006                 Send( pPacket, TRUE );
01007         }
01008         
01009         return TRUE;
01010 }
01011 
01012 BOOL CChatSession::OnChatRequest(CG2Packet* pPacket)
01013 {
01014         if ( ! pPacket->m_bCompound ) return TRUE;
01015         
01016         BOOL bGUID = FALSE;
01017         GGUID pGUID;
01018         
01019         CHAR szType[9];
01020         DWORD nLength;
01021         
01022         while ( pPacket->ReadPacket( szType, nLength ) )
01023         {
01024                 DWORD nOffset = pPacket->m_nPosition + nLength;
01025                 
01026                 if ( strcmp( szType, "USERGUID" ) == 0 && nLength >= 16 )
01027                 {
01028                         pPacket->Read( &pGUID, 16 );
01029                         bGUID = TRUE;
01030                 }
01031                 
01032                 pPacket->m_nPosition = nOffset;
01033         }
01034         
01035         pPacket = CG2Packet::New( "CHATANS", TRUE );
01036         
01037         pPacket->WritePacket( "USERGUID", 16 );
01038         GGUID tmp( MyProfile.GUID );
01039         pPacket->Write( &tmp, 16 );
01040         
01041         if ( bGUID && pGUID == MyProfile.GUID )
01042         {
01043                 pPacket->WritePacket( "ACCEPT", 0 );
01044                 PostOpenWindow();
01045         }
01046         else
01047         {
01048                 pPacket->WritePacket( "DENY", 0 );
01049         }
01050         
01051         Send( pPacket, TRUE );
01052         
01053         return TRUE;
01054 }
01055 
01056 BOOL CChatSession::OnChatAnswer(CG2Packet* pPacket)
01057 {
01058         if ( ! pPacket->m_bCompound ) return TRUE;
01059         
01060         CHAR szType[9];
01061         DWORD nLength;
01062         
01063         while ( pPacket->ReadPacket( szType, nLength ) )
01064         {
01065                 DWORD nOffset = pPacket->m_nPosition + nLength;
01066                 
01067                 if ( strcmp( szType, "ACCEPT" ) == 0 )
01068                 {
01069                         m_nState = cssActive;
01070                         StatusMessage( 2, IDS_CHAT_PRIVATE_ONLINE, (LPCTSTR)m_sUserNick );
01071                         StatusMessage( 0, 0 );
01072                         return TRUE;
01073                 }
01074                 else if ( strcmp( szType, "DENY" ) == 0 )
01075                 {
01076                         StatusMessage( 1, IDS_CHAT_PRIVATE_REFUSED, (LPCTSTR)m_sUserNick );
01077                 }
01078                 else if ( strcmp( szType, "AWAY" ) == 0 )
01079                 {
01080                         CString strAway = pPacket->ReadString( nLength );
01081                         StatusMessage( 1, IDS_CHAT_PRIVATE_AWAY, (LPCTSTR)m_sUserNick,
01082                                 (LPCTSTR)strAway );
01083                 }
01084                 
01085                 pPacket->m_nPosition = nOffset;
01086         }
01087         
01088         Close();
01089         
01090         return FALSE;
01091 }
01092 
01093 BOOL CChatSession::OnChatMessage(CG2Packet* pPacket)
01094 {
01095         if ( ! pPacket->m_bCompound ) return TRUE;
01096         
01097         BOOL bAction = FALSE;
01098         CString strBody;
01099         CHAR szType[9];
01100         DWORD nLength;
01101         
01102         while ( pPacket->ReadPacket( szType, nLength ) )
01103         {
01104                 DWORD nOffset = pPacket->m_nPosition + nLength;
01105                 
01106                 if ( strcmp( szType, "BODY" ) == 0 )
01107                 {
01108                         strBody = pPacket->ReadString( nLength );
01109                 }
01110                 else if ( strcmp( szType, "ACT" ) == 0 )
01111                 {
01112                         bAction = TRUE;
01113                 }
01114                 
01115                 pPacket->m_nPosition = nOffset;
01116         }
01117         
01118         if ( strBody.IsEmpty() ) return TRUE;
01119         
01120         if ( m_pWndPrivate != NULL ) m_pWndPrivate->OnRemoteMessage( bAction, strBody );
01121         
01122         return TRUE;
01123 }
01124 
01126 // CChatSession message interface
01127 
01128 BOOL CChatSession::SendPrivateMessage(BOOL bAction, LPCTSTR pszText)
01129 {
01130         CSingleLock pLock( &ChatCore.m_pSection, TRUE );
01131         
01132         if ( m_nState != cssActive ) return FALSE;
01133 
01134         if ( m_nProtocol == PROTOCOL_G2 )
01135         {
01136                 CG2Packet* pPacket = CG2Packet::New( "CMSG", TRUE );
01137                 
01138                 if ( bAction ) pPacket->WritePacket( "ACT", 0 );
01139                 
01140                 pPacket->WritePacket( "BODY", pPacket->GetStringLen( pszText ) );
01141                 pPacket->WriteString( pszText, FALSE );
01142                 
01143                 Send( pPacket, TRUE );
01144         }
01145         else if ( m_nProtocol == PROTOCOL_ED2K )
01146         {
01147                 // Limit outgoing ed2k messages to shorter than ED2K_MESSAGE_MAX characters, just in case
01148                 CString strMessage = pszText;
01149                 strMessage = strMessage.Left( ED2K_MESSAGE_MAX - 50 );
01150 
01151                 // Create an ed2k packet holding the message
01152                 CEDPacket* pPacket = CEDPacket::New( ED2K_C2C_MESSAGE, ED2K_PROTOCOL_EDONKEY );
01153 
01154                 if ( m_bUnicode )
01155                 {
01156                         pPacket->WriteShortLE( pPacket->GetStringLenUTF8( pszText ) );
01157                         pPacket->WriteStringUTF8( pszText, FALSE );
01158                 }
01159                 else
01160                 {
01161                         pPacket->WriteShortLE( pPacket->GetStringLen( strMessage ) );
01162                         pPacket->WriteString( strMessage, FALSE );
01163                 }
01164 
01165                 // A few asserts for debug purposes
01166                 ASSERT( m_nProtocol == PROTOCOL_ED2K );
01167                 ASSERT( pPacket != NULL );
01168                 ASSERT( pPacket->m_nEdProtocol == ED2K_PROTOCOL_EDONKEY );
01169 
01170                 // Put the packet into the output buffer
01171                 pPacket->ToBuffer( m_pOutput );
01172                 pPacket->Release();
01173         }
01174         else // PROTOCOL_G1
01175         {
01176 
01177                 CString str;
01178                 
01179                 if ( ! m_bOld ) str += _T("MESSAGE ");
01180                 if ( bAction ) str += _T("\001ACTION ");
01181                 str += pszText;
01182                 str += _T("\r\n");
01183                 
01184                 Print( str );
01185         }
01186 
01187         return TRUE;
01188 }
01189 
01191 // CChatSession status message
01192 
01193 void CChatSession::StatusMessage(int nFlags, UINT nID, ...)
01194 {
01195         CString strFormat;
01196         va_list pArgs;
01197         
01198         if ( nID )
01199                 LoadString( strFormat, nID );
01200         else
01201                 strFormat = _T("-");
01202         
01203         va_start( pArgs, nID );
01204         
01205         if ( strFormat.Find( _T("%1") ) >= 0 )
01206         {
01207                 LPTSTR lpszTemp;
01208                 if ( ::FormatMessage( FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ALLOCATE_BUFFER,
01209                         strFormat, 0, 0, (LPTSTR)&lpszTemp, 0, &pArgs ) != 0 && lpszTemp != NULL )
01210                 {
01211                         if ( m_pWndPrivate != NULL ) m_pWndPrivate->OnStatusMessage( nFlags, lpszTemp );
01212                         LocalFree( lpszTemp );
01213                 }
01214         }
01215         else
01216         {
01217                 TCHAR szMessageBuffer[1024];
01218                 _vsntprintf( szMessageBuffer, sizeof( szMessageBuffer ) / sizeof( TCHAR ), strFormat, pArgs );
01219                 szMessageBuffer[ 1023 ] = 0; //truncate here if necessary
01220                 if ( m_pWndPrivate != NULL ) m_pWndPrivate->OnStatusMessage( nFlags, szMessageBuffer );
01221         }
01222         
01223         va_end( pArgs );
01224 }
01225 
01227 // CChatSession chat window interface
01228 
01229 void CChatSession::PostOpenWindow()
01230 {
01231         if ( m_pWndPrivate != NULL || m_pWndPublic != NULL ) return;
01232         
01233         if ( CWnd* pWnd = (CWnd*)theApp.SafeMainWnd() )
01234         {
01235                 pWnd->PostMessage( WM_OPENCHAT, (WPARAM)this );
01236         }
01237 }
01238 
01239 void CChatSession::OnOpenWindow()
01240 {
01241         ASSERT( m_pWndPrivate == NULL && m_pWndPublic == NULL );
01242         
01243         if ( m_bGUID )
01244         {
01245                 m_pWndPrivate = ChatWindows.FindPrivate( &m_pGUID );
01246         }
01247         else
01248         {
01249                 m_pWndPrivate = ChatWindows.FindPrivate( &m_pHost.sin_addr );
01250         }
01251 
01252         if ( ( m_pWndPrivate == NULL ) && ( m_nProtocol == PROTOCOL_ED2K ) )
01253         {
01254                 if ( m_bMustPush )
01255                         m_pWndPrivate = ChatWindows.FindED2KFrame( m_nClientID, &m_pServer );
01256                 else
01257                         m_pWndPrivate = ChatWindows.FindED2KFrame( &m_pHost );
01258         }
01259         
01260         if ( m_pWndPrivate == NULL )
01261         {
01262                 m_pWndPrivate = new CPrivateChatFrame();
01263         }
01264         
01265         if ( ! m_pWndPrivate->Accept( this ) )
01266         {
01267                 m_pWndPrivate = new CPrivateChatFrame();
01268                 m_pWndPrivate->Accept( this );
01269         }
01270         
01271         m_pWndPrivate->OnProfileReceived();
01272         
01273         StatusMessage( 2, IDS_CHAT_PRIVATE_ONLINE, (LPCTSTR)m_sUserNick );
01274         StatusMessage( 0, 0 );
01275         
01276         PlaySound( _T("RAZA_IncomingChat"), NULL, SND_APPLICATION|SND_ALIAS|SND_ASYNC );
01277         
01278         m_nState = cssActive;
01279         
01280         // Hack to open it
01281         
01282         CWnd* pParent = m_pWndPrivate->GetParent();
01283         if ( pParent->IsIconic() ) pParent->ShowWindow( SW_SHOWNORMAL );
01284         pParent->BringWindowToTop();
01285         pParent->SetForegroundWindow();
01286 }
01287 
01288 void CChatSession::OnCloseWindow()
01289 {
01290         m_pWndPrivate = NULL;
01291         m_pWndPublic = NULL;
01292 
01293         Close();
01294 
01295         if ( m_nProtocol == PROTOCOL_ED2K )
01296         {
01297                 if ( m_pProfile != NULL ) delete m_pProfile;
01298                 delete this;
01299         }
01300 }
01301 

Generated on Thu Dec 15 10:39:34 2005 for Shareaza 2.2.1.0 by  doxygen 1.4.2