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

Connection.cpp

Go to the documentation of this file.
00001 //
00002 // Connection.cpp
00003 //
00004 //      Date:                   "$Date: 2005/09/26 00:09:36 $"
00005 //      Revision:               "$Revision: 1.27 $"
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 // CConnection holds a socket used to communicate with a remote computer, and is the root of a big inheritance tree
00027 // http://wiki.shareaza.com/static/Developers.Code.CConnection
00028 
00029 // Copy in the contents of these files here before compiling
00030 #include "StdAfx.h"
00031 #include "Shareaza.h"
00032 #include "Settings.h"
00033 #include "Connection.h"
00034 #include "Network.h"
00035 #include "Security.h"
00036 #include "Buffer.h"
00037 #include "Statistics.h"
00038 
00039 // If we are compiling in debug mode, replace the text "THIS_FILE" in the code with the name of this file
00040 #ifdef _DEBUG
00041 #undef THIS_FILE
00042 static char THIS_FILE[]=__FILE__;
00043 #define new DEBUG_NEW
00044 #endif
00045 
00046 // Hard-coded settings for socket data transfer
00047 #define TEMP_BUFFER 10240 // Transfer data between the socket and a local 10 KB buffer of space
00048 
00049 // Hard-coded settings for the bandwidth transfer meter
00050 #define METER_SECOND  1000  // Used by the bandwidth transfer meter, 1000 milliseconds is 1 second
00051 #define METER_MINIMUM 100   // If the current slot is less than a tenth of a second old, record more bytes transferred there
00052 #define METER_LENGTH  64    // Number of slots in the bandwidth meter, used to be 24, now 64
00053 #define METER_PERIOD  6000  // The bandwidth meter holds onto information for 6 seconds, used to be 2000, now 6000
00054 
00056 // CConnection construction
00057 
00058 // Make a new CConnection object
00059 CConnection::CConnection()
00060 {
00061         // Initialize member variables with null or default values
00062         m_hSocket               = INVALID_SOCKET;       // Set the actual Windows socket to the invalid value
00063         m_pInput                = NULL;                         // Null pointers to input and output CBuffer objects
00064         m_pOutput               = NULL;
00065         m_bInitiated    = FALSE;                        // This connection hasn't been initiated or connected yet
00066         m_bConnected    = FALSE;
00067         m_tConnected    = 0;                            // This will be the tick count when the connection is made
00068         m_nQueuedRun    = 0;                            // DoRun sets it to 0, QueueRun sets it to 2 (do)
00069 
00070         // Zero the memory of the input and output TCPBandwidthMeter objects
00071         ZeroMemory( &m_mInput, sizeof(m_mInput) );
00072         ZeroMemory( &m_mOutput, sizeof(m_mOutput) );
00073 }
00074 
00075 // Delete this CConnection object
00076 CConnection::~CConnection()
00077 {
00078         // Close the socket before the system deletes the object
00079         CConnection::Close();
00080 }
00081 
00083 // CConnection connect to
00084 
00085 // Connect this CConnection object to a remote computer on the Internet
00086 // Takes pHost, a pointer to a SOCKADDR_IN structure, which is MFC's way of holding an IP address and port number
00087 // Returns true if connected
00088 BOOL CConnection::ConnectTo(SOCKADDR_IN* pHost)
00089 {
00090         // Call the next ConnectTo method, and return the result
00091         return ConnectTo( &pHost->sin_addr, htons( pHost->sin_port ) );
00092 }
00093 
00094 // Connect this CConnection object to a remote computer on the Internet
00095 // Takes pAddress, a Windows Sockets structure that holds an IP address, and takes the port number seprately
00096 // Returns true if connected
00097 BOOL CConnection::ConnectTo(IN_ADDR* pAddress, WORD nPort)
00098 {
00099         // Check inputs
00100         if ( m_hSocket != INVALID_SOCKET )              return FALSE; // Make sure the socket isn't already connected somehow
00101         if ( pAddress == NULL || nPort == 0 )   return FALSE; // Make sure we have an address and a nonzero port number
00102         if ( pAddress->S_un.S_addr == 0 )               return FALSE; // S_un.S_addr is the IP as a single unsigned 4-byte long
00103 
00104         // The IP address is in the security list of government and corporate addresses we want to avoid
00105         if ( Security.IsDenied( pAddress ) )
00106         {
00107                 // Report that we aren't connecting to this IP address and return false
00108                 theApp.Message( MSG_ERROR, IDS_NETWORK_SECURITY_OUTGOING, (LPCTSTR)CString( inet_ntoa( *pAddress ) ) );
00109                 return FALSE;
00110         }
00111 
00112         // The IN_ADDR structure we just got passed isn't the same as the one already stored in this object
00113         if ( pAddress != &m_pHost.sin_addr )
00114         {
00115                 // Zero the memory of the entire SOCKADDR_IN structure m_pHost, and then copy in the sin_addr part
00116                 ZeroMemory( &m_pHost, sizeof(m_pHost) );
00117                 m_pHost.sin_addr = *pAddress;
00118         }
00119 
00120         // Fill in more parts of the m_pHost structure
00121         m_pHost.sin_family      = PF_INET;                                                      // PF_INET means just normal IPv4, not IPv6 yet
00122         m_pHost.sin_port        = htons( nPort );                                       // Copy the port number into the m_pHost structure
00123         m_sAddress                      = inet_ntoa( m_pHost.sin_addr );        // Save the IP address as a string of text
00124 
00125         // Create a socket and store it in m_hSocket
00126         m_hSocket = socket(
00127                 PF_INET,                // Normal IPv4, not IPv6
00128                 SOCK_STREAM,    // The two-way sequenced reliable byte streams of TCP, not the datagrams of UDP
00129                 IPPROTO_TCP );  // Again, we want TCP
00130 
00131         // Choose asynchronous, non-blocking reading and writing on our new socket
00132         DWORD dwValue = 1;
00133         ioctlsocket(    // Call Windows Sockets ioctlsocket to control the input/output mode of our new socket
00134                 m_hSocket,      // Give it our new socket
00135                 FIONBIO,        // Select the option for blocking i/o, should the program wait on read and write calls, or keep going?
00136                 &dwValue ); // Nonzero, it should keep going
00137 
00138         // If the OutHost string in connection settings has an IP address written in it
00139         if ( Settings.Connection.OutHost.GetLength() )
00140         {
00141                 // Read the text and copy the IP address and port into a new local MFC SOCKADDR_IN structure called pOutgoing
00142                 SOCKADDR_IN pOutgoing;
00143                 Network.Resolve( Settings.Connection.OutHost, 0, &pOutgoing );
00144 
00145                 // S_addr is the IP address as a single long number, if it's not zero
00146                 if ( pOutgoing.sin_addr.S_un.S_addr )
00147                 {
00148                         // Call bind in Windows Sockets to associate the local address with the socket
00149                         bind(
00150                                 m_hSocket,                              // Our socket
00151                                 (SOCKADDR*)&pOutgoing,  // The IP address this computer appears to have on the Internet (do)
00152                                 sizeof(SOCKADDR_IN) );  // Tell bind how many bytes it can read at the pointer
00153                 }
00154         }
00155 
00156         // Try to connect to the remote computer
00157         if ( WSAConnect(
00158                 m_hSocket,                                      // Our socket
00159                 (SOCKADDR*)&m_pHost,            // The remote IP address and port number
00160                 sizeof(SOCKADDR_IN),            // How many bytes the function can read
00161                 NULL, NULL, NULL, NULL ) )      // No advanced features
00162         {
00163                 // If no error occurs, WSAConnect returns 0, so if we're here an error happened
00164                 UINT nError = WSAGetLastError(); // Get the last Windows Sockets error number
00165 
00166                 // An error of "would block" is normal because connections can't be made instantly and this is a non-blocking socket
00167                 if ( nError != WSAEWOULDBLOCK )
00168                 {
00169                         // The error is something else, record it, close the socket, set the value of m_hSocket, and leave
00170                         theApp.Message( MSG_DEBUG, _T("connect() error 0x%x"), nError );
00171                         closesocket( m_hSocket );
00172                         m_hSocket = INVALID_SOCKET;
00173                         return FALSE;
00174                 }
00175         }
00176 
00177         // Delete the input and output CBuffer objects if they exist, and then create new ones
00178         if ( m_pInput ) delete m_pInput;
00179         if ( m_pOutput ) delete m_pOutput;
00180         m_pInput                = new CBuffer( &Settings.Bandwidth.PeerIn );
00181         m_pOutput               = new CBuffer( &Settings.Bandwidth.PeerOut );
00182 
00183         // Record that we initiated this connection, and when it happened
00184         m_bInitiated    = TRUE;
00185         m_tConnected    = GetTickCount();
00186 
00187         // Record one more outgoing connection in the statistics
00188         Statistics.Current.Connections.Outgoing++;
00189 
00190         // The connection was successfully attempted
00191         return TRUE;
00192 }
00193 
00195 // CConnection accept an incoming connection
00196 
00197 // Called when a remote computer wants to connect to us
00198 // When WSAAccept accepted the connection, it created a new socket hSocket for it and wrote the remote IP in pHost
00199 void CConnection::AcceptFrom(SOCKET hSocket, SOCKADDR_IN* pHost)
00200 {
00201         // Make sure the newly accepted socket is valid
00202         ASSERT( m_hSocket == INVALID_SOCKET );
00203 
00204         // Record the connection information here
00205         m_hSocket               = hSocket;                                                      // Keep the socket here
00206         m_pHost                 = *pHost;                                                       // Copy the remote IP address into this object
00207         m_sAddress              = inet_ntoa( m_pHost.sin_addr );        // Store it as a string also
00208 
00209         // Make new input and output buffer objects
00210         m_pInput                = new CBuffer( &Settings.Bandwidth.PeerIn );
00211         m_pOutput               = new CBuffer( &Settings.Bandwidth.PeerOut );
00212 
00213         // Facts about the connection
00214         m_bInitiated    = FALSE;                        // We didn't initiate this connection
00215         m_bConnected    = TRUE;                         // We're connected right now
00216         m_tConnected    = GetTickCount();       // Record the time this happened
00217 
00218         // Choose asynchronous, non-blocking reading and writing on the new socket
00219         DWORD dwValue = 1;
00220         ioctlsocket( m_hSocket, FIONBIO, &dwValue );
00221 
00222         // Record one more incoming connection in the statistics
00223         Statistics.Current.Connections.Incoming++;
00224 }
00225 
00227 // CConnection attach to an existing connection
00228 
00229 // Call to copy a connection object to this one (do)
00230 // Takes another connection object
00231 void CConnection::AttachTo(CConnection* pConnection)
00232 {
00233         // Make sure the socket isn't valid yet
00234         ASSERT( m_hSocket == INVALID_SOCKET );                          // Make sure the socket here isn't valid yet
00235         ASSERT( pConnection != NULL );                                          // Make sure we got a CConnection object
00236         ASSERT( pConnection->m_hSocket != INVALID_SOCKET ); // And make sure its socket exists
00237 
00238         // Copy values from the given CConnection object to this one
00239         m_pHost                 = pConnection->m_pHost;
00240         m_sAddress              = pConnection->m_sAddress;
00241         m_hSocket               = pConnection->m_hSocket;
00242         m_bInitiated    = pConnection->m_bInitiated;
00243         m_bConnected    = pConnection->m_bConnected;
00244         m_tConnected    = pConnection->m_tConnected;
00245         m_pInput                = pConnection->m_pInput;
00246         m_pOutput               = pConnection->m_pOutput;
00247         m_sUserAgent    = pConnection->m_sUserAgent; // But, we don't also copy across m_mInput and m_mOutput
00248 
00249         // Record the current time in the input and output TCP bandwidth meters
00250         m_mInput.tLast = m_mOutput.tLast = GetTickCount();
00251 
00252         // Invalidate the socket in the given connection object so it's just here now
00253         pConnection->m_hSocket  = INVALID_SOCKET;
00254 
00255         // Null the input and output pointers
00256         pConnection->m_pInput   = NULL;
00257         pConnection->m_pOutput  = NULL;
00258 
00259         // Zero the memory of the input and output TCPBandwidthMeter objects
00260         ZeroMemory( &pConnection->m_mInput, sizeof(m_mInput) );
00261         ZeroMemory( &pConnection->m_mOutput, sizeof(m_mOutput) );
00262 }
00263 
00265 // CConnection close
00266 
00267 // Call to close the connection represented by this object
00268 void CConnection::Close()
00269 {
00270         // Make sure this object exists, and is located entirely within the program's memory space
00271         ASSERT( this != NULL );
00272         ASSERT( AfxIsValidAddress( this, sizeof(*this) ) );
00273 
00274         // The socket is valid
00275         if ( m_hSocket != INVALID_SOCKET )
00276         {
00277                 // Close it and mark it invalid
00278                 closesocket( m_hSocket );
00279                 m_hSocket = INVALID_SOCKET;
00280         }
00281 
00282         // Delete and mark null the input and output buffers
00283         if ( m_pOutput ) delete m_pOutput;
00284         m_pOutput = NULL;
00285         if ( m_pInput ) delete m_pInput;
00286         m_pInput = NULL;
00287 
00288         // This connection object isn't connected any longer
00289         m_bConnected = FALSE;
00290 }
00291 
00293 // CConnection run function
00294 
00295 // Talk to the other computer, write the output buffer to the socket and read from the socket to the input buffer
00296 // Return true if this worked, false if we've lost the connection
00297 BOOL CConnection::DoRun()
00298 {
00299         // If this socket is invalid, call OnRun and return the result (do)
00300         if ( m_hSocket == INVALID_SOCKET ) return OnRun();
00301 
00302         // Setup pEvents to store the socket's internal information about network events
00303         WSANETWORKEVENTS pEvents;
00304         WSAEnumNetworkEvents( m_hSocket, NULL, &pEvents );
00305 
00306         // If the FD_CONNECT network event has occurred
00307         if ( pEvents.lNetworkEvents & FD_CONNECT )
00308         {
00309                 // If there is a nonzero error code for the connect operation
00310                 if ( pEvents.iErrorCode[ FD_CONNECT_BIT ] != 0 )
00311                 {
00312                         // This connection was dropped
00313                         OnDropped( TRUE );
00314                         return FALSE;
00315                 }
00316 
00317                 // The socket is now connected
00318                 m_bConnected = TRUE;
00319                 m_tConnected = m_mInput.tLast = m_mOutput.tLast = GetTickCount(); // Store the time 3 places
00320 
00321                 // Call CShakeNeighbour::OnConnected to start reading the handshake
00322                 if ( ! OnConnected() ) return FALSE;
00323         }
00324 
00325         // If the FD_CLOSE network event has occurred, set bClosed to true, otherwise set it to false
00326         BOOL bClosed = ( pEvents.lNetworkEvents & FD_CLOSE ) ? TRUE : FALSE;
00327 
00328         // If the close event happened, null a pointer within the TCP bandwidth meter for input (do)
00329         if ( bClosed ) m_mInput.pLimit = NULL;
00330 
00331         // Change the queued run state to 1 (do)
00332         m_nQueuedRun = 1;
00333 
00334         // Write the contents of the output buffer to the remote computer, and read in data it sent us
00335         if ( ! OnWrite() ) return FALSE; // If this is a CShakeNeighbour object, calls CShakeNeighbour::OnWrite
00336         if ( ! OnRead() ) return FALSE;
00337 
00338         // If the close event happened
00339         if ( bClosed )
00340         {
00341                 /*
00342                 if ( pEvents.iErrorCode[ FD_CLOSE_BIT ] != 0 )
00343                 {
00344                         CString strError;
00345                         strError.Format( _T("Close Error: %i "),  pEvents.iErrorCode[ FD_CLOSE_BIT ] );
00346                         theApp.Message(MSG_ERROR, strError );
00347                 }
00348                 */
00349                 // Call OnDropped, telling it true if there is a close error
00350                 OnDropped( pEvents.iErrorCode[ FD_CLOSE_BIT ] != 0 ); // True if there is an nonzero error code for the close bit
00351                 return FALSE;
00352         }
00353 
00354         // Make sure the handshake doesn't take too long
00355         if ( ! OnRun() ) return FALSE; // If this is a CShakeNeighbour object, calls CShakeNeighbour::OnRun
00356 
00357         // If the queued run state is 2 and OnWrite returns false, leave here with false also
00358         if ( m_nQueuedRun == 2 && ! OnWrite() ) return FALSE;
00359 
00360         // Change the queued run state back to 0 and report success (do)
00361         m_nQueuedRun = 0;
00362         return TRUE;
00363 }
00364 
00365 // Call OnWrite if DoRun has just succeeded (do)
00366 void CConnection::QueueRun()
00367 {
00368         // If the queued run state is 1 or 2, make it 2, if it's 0, call OnWrite now (do)
00369         if ( m_nQueuedRun )     m_nQueuedRun = 2;       // The queued run state is not 0, make it 2 and do nothing else here
00370         else                            OnWrite();                      // The queued run state is 0, do a write (do)
00371 }
00372 
00374 // CConnection socket event handlers
00375 
00376 // Objects that inherit from CConnection have OnConnected methods that do things, unlike this one
00377 BOOL CConnection::OnConnected()
00378 {
00379         // Just return true
00380         return TRUE;
00381 }
00382 
00383 // Objects that inherit from CConnection have OnDropped methods that do things, unlike this one
00384 void CConnection::OnDropped(BOOL bError)
00385 {
00386         // Do nothing
00387 }
00388 
00389 // Objects that inherit from CConnection have OnRun methods that do things, unlike this one
00390 BOOL CConnection::OnRun()
00391 {
00392         // Just return true
00393         return TRUE;
00394 }
00395 
00397 // CConnection read event handler
00398 
00399 // Read data waiting in the socket into the input buffer
00400 BOOL CConnection::OnRead()
00401 {
00402         // Make sure the socket is valid
00403         if ( m_hSocket == INVALID_SOCKET ) return FALSE;
00404 
00405         // Setup local variables
00406         BYTE pData[TEMP_BUFFER];                        // Make a 10 KB data buffer called pData
00407         DWORD tNow              = GetTickCount();       // The time right now
00408         DWORD nLimit    = 0xFFFFFFFF;           // Make the limit huge
00409         DWORD nTotal    = 0;                            // Start the total at 0
00410 
00411         // If we need to worry about throttling bandwidth, calculate nLimit, the number of bytes we are allowed to read now
00412         if ( m_mInput.pLimit                                                    // If the input bandwidth memter points to a limit
00413                 && *m_mInput.pLimit                                                     // And that limit isn't 0
00414                 && ( Settings.Live.BandwidthScale <= 100        // And either the bandwidth meter in program settings is (do)
00415                 || m_mInput.bUnscaled ) )                                       // Or the input bandwidth meter in this object is unscaled (do)
00416         {
00417                 // tCutoff is the tick count 1 second ago
00418                 DWORD tCutoff = tNow - METER_SECOND; // METER_SECOND is 1 second
00419 
00420                 // nLimit is the number of bytes we've read in the last second
00421                 // This part of the code has been translated into assembly language to make it faster
00422 /*
00423                 // Loop across the first 24 times and histories stored in the input bandwidth meter
00424                 DWORD* pHistory = m_mInput.pHistory;    // Start the pointers at the beginning of the arrays
00425                 DWORD* pTime    = m_mInput.pTimes;
00426                 nLimit                  = 0;                                    // Count up the limit from 0
00427                 for ( int nSeek = METER_LENGTH ; nSeek ; nSeek--, pHistory++, pTime++ )
00428                 {
00429                         // If this time is within the last second, add its bytes to the limit
00430                     if ( *pTime >= tCutoff ) nLimit += *pHistory;
00431                 }
00432 */
00433                 // Here is the assembly language which does the same thing
00434                 __asm
00435                 {
00436                         mov             ecx, this
00437                         mov             ebx, -METER_LENGTH
00438                         mov             edx, tCutoff
00439                         xor             eax, eax
00440 _loop:          cmp             edx, [ ecx + ebx * 4 ]CConnection.m_mInput.pTimes + METER_LENGTH * 4
00441                         jnbe    _ignore
00442                         add             eax, [ ecx + ebx * 4 ]CConnection.m_mInput.pHistory + METER_LENGTH * 4
00443 _ignore:        inc             ebx
00444                         jnz             _loop
00445                         mov             nLimit, eax
00446                 }
00447 
00448                 // nActual is the speed limit, set by the input bandwidth meter and the bandwidth scale in settings
00449                 DWORD nActual = *m_mInput.pLimit; // Get the speed limit from the input bandwidth meter
00450                 if ( Settings.Live.BandwidthScale < 100 && ! m_mInput.bUnscaled ) // The scale is turned down and we should use it
00451                 {
00452                         // Adjust actual based on the scale
00453                         nActual = nActual * Settings.Live.BandwidthScale / 100; // If the settings scale is 50, this cuts actual in half
00454                 }
00455 
00456                 // tCutoff is the number of bytes we can read now, multiply the speed limit with the elapsed time to get it
00457                 tCutoff = nActual * ( tNow - m_mInput.tLastAdd ) / 1000; // Subtract tick counts and divide by 1000 to get seconds
00458                 m_mInput.tLastAdd = tNow; // Record that the last add in the input bandwidth meter happened now
00459 
00460                 // nActual is the speed limit in bytes per second
00461                 // nLimit is how many bytes we read in the last second
00462                 // So, nActual - nLimit is the number of bytes we can still read this second
00463                 // Set nLimit to this, or 0 if we're over the limit
00464                 nLimit = ( nLimit >= nActual ) ? 0 : ( nActual - nLimit );
00465 
00466                 // If the cutoff is even more restrictive than the limit, make it the limit instead
00467                 // nLimit = speed limit in bytes per second - bytes we read in the last second
00468                 // tCutoff = speed limit in bytes per second * elapsed time
00469                 nLimit = min( nLimit, tCutoff );
00470         }
00471 
00472         // Loop until the limit has run out
00473         while ( nLimit )
00474         {
00475                 // Set nLength to nLimit or 4 KB, whichever is smaller
00476                 int nLength = min( ( nLimit & 0xFFFFF ), DWORD(TEMP_BUFFER) );
00477 
00478                 // Read the bytes from the socket
00479                 nLength = recv(         // nLength is the number of bytes we received from the socket
00480                         m_hSocket,              // Use the socket in this CConnection object
00481                         (char*)pData,   // Tell recv to write the data here
00482                         nLength,                // The size of the buffer, and how many bytes recv should write there
00483                         0 );                    // No special options
00484                 if ( nLength <= 0 ) break; // Loop until nothing is left or an error occurs
00485 
00486                 // Record the time of this read in the input bandwidth meter
00487                 m_mInput.tLast = tNow;
00488 
00489                 // If the length is positive and up to 4 KB
00490                 if ( nLength > 0 && nLength <= TEMP_BUFFER )
00491                 {
00492                         // Add the data to the input buffer in the connection object
00493                         m_pInput->Add( pData, nLength );
00494                 }
00495 
00496                 // Include these new bytes in the total
00497                 nTotal += nLength;
00498 
00499                 // If we are keeping track of the limit, subtract the bytes we just read from it
00500                 if ( nLimit != 0xFFFFFFFF ) nLimit -= nLength;
00501         }
00502 
00503         // If we are keeping track of history and we just read some bytes
00504         if ( m_mInput.pHistory && nTotal )
00505         {
00506                 // If it's less than 1/10 of a second since we last wrote some history information
00507                 if ( tNow - m_mInput.tLastSlot < METER_MINIMUM )
00508                 {
00509                         // Use the same place in the array as before
00510                         m_mInput.pHistory[ m_mInput.nPosition ] += nTotal;
00511 
00512                 } // It's been more than a tenth of a second since we last recorded a read
00513                 else
00514                 {
00515                         // Store the time and total in a new array slot
00516                         m_mInput.nPosition = ( m_mInput.nPosition + 1 ) % METER_LENGTH; // Move to the next position in the array
00517                         m_mInput.pTimes[ m_mInput.nPosition ] = tNow;                                   // Record the new time
00518                         m_mInput.pHistory[ m_mInput.nPosition ] = nTotal;                               // Store the bytes read next to it
00519                         m_mInput.tLastSlot = tNow;                                                                              // We just wrote some history information
00520                 }
00521         }
00522 
00523         // Add the bytes we read to the total in the bandwidth meter and the total in statistics
00524         m_mInput.nTotal += nTotal;
00525         Statistics.Current.Bandwidth.Incoming += nTotal;
00526         return TRUE;
00527 }
00528 
00530 // CConnection write event handler
00531 
00532 // Call to send the contents of the output buffer to the remote computer
00533 BOOL CConnection::OnWrite()
00534 {
00535         // Make sure the socket is valid and there is something to send
00536         if ( m_hSocket == INVALID_SOCKET ) return FALSE;
00537         if ( m_pOutput->m_nLength == 0 ) return TRUE; // There is nothing to send, so we succeed without doing anything
00538 
00539         // Setup local variables
00540         DWORD tNow              = GetTickCount();       // The time right now
00541         DWORD nLimit    = 0xFFFFFFFF;           // Make the limit huge
00542 
00543         // If we need to worry about throttling bandwidth, calculate nLimit, the number of bytes we are allowed to write now
00544         if ( m_mOutput.pLimit                                                   // If the output bandwidth meter points to a limit
00545                 && *m_mOutput.pLimit                                            // And that limit isn't 0
00546                 && ( Settings.Live.BandwidthScale <= 100        // And either the bandwidth meter in program settings is (do)
00547                 || m_mOutput.bUnscaled ) )                                      // Or the input bandwidth meter in this object is unscaled (do)
00548         {
00549                 // tCutoff is the tick count 1 second ago
00550                 DWORD tCutoff = tNow - METER_SECOND; // METER_SECOND is 1 second
00551 
00552                 // nUsed is the number of bytes we've written in the last second
00553                 // This part of the code has been translated into assembly language to make it faster
00554 /*
00555                 // Loop across the first 24 times and histories stored in the output bandwidth meter
00556                 DWORD* pHistory = m_mOutput.pHistory;   // Start the pointers at the beginning of the arrays
00557                 DWORD* pTime    = m_mOutput.pTimes;
00558                 DWORD nUsed             = 0;                                    // Count up used from 0
00559                 for ( int nSeek = METER_LENGTH ; nSeek ; nSeek--, pHistory++, pTime++ )
00560                 {
00561                         // If this time is within the last second, add its bytes to used
00562                         if ( *pTime >= tCutoff ) nUsed += *pHistory;
00563                 }
00564 */
00565                 // Here is the assembly language which does the same thing
00566                 DWORD nUsed;
00567                 __asm
00568                 {
00569                         mov             ecx, this
00570                         mov             ebx, -METER_LENGTH
00571                         mov             edx, tCutoff
00572                         xor             eax, eax
00573 _loop:          cmp             edx, [ ecx + ebx * 4 ]CConnection.m_mOutput.pTimes + METER_LENGTH * 4
00574                         jnbe    _ignore
00575                         add             eax, [ ecx + ebx * 4 ]CConnection.m_mOutput.pHistory + METER_LENGTH * 4
00576 _ignore:        inc             ebx
00577                         jnz             _loop
00578                         mov             nUsed, eax
00579                 }
00580 
00581                 // nLimit is the speed limit, set by the output bandwidth meter and the bandwidth scale in settings
00582                 nLimit = *m_mOutput.pLimit; // Get the speed limit from the output bandwidth meter
00583                 if ( Settings.Live.BandwidthScale < 100 && ! m_mOutput.bUnscaled ) // The scale is turned down and we should use it
00584                 {
00585                         // Adjust the limit lower based on the scale
00586                         nLimit = nLimit * Settings.Live.BandwidthScale / 100;
00587                 }
00588 
00589                 // The program is running in throttle mode
00590                 if ( Settings.Uploads.ThrottleMode )
00591                 {
00592                         // Set nLimit to the remaining bytes we're allowd to write this second
00593                         nLimit = ( nUsed >= nLimit ) ? 0 : ( nLimit - nUsed );
00594 
00595                 } // The program is not running in throttle mode
00596                 else
00597                 {
00598                         // tCutoff is the number of bytes we can write now, multiply the speed limit with the elapsed time to get it
00599                         tCutoff = nLimit * ( tNow - m_mOutput.tLastAdd ) / 1000;
00600 
00601                         // Set nLimit to the remaining bytes we're allowd to write this second
00602                         nLimit = ( nUsed >= nLimit ) ? 0 : ( nLimit - nUsed );
00603 
00604                         // If the cutoff is even more restrictive than the limit, make it the limit instead
00605                         // nLimit = speed limit in bytes per second - bytes we read in the last second
00606                         // tCutoff = speed limit in bytes per second * elapsed time
00607                         nLimit = min( nLimit, tCutoff );
00608 
00609                         // Record that the last add in the input bandwidth meter happened now
00610                         m_mOutput.tLastAdd = tNow;
00611                 }
00612         }
00613 
00614         // Point pBuffer at the start of the output buffer, and set nBuffer to its length
00615         BYTE* pBuffer = m_pOutput->m_pBuffer;
00616         DWORD nBuffer = m_pOutput->m_nLength;
00617 
00618         // Loop while we're under our limit and there are still bytes to write
00619         while ( nLimit && nBuffer )
00620         {
00621                 // nLength is the number of bytes we will write, set it to the limit or the length, whichever is smaller
00622                 int nLength = (int)min( nLimit, nBuffer );
00623 
00624                 // Send the bytes to the other computer through the socket
00625                 nLength = send(         // The send function returns how many bytes it sent
00626                         m_hSocket,              // Use the socket in this CConnection object
00627                         (char*)pBuffer, // Tell send to read the data here
00628                         nLength,                // This is how many bytes are there to send
00629                         0 );                    // No special options
00630                 if ( nLength <= 0 ) break; // Loop until nothing is left or an error occurs
00631 
00632                 // Move the pointer forward past the sent bytes, and subtract their size from the length
00633                 pBuffer += nLength;
00634                 nBuffer -= nLength;
00635 
00636                 // If we are keeping track of bandwidth, subtract the size of what we wrote from the limit
00637                 if ( nLimit != 0xFFFFFFFF ) nLimit -= nLength;
00638         }
00639 
00640         // The total number of bytes written is the length of the output buffer minus any bytes left still to write
00641         DWORD nTotal = ( m_pOutput->m_nLength - nBuffer );
00642 
00643         // We wrote some bytes into the socket
00644         if ( nTotal )
00645         {
00646                 // Remove them from the output buffer, otherwise they would sit there and get sent again the next time
00647                 m_pOutput->Remove( nTotal );
00648                 m_mOutput.tLast = tNow; // Record that we last wrote now
00649 
00650                 // If it's less than 1/10 of a second since we last wrote some bandwidth history information
00651                 if ( tNow - m_mOutput.tLastSlot < METER_MINIMUM )
00652                 {
00653                         // Just add the bytes in the same time slot as before
00654                         m_mOutput.pHistory[ m_mOutput.nPosition ] += nTotal;
00655 
00656                 } // It's been more than a tenth of a second since we last recorded a write
00657                 else
00658                 {
00659                         // Store the time and total in a new array slot
00660                         m_mOutput.nPosition = ( m_mOutput.nPosition + 1 ) % METER_LENGTH;       // Move to the next position in the array
00661                         m_mOutput.pTimes[ m_mOutput.nPosition ] = tNow;                                         // Record the new time
00662                         m_mOutput.pHistory[ m_mOutput.nPosition ] = nTotal;                                     // Store the bytes written next to it
00663                         m_mOutput.tLastSlot = tNow;                                                                                     // Mark down when we did this
00664                 }
00665 
00666                 // Add the bytes we read to the total in the bandwidth meter and the total in statistics
00667                 m_mOutput.nTotal += nTotal;
00668                 Statistics.Current.Bandwidth.Outgoing += nTotal;
00669         }
00670 
00671         // Report success
00672         return TRUE;
00673 }
00674 
00676 // CConnection measure
00677 
00678 // Calculate the input and output speeds for this connection
00679 void CConnection::Measure()
00680 {
00681         // Set tCutoff to the tick count exactly 2 seconds ago
00682         DWORD tCutoff = GetTickCount() - METER_PERIOD; // METER_PERIOD is 6 seconds
00683 
00684         // This part of the code has been translated into assembly language to make it faster
00685 /*
00686         // Point to the start of the input and output history and time arrays
00687         DWORD* pInHistory       = m_mInput.pHistory;
00688         DWORD* pInTime          = m_mInput.pTimes;
00689         DWORD* pOutHistory      = m_mOutput.pHistory;
00690         DWORD* pOutTime         = m_mOutput.pTimes;
00691 
00692         // Start counts at zero
00693         DWORD nInput    = 0;
00694         DWORD nOutput   = 0;
00695 
00696         // Loop down the arrays
00697         for ( int tNow = METER_LENGTH ; tNow ; tNow-- )
00698         {
00699                 // If this record is from the last 2 seconds, add it to the input or output total
00700                 if ( *pInTime >= tCutoff ) nInput += *pInHistory;
00701                 if ( *pOutTime >= tCutoff ) nOutput += *pOutHistory;
00702 
00703                 // Move pointers forward to the next spot in the array
00704                 pInHistory++, pInTime++;
00705                 pOutHistory++, pOutTime++;
00706         }
00707 
00708         // Set nMeasure for input and output as the average bytes read and written per second over the last 2 seconds
00709         m_mInput.nMeasure  = nInput  * 1000 / METER_PERIOD;
00710         m_mOutput.nMeasure = nOutput * 1000 / METER_PERIOD;
00711 */
00712         // Here is the assembly language which does the same thing
00713         __asm
00714         {
00715                 mov             ebx, this
00716                 mov             edx, tCutoff
00717                 xor             eax, eax
00718                 xor             esi, esi
00719                 mov             ecx, -METER_LENGTH
00720 _loop:  cmp             edx, [ ebx + ecx * 4 ]CConnection.m_mInput.pTimes + METER_LENGTH * 4
00721                 jnbe    _ignoreIn
00722                 add             eax, [ ebx + ecx * 4 ]CConnection.m_mInput.pHistory + METER_LENGTH * 4
00723 _ignoreIn:cmp   edx, [ ebx + ecx * 4 ]CConnection.m_mOutput.pTimes + METER_LENGTH * 4
00724                 jnbe    _ignoreOut
00725                 add             esi, [ ebx + ecx * 4 ]CConnection.m_mOutput.pHistory + METER_LENGTH * 4
00726 _ignoreOut:inc  ecx
00727                 jnz             _loop
00728                 xor             edx, edx
00729                 mov             ecx, METER_PERIOD / 1000
00730                 div             ecx
00731                 mov             [ ebx ]CConnection.m_mInput.nMeasure, eax
00732                 xor             edx, edx
00733                 mov             eax, esi
00734                 div             ecx
00735                 mov             [ ebx ]CConnection.m_mOutput.nMeasure, eax
00736         }
00737 }
00738 
00740 // CConnection HTML header reading
00741 
00742 // Remove the headers from the input buffer, handing each to OnHeaderLine
00743 BOOL CConnection::ReadHeaders()
00744 {
00745         // Move the first line from the m_pInput buffer to strLine and do the contents of the while loop
00746         CString strLine;
00747         while ( m_pInput->ReadLine( strLine ) ) // ReadLine will return false when there are no more lines
00748         {
00749                 // If the line is more than 20 KB, change it to the line too long error code 
00750                 if ( strLine.GetLength() > 20480 ) strLine = _T("#LINE_TOO_LONG#");
00751 
00752                 // Find the first colon in the line
00753                 int nPos = strLine.Find( _T(":") );
00754 
00755                 // The line is empty, it's just a \n character
00756                 if ( strLine.IsEmpty() )
00757                 {
00758                         // Empty the last header member variable (do)
00759                         m_sLastHeader.Empty();
00760 
00761                         // Call the OnHeadersComplete method for the most advanced class that inherits from CConnection
00762                         return OnHeadersComplete(); // Calls CShakeNeighbour::OnHeadersComplete()
00763 
00764                 } // The line starts with a space
00765                 else if ( _istspace( strLine.GetAt( 0 ) ) ) // Get the first character in the string, and see if its a space
00766                 {
00767                         // The last header has length
00768                         if ( m_sLastHeader.GetLength() )
00769                         {
00770                                 // Trim the spaces from both ends of the line, and see if it still has length
00771                                 strLine.TrimLeft();
00772                                 strLine.TrimRight();
00773                                 if ( strLine.GetLength() > 0 )
00774                                 {
00775                                         // Give OnHeaderLine the last header and this line
00776                                         if ( ! OnHeaderLine( m_sLastHeader, strLine ) ) return FALSE;
00777                                 }
00778                         }
00779 
00780                 } // The colon is at a distance greater than 1 and less than 64
00781                 else if ( nPos > 1 && nPos < 64 ) // ":a" is 0 and "a:a" is 1, but "aa:a" is greater than 1
00782                 {
00783                         // The line is like "header:value", copy out both parts
00784                         m_sLastHeader           = strLine.Left( nPos );
00785                         CString strValue        = strLine.Mid( nPos + 1 );
00786 
00787                         // Trim spaces from both ends of the value, and see if it still has length
00788                         strValue.TrimLeft();
00789                         strValue.TrimRight();
00790                                 // Give OnHeaderLine this last header, and its value
00791                                 if ( ! OnHeaderLine( m_sLastHeader, strValue ) ) return FALSE; // Calls CShakeNeighbour::OnHeaderLine
00792                         }
00793                 }
00794 
00795         // Send the contents of the output buffer to the remote computer
00796         OnWrite();
00797         return TRUE;
00798 }
00799 
00800 // Takes a header and its value
00801 // Reads and processes popular Gnutella headers
00802 // Returns true to have ReadHeaders keep going
00803 BOOL CConnection::OnHeaderLine(CString& strHeader, CString& strValue)
00804 {
00805         // It's the user agent header
00806         if ( strHeader.CompareNoCase( _T("User-Agent") ) == 0 )
00807         {
00808                 // Copy the value into the user agent member string
00809                 m_sUserAgent = strValue; // This tells what software the remote computer is running
00810                 return TRUE;             // Have ReadHeaders keep going
00811         
00812         } // It's the remote IP header
00813         else if ( strHeader.CompareNoCase( _T("Remote-IP") ) == 0 )
00814         {
00815                 // Add this address to our record of them
00816                 Network.AcquireLocalAddress( strValue );
00817         
00818         } // It's the x my address, listen IP, or node header, like "X-My-Address: 10.254.0.16:6349"
00819         else if (       strHeader.CompareNoCase( _T("X-My-Address") ) == 0 ||
00820                                 strHeader.CompareNoCase( _T("Listen-IP") ) == 0 ||
00821                                 strHeader.CompareNoCase( _T("X-Node") ) == 0 ||
00822                                 strHeader.CompareNoCase( _T("Node") ) == 0 )
00823         {
00824                 // Find another colon in the value
00825                 int nColon = strValue.Find( ':' );
00826 
00827                 // If the remote computer first contacted us and the colon is there but not first
00828                 if ( ! m_bInitiated && nColon > 0 )
00829                 {
00830                         // Read the number after the colon into nPort
00831                         int nPort = GNUTELLA_DEFAULT_PORT; // Start out nPort as the default value, 6346
00832                         if ( _stscanf( strValue.Mid( nColon + 1 ), _T("%lu"), &nPort ) == 1 // Make sure 1 number was found
00833                                 && nPort != 0 ) // Make sure the found number isn't 0
00834                         {
00835                                 // Save the found port number in m_pHost
00836                                 m_pHost.sin_port = htons( nPort ); // Convert Windows little endian to big for the Internet with htons
00837                         }
00838                 }
00839         }
00840 
00841         // Have ReadHeaders keep going
00842         return TRUE;
00843 }
00844 
00845 // Classes that inherit from CConnection override this virtual method, adding code specific to them
00846 // Returns true
00847 BOOL CConnection::OnHeadersComplete()
00848 {
00849         // Just return true, it's CShakeNeighbour::OnHeadersComplete() that usually gets called instead of this method
00850         return TRUE;
00851 }
00852 
00854 // CConnection header output helpers
00855 
00856 // Compose text like "Listen-IP: 1.2.3.4:5" and prints it into the output buffer
00857 // Returns true if we are listening on a port and the header was sent, false otherwise
00858 BOOL CConnection::SendMyAddress()
00859 {
00860         // Only do something if we are listening on a port
00861         if ( Network.IsListening() )
00862         {
00863                 // Compose header text
00864                 CString strHeader;
00865 
00866                 strHeader.Format(
00867                         _T("Listen-IP: %s:%hu\r\n"),                                                            // Make it like "Listen-IP: 67.176.34.172:6346\r\n"
00868                         (LPCTSTR)CString( inet_ntoa( Network.m_pHost.sin_addr ) ),      // Insert the IP address like "67.176.34.172"
00869                         htons( Network.m_pHost.sin_port ) );                                            // Our port number in big endian
00870 
00871                 // Print the line into the bottom of the output buffer
00872                 m_pOutput->Print( strHeader ); // It will be sent to the remote computer on the next write
00873 
00874                 // Report that we are listening on a port, and the header is sent
00875                 return TRUE;
00876         }
00877 
00878         // We're not even listening on a port
00879         return FALSE;
00880 }
00881 
00883 // CConnection blocked agent filter
00884 
00885 // Call to determine if the remote computer is running software we'd rather not communicate with
00886 // Returns true to block or false to allow the program
00887 BOOL CConnection::IsAgentBlocked()
00888 {
00889         // Eliminate some obvious block and don't block cases
00890         if ( m_sUserAgent == _T("Fake Shareaza") ) return TRUE; // Block "Fake Shareaza"
00891 
00892         // The remote computer didn't send a "User-Agent" header, or the sent blank text
00893         if ( m_sUserAgent.IsEmpty() )                                                                   // Blank user agent
00894         {
00895                 // If settings say we should block that, return true
00896                 if ( Settings.Gnutella.BlockBlankClients ) return TRUE;
00897                 else                                       return FALSE;        
00898         }
00899 
00900         // If the list of programs to block is empty, allow this program
00901         if ( Settings.Uploads.BlockAgents.IsEmpty() ) return FALSE;
00902 
00903         // Get the list of blocked programs, and make a copy here of it all in lowercase letters
00904         CString strBlocked = Settings.Uploads.BlockAgents;
00905         CharLower( strBlocked.GetBuffer() );
00906         strBlocked.ReleaseBuffer();
00907 
00908         // Get the name of the program running on the other side of the connection, and make it lowercase also
00909         CString strAgent = m_sUserAgent;
00910         CharLower( strAgent.GetBuffer() );
00911         strAgent.ReleaseBuffer();
00912 
00913         // Loop through the list of programs to block
00914         for ( strBlocked += '|' ; strBlocked.GetLength() ; )
00915         {
00916                 // Break off a blocked program name from the start of the list
00917                 CString strBrowser      = strBlocked.SpanExcluding( _T("|;,") );                // Get the text before a puncutation mark
00918                 strBlocked                      = strBlocked.Mid( strBrowser.GetLength() + 1 ); // Remove that much text from the start
00919 
00920                 // If the blocked list still exists and the blocked program and remote program match, block it
00921                 if ( strBrowser.GetLength() > 0 && strAgent.Find( strBrowser ) >= 0 ) return TRUE;
00922         }
00923 
00924         // Allow it
00925         return FALSE;
00926 }
00927 
00929 // CConnection URL encodings
00930 
00931 // Encodes unsafe characters in a string, turning "hello world" into "hello%20world", for instance
00932 // Takes text and returns a string
00933 CString CConnection::URLEncode(LPCTSTR pszInputT)
00934 {
00935         // Setup two strings, one with all the hexidecimal digits, the other with all the characters to find and encode
00936         static LPCTSTR pszHex   = _T("0123456789ABCDEF");       // A string with all the hexidecimal digits
00937         static LPCSTR pszUnsafe = "<>\"#%{}|\\^~[]+?&@=:,";     // A string with all the characters unsafe for a URL
00938 
00939         // The output string starts blank
00940         CString strOutput;
00941 
00942         // If the input character pointer points to null or points to the null terminator, just return the blank output string
00943         if ( pszInputT == NULL || *pszInputT == 0 ) return strOutput;
00944 
00945         // Map the wide character string to a new character set
00946         int nUTF8 = WideCharToMultiByte(
00947                 CP_UTF8,        // Translate using UTF-8, the default encoding for Unicode
00948                 0,                      // Must be 0 for UTF-8 encoding
00949                 pszInputT,      // Points to the wide character string to be converted
00950                 -1,                     // The string is null terminated
00951                 NULL,           // We just want to find out how long the buffer for the output string needs to be
00952                 0,
00953                 NULL,           // Both must be NULL for UTF-8 encoding
00954                 NULL );
00955 
00956         // If the converted text would take less than 2 bytes, which is 1 character, just return blank
00957         if ( nUTF8 < 2 ) return strOutput;
00958 
00959         // Make a new array of CHARs which is nUTF8 bytes long
00960         LPSTR pszUTF8 = new CHAR[ nUTF8 ];
00961 
00962         // Call WideCharToMultiByte again, this time it has the output buffer and will actually do the conversion
00963         WideCharToMultiByte( CP_UTF8, 0, pszInputT, -1, pszUTF8, nUTF8, NULL, NULL );
00964 
00965         // Set the null terminator in pszUTF8 to right where you think it should be, and point a new character pointer at it
00966         pszUTF8[ nUTF8 - 1 ] = 0;
00967         LPCSTR pszInput = pszUTF8;
00968 
00969         // Get the character buffer inside the output string, specifying how much larger to make it
00970         LPTSTR pszOutput = strOutput.GetBuffer( strlen( pszInput ) * 3 + 1 ); // Times 3 in case every character gets encoded
00971 
00972         // Loop for each character of input text
00973         for ( ; *pszInput ; pszInput++ )
00974         {
00975                 // If the character code is 32 or less, more than 127, or in the unsafe list
00976                 if ( *pszInput <= 32 || *pszInput > 127 || strchr( pszUnsafe, *pszInput ) != NULL )
00977                 {
00978                         // Write a three letter code for it like %20 in the output text
00979                         *pszOutput++ = _T('%');
00980                         *pszOutput++ = pszHex[ ( *pszInput >> 4 ) & 0x0F ];
00981                         *pszOutput++ = pszHex[ *pszInput & 0x0F ];
00982 
00983                 } // The character doesn't need to be encoded
00984                 else
00985                 {
00986                         // Just copy it across
00987                         *pszOutput++ = (TCHAR)*pszInput;
00988                 }
00989         }
00990 
00991         // Null terminate the output text, and then close our direct manipulation of the string
00992         *pszOutput = 0;
00993         strOutput.ReleaseBuffer(); // This closes the string so Windows can again start managing its memory for us
00994 
00995         // Free the memory we allocated with the new keyword above
00996         delete [] pszUTF8;
00997 
00998 
00999         // Return the URL-encoded, %20-filled text
01000         return strOutput;
01001 }
01002 
01003 
01004 // Decodes unsafe characters in a string, turning "hello%20world" into "hello world", for instance
01005 // Takes text and returns a string
01006 CString CConnection::URLDecode(LPCTSTR pszInput)
01007 {
01008         LPCTSTR pszLoop = pszInput;
01009         // Check each character of input text
01010         for ( ; *pszLoop ; pszLoop++ )
01011         {
01012                 if ( *pszLoop > 255 )
01013                 {
01014                         // This URI is not properly encoded, and has unicode characters in it. URL-decode only
01015                         return URLDecodeUnicode( pszInput );
01016                 }
01017         }
01018 
01019         // This is a correctly formatted URI, which must be url-decoded, then UTF-8 decoded.
01020         return URLDecodeANSI( pszInput );
01021 }
01022 
01023 // Decodes a properly formatted URI, then UTF-8 decodes it
01024 CString CConnection::URLDecodeANSI(LPCTSTR pszInput)
01025 {
01026         // Setup local variables useful for the conversion
01027         TCHAR szHex[3] = { 0, 0, 0 };   // A 3 character long array filled with 3 null terminators
01028         CString strOutput;                              // The output string, which starts out blank
01029         int nHex;                                               // The hex code of the character we found
01030         
01031         // Allocate a new CHAR array big enough to hold the input characters and a null terminator
01032         LPSTR pszBytes = new CHAR[ _tcslen( pszInput ) + 1 ];
01033 
01034         // Point the output string pointer at this array
01035         LPSTR pszOutput = pszBytes;
01036         
01037         // Loop for each character of input text
01038         for ( ; *pszInput ; pszInput++ )
01039         {
01040                 // We hit a %, which might be the start of something like %20
01041                 if ( *pszInput == '%' )
01042                 {
01043                         // Copy characters like "20" into szHex, making sure neither are null
01044                         if ( ! ( szHex[0] = pszInput[1] ) ) break;
01045                         if ( ! ( szHex[1] = pszInput[2] ) ) break;
01046 
01047                         // Read the text like "20" as a number, and store it in nHex
01048                         if ( _stscanf( szHex, _T("%x"), &nHex ) != 1 ) break;
01049                         if ( nHex < 1 ) break; // Make sure the number isn't 0 or negative
01050 
01051                         // That number is the code of a character, copy it into the output string
01052                         *pszOutput++ = nHex; // And then move the output pointer to the next spot
01053 
01054                         // Move the input pointer past the two characters of the "20"
01055                         pszInput += 2;
01056 
01057                 } // We hit a +, which is shorthand for space
01058                 else if ( *pszInput == '+' )
01059                 {
01060                         // Add a space to the output text, and move the pointer forward
01061                         *pszOutput++ = ' ';
01062 
01063                 } // The input pointer is just on a normal character
01064                 else
01065                 {
01066                         // Copy it across
01067                         *pszOutput++ = (CHAR)*pszInput;
01068                 }
01069         }
01070 
01071         // Cap off the output text with a null terminator
01072         *pszOutput = 0;
01073 
01074         // Copy the text from pszBytes into strOutput, converting it into Unicode
01075         int nLength = MultiByteToWideChar( CP_UTF8, 0, pszBytes, -1, NULL, 0 );
01076         MultiByteToWideChar( CP_UTF8, 0, pszBytes, -1, strOutput.GetBuffer( nLength ), nLength );
01077 
01078         // Close the output string, we are done editing its buffer directly
01079         strOutput.ReleaseBuffer();
01080 
01081         // Free the memory we allocated above
01082         delete [] pszBytes;
01083 
01084         // Return the output string
01085         return strOutput;
01086 }
01087 
01088 
01089 // Decodes encoded characters in a unicode string
01090 CString CConnection::URLDecodeUnicode(LPCTSTR pszInput)
01091 {
01092         // Setup local variables useful for the conversion
01093         TCHAR szHex[3] = { 0, 0, 0 };   // A 3 character long array filled with 3 null terminators
01094         CString strOutput;                              // The output string, which starts out blank
01095         int nHex;                                               // The hex code of the character we found
01096         
01097         // Allocate a new CHAR array big enough to hold the input characters and a null terminator
01098         LPTSTR pszBytes = strOutput.GetBuffer( _tcslen( pszInput ) );
01099 
01100         // Point the output string pointer at this array
01101         LPTSTR pszOutput = pszBytes;
01102         
01103         // Loop for each character of input text
01104         for ( ; *pszInput ; pszInput++ )
01105         {
01106                 // We hit a %, which might be the start of something like %20
01107                 if ( *pszInput == '%' )
01108                 {
01109                         // Copy characters like "20" into szHex, making sure neither are null
01110                         if ( ! ( szHex[0] = pszInput[1] ) ) break;
01111                         if ( ! ( szHex[1] = pszInput[2] ) ) break;
01112 
01113                         // Read the text like "20" as a number, and store it in nHex
01114                         if ( _stscanf( szHex, _T("%x"), &nHex ) != 1 ) break;
01115                         if ( nHex < 1 ) break; // Make sure the number isn't 0 or negative
01116 
01117                         // That number is the code of a character, copy it into the output string
01118                         *pszOutput++ = nHex; // And then move the output pointer to the next spot
01119 
01120                         // Move the input pointer past the two characters of the "20"
01121                         pszInput += 2;
01122 
01123                 } // We hit a +, which is shorthand for space
01124                 else if ( *pszInput == '+' )
01125                 {
01126                         // Add a space to the output text, and move the pointer forward
01127                         *pszOutput++ = ' ';
01128 
01129                 } // The input pointer is just on a normal character
01130                 else
01131                 {
01132                         // Copy it across
01133                         *pszOutput++ = (TCHAR)*pszInput;
01134                 }
01135         }
01136 
01137         // End the output text with a null terminator
01138         *pszOutput = 0;
01139         // Put the output into a CString
01140         strOutput.ReleaseBuffer();
01141         // Return the output string
01142         return strOutput;
01143 }
01144 
01145 // Takes some input text like "hello world" and a text tag like "hello"
01146 // Determines if the input starts with the text
01147 // Returns true or false
01148 BOOL CConnection::StartsWith(LPCTSTR pszInput, LPCTSTR pszText)
01149 {
01150         // See if input starts with text
01151         return _tcsnicmp(
01152                 pszInput,                               // Compare input
01153                 pszText,                                // With text
01154                 _tcslen( pszText ) )    // But just the first text-length part of both
01155                 == 0;                                   // Return true if it matches, false if not
01156 }

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