gloox 1.0
connectiontcpbase.cpp
00001 /*
00002   Copyright (c) 2004-2009 by Jakob Schroeter <js@camaya.net>
00003   This file is part of the gloox library. http://camaya.net/gloox
00004 
00005   This software is distributed under a license. The full license
00006   agreement can be found in the file LICENSE in this distribution.
00007   This software may not be copied, modified, sold or distributed
00008   other than expressed in the named license agreement.
00009 
00010   This software is distributed without any warranty.
00011 */
00012 
00013 
00014 
00015 #include "gloox.h"
00016 
00017 #include "connectiontcpbase.h"
00018 #include "dns.h"
00019 #include "logsink.h"
00020 #include "prep.h"
00021 #include "mutexguard.h"
00022 
00023 #ifdef __MINGW32__
00024 # include <winsock.h>
00025 #endif
00026 
00027 #if ( !defined( _WIN32 ) && !defined( _WIN32_WCE ) ) || defined( __SYMBIAN32__ )
00028 # include <arpa/inet.h>
00029 # include <sys/types.h>
00030 # include <sys/socket.h>
00031 # include <sys/select.h>
00032 # include <netinet/in.h>
00033 # include <unistd.h>
00034 #elif ( defined( _WIN32 ) || defined( _WIN32_WCE ) ) && !defined( __SYMBIAN32__ )
00035 # include <winsock.h>
00036 typedef int socklen_t;
00037 #endif
00038 
00039 #include <ctime>
00040 
00041 #include <cstdlib>
00042 #include <string>
00043 
00044 namespace gloox
00045 {
00046 
00047   ConnectionTCPBase::ConnectionTCPBase( const LogSink& logInstance,
00048                                         const std::string& server, int port )
00049     : ConnectionBase( 0 ),
00050       m_logInstance( logInstance ), m_buf( 0 ), m_socket( -1 ), m_totalBytesIn( 0 ),
00051       m_totalBytesOut( 0 ), m_bufsize( 1024 ), m_cancel( true )
00052   {
00053     init( server, port );
00054   }
00055 
00056   ConnectionTCPBase::ConnectionTCPBase( ConnectionDataHandler* cdh, const LogSink& logInstance,
00057                                         const std::string& server, int port )
00058     : ConnectionBase( cdh ),
00059       m_logInstance( logInstance ), m_buf( 0 ), m_socket( -1 ), m_totalBytesIn( 0 ),
00060       m_totalBytesOut( 0 ), m_bufsize( 1024 ), m_cancel( true )
00061   {
00062     init( server, port );
00063   }
00064 
00065   void ConnectionTCPBase::init( const std::string& server, int port )
00066   {
00067 // FIXME check return value?
00068     prep::idna( server, m_server );
00069     m_port = port;
00070     m_buf = (char*)calloc( m_bufsize + 1, sizeof( char ) );
00071   }
00072 
00073   ConnectionTCPBase::~ConnectionTCPBase()
00074   {
00075     cleanup();
00076     free( m_buf );
00077     m_buf = 0;
00078   }
00079 
00080   void ConnectionTCPBase::disconnect()
00081   {
00082     util::MutexGuard rm( m_recvMutex );
00083     m_cancel = true;
00084   }
00085 
00086   bool ConnectionTCPBase::dataAvailable( int timeout )
00087   {
00088     if( m_socket < 0 )
00089       return true; // let recv() catch the closed fd
00090 
00091     fd_set fds;
00092     struct timeval tv;
00093 
00094     FD_ZERO( &fds );
00095     // the following causes a C4127 warning in VC++ Express 2008 and possibly other versions.
00096     // however, the reason for the warning can't be fixed in gloox.
00097     FD_SET( m_socket, &fds );
00098 
00099     tv.tv_sec = timeout / 1000000;
00100     tv.tv_usec = timeout % 1000000;
00101 
00102     return ( ( select( m_socket + 1, &fds, 0, 0, timeout == -1 ? 0 : &tv ) > 0 )
00103              && FD_ISSET( m_socket, &fds ) != 0 );
00104   }
00105 
00106   ConnectionError ConnectionTCPBase::receive()
00107   {
00108     if( m_socket < 0 )
00109       return ConnNotConnected;
00110 
00111     ConnectionError err = ConnNoError;
00112     while( !m_cancel && ( err = recv( 10 ) ) == ConnNoError )
00113       ;
00114     return err == ConnNoError ? ConnNotConnected : err;
00115   }
00116 
00117   bool ConnectionTCPBase::send( const std::string& data )
00118   {
00119     m_sendMutex.lock();
00120 
00121     if( data.empty() || ( m_socket < 0 ) )
00122     {
00123       m_sendMutex.unlock();
00124       return false;
00125     }
00126 
00127     int sent = 0;
00128     for( size_t num = 0, len = data.length(); sent != -1 && num < len; num += sent )
00129     {
00130       sent = static_cast<int>( ::send( m_socket, (data.c_str()+num), (int)(len - num), 0 ) );
00131     }
00132 
00133     m_totalBytesOut += (int)data.length();
00134 
00135     m_sendMutex.unlock();
00136 
00137     if( sent == -1 && m_handler )
00138       m_handler->handleDisconnect( this, ConnIoError );
00139 
00140     return sent != -1;
00141   }
00142 
00143   void ConnectionTCPBase::getStatistics( long int &totalIn, long int &totalOut )
00144   {
00145     totalIn = m_totalBytesIn;
00146     totalOut = m_totalBytesOut;
00147   }
00148 
00149   void ConnectionTCPBase::cleanup()
00150   {
00151     if( !m_sendMutex.trylock() )
00152       return;
00153 
00154     if( !m_recvMutex.trylock() )
00155     {
00156       m_sendMutex.unlock();
00157       return;
00158     }
00159 
00160     if( m_socket >= 0 )
00161     {
00162       DNS::closeSocket( m_socket, m_logInstance );
00163       m_socket = -1;
00164     }
00165 
00166     m_state = StateDisconnected;
00167     m_cancel = true;
00168     m_totalBytesIn = 0;
00169     m_totalBytesOut = 0;
00170 
00171     m_recvMutex.unlock(),
00172     m_sendMutex.unlock();
00173   }
00174 
00175   int ConnectionTCPBase::localPort() const
00176   {
00177     struct sockaddr local;
00178     socklen_t len = (socklen_t)sizeof( local );
00179     if( getsockname ( m_socket, &local, &len ) < 0 )
00180       return -1;
00181     else
00182       return ntohs( ((struct sockaddr_in *)&local)->sin_port );
00183   }
00184 
00185   const std::string ConnectionTCPBase::localInterface() const
00186   {
00187     struct sockaddr_in local;
00188     socklen_t len = (socklen_t)sizeof( local );
00189     if( getsockname ( m_socket, (reinterpret_cast<struct sockaddr*>( &local )), &len ) < 0 )
00190       return EmptyString;
00191     else
00192     {
00193 //       char addr[INET_ADDRSTRLEN];
00194 //       return inet_ntop( AF_INET, &(local.sin_addr), addr, sizeof( addr ) ); //FIXME is this portable?
00195       return inet_ntoa( local.sin_addr );
00196     }
00197   }
00198 
00199 }