#include <iostream.h>
#include <sstream>
#include <libssh2.h>
#include <libssh2_sftp.h>
#ifndef SV_WINDOWS
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>
#include <arpa/inet.h> 
#include <sys/time.h> 
#endif
#include <sys/types.h> 
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <ctype.h>
   ///////////////////////////////////////////////////////////////////////////////////////////
void trimSpaces( std::string& s)
{
	std::string stuff_to_trim = " \n\b\t\a\r\xc" ; 
 	
	s.erase( s.find_last_not_of(stuff_to_trim) + 1) ; //erase all trailing spaces
	s.erase(0 , s.find_first_not_of(stuff_to_trim) ) ; //erase all leading spaces
	cout<< "Trim Spaces is" << s <<"\n";
}

int waitsocket(int socket_fd, LIBSSH2_SESSION *session)
{
	cout<<"Entered waitsocket() \n";
    struct timeval timeout;
    int rc;
    fd_set fd;
    fd_set *writefd = NULL;
    fd_set *readfd = NULL;
    int dir;

    timeout.tv_sec = 10;
    timeout.tv_usec = 0;

    FD_ZERO(&fd);

    FD_SET(socket_fd, &fd);

	dir = libssh2_session_block_directions(session);

    if(dir & LIBSSH2_SESSION_BLOCK_INBOUND)
        readfd = &fd;

    if(dir & LIBSSH2_SESSION_BLOCK_OUTBOUND)
        writefd = &fd;

    rc = select( socket_fd + 1, readfd, writefd, NULL, &timeout );
    cout<<"Exited waitsocket() \n" << rc << std::endl ;  
    return rc;
}	

bool libssh2_exec( std::string ip, std::string passwd1 ,std::string cmd, std::string& outPutInfo )
{
    cout<< " \n entered into libssh2_exec() \n" ;
    int exitcode;
    bool bRet = 1 ;
	std::stringstream errorStream;
	std::string user = "root";
    std::string passwd = passwd1;
	const char *remotehost  = ip.c_str();
	cmd = cmd + ";sleep 10";
    cout<< "cmd is "<< cmd <<"\n";
    const char *commandline =  cmd.c_str(); 
    const char *username    = user.c_str();
    const char *password    = passwd.c_str();
    unsigned long hostaddr;
    int sock, i;
    struct sockaddr_in sin;
    const char *fingerprint;
    LIBSSH2_SESSION *session;
    LIBSSH2_CHANNEL *channel;
    int rc;
    std::stringstream osTypeStream;
#ifdef WIN32
    WSADATA wsadata;
    WSAStartup(MAKEWORD(2,0), &wsadata);
#endif
    hostaddr = inet_addr( remotehost );
	sock = socket(AF_INET, SOCK_STREAM, 0);
	sin.sin_family = AF_INET;
    sin.sin_port = htons(22);
    sin.sin_addr.s_addr = hostaddr;
    cout<<"Connecting to "<<ip << " to execute " << cmd <<"\n";	 
    if (connect(sock, (struct sockaddr*)(&sin), sizeof(struct sockaddr_in)) != 0) 
	{
        errorStream.str("") ;
        errorStream << " failed to connect to " << ip << "\n" ;
		cout<<errorStream;
        return 0;
    }
//#ifdef F_SETFL
 //   rc = fcntl(sock, F_GETFL, 0);
   // fcntl(sock, F_SETFL, rc | O_NONBLOCK);
//#elif defined(HAVE_IOCTLSOCKET)
  //  ioctlsocket(sock, FIONBIO, &flag);
//#else
//#ifdef WIN32
 //   u_long mode = 1;
   // ioctlsocket (sock, FIONBIO, &mode);
//#endif
//#endif
    do
    {
        cout<<"session init \n";
        session = libssh2_session_init();
        if (!session)
        {
            errorStream.str("") ;
            errorStream << " failed to initialize libssh2 session " << "\n" ;
			cout<<errorStream;
            return 0;
        }
        
        while (( rc = libssh2_session_startup( session, sock )) == LIBSSH2_ERROR_EAGAIN );
        if ( rc ) 
	    {
            errorStream.str("") ;
            errorStream << " Failure establishing SSH session: return code is " << rc << "\n" ;
			cout<<errorStream;
            return 0;
        }
        
  //      libssh2_session_set_blocking(session, 0);
        fingerprint = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_MD5);
        cout<<"Logging into "<< ip <<"\n";
		if ( strlen(password) != 0 ) 
	    {
			do
			{
				rc = libssh2_userauth_password(session, username, password) ;
				if( rc == -1 ) 
					rc = libssh2_session_last_errno( session ) ;
				cout << "trying again... " << rc << " " << std::endl ;
				waitsocket( sock, session ) ;
			} while( rc == LIBSSH2_ERROR_EAGAIN ) ;
            if (rc != 0 ) 
		    {
                cout  << "Authentication by password failed. \n" ;
                bRet = 0;
			    char *errorMessage ;
				int size ;
	   			int errNo = libssh2_session_last_error(session,&errorMessage,&size,0 ) ;
				cout << errorMessage << std::endl << "error no: " << errNo << std::endl ; 
				//if( errNo == -37 )
				//	waitsocket( sock, session ) ;
            }
                if( libssh2_userauth_authenticated( session ) == 0 )
                {
					cout << "Authentication not done: " << libssh2_session_last_errno( session ) << std::endl ;
					break;
				}
        } 
	    cout<<"set blocking socket \n";
    //    libssh2_session_set_blocking(session, 0);
      //char *msg;
		//cout<< libssh2_session_last_error(session,NULL,&msg,0);
		 cout<<" Logged in ??"<<"\n";
        libssh2_trace(session, LIBSSH2_TRACE_TRANS | LIBSSH2_TRACE_KEX | LIBSSH2_TRACE_AUTH | LIBSSH2_TRACE_CONN | LIBSSH2_TRACE_SCP | LIBSSH2_TRACE_SFTP | LIBSSH2_TRACE_ERROR | LIBSSH2_TRACE_PUBLICKEY );
		cout<<"Channel Open Session.. "<< ip <<"\n";
        while( (channel = libssh2_channel_open_session(session)) == NULL &&
               libssh2_session_last_error(session,NULL,NULL,0) == LIBSSH2_ERROR_EAGAIN )
        {
            waitsocket(sock, session);
        }
        if( channel == NULL )
        {
            errorStream.str("") ;
            errorStream << " Unable to open a session " << "\n" ;
			cout<<errorStream;
            bRet = 0 ;
            break;
        }
		cout<<" channel exec.. "<< ip <<"\n";
        while( (rc = libssh2_channel_exec(channel, commandline)) == LIBSSH2_ERROR_EAGAIN )
        {
            waitsocket(sock, session);
        }
        if( rc != 0 )
        {
            errorStream.str("") ;
            std::string cmdline = commandline;
            errorStream << " Unable to execute commandline on  " << ip << "command line is " <<cmdline<< "\n" ;
			cout<<errorStream;
            bRet = 0 ;
            break;
        }
        for( ;; )
        {
            int rc;
		    std::stringstream readBuf;
            readBuf.str("");
            do
            {
                char buffer[0x4000];
#ifdef SV_WINDOWS
                Sleep( 100 );
#else
                sleep( 3 );
#endif
                rc = libssh2_channel_read( channel, buffer, sizeof(buffer) );
//			    rc = libssh2_channel_read_stderr( channel, buffer, sizeof(buffer) );
				cout<< "reading from channel\n" ;
			    if( rc > 0 )
			    {
				    int i;
				    for( i=0; i < rc; ++i )
					    readBuf << buffer[i];
			    }
		    }while( rc > 0 );
            cout<< " Read Buffer is " << readBuf.str() <<"\n";
            osTypeStream << readBuf.str();
            if( rc == LIBSSH2_ERROR_EAGAIN )
            {
                waitsocket(sock, session);
            }
            else if( rc == 0 )
                break;
        }
        outPutInfo = osTypeStream.str();
        exitcode = 127;
        while( (rc = libssh2_channel_close(channel)) == LIBSSH2_ERROR_EAGAIN );
        if( rc == 0 )
        {
            exitcode = libssh2_channel_get_exit_status( channel );
        }
        cout<< "exitcode: " <<exitcode <<" \n" ;
	    if(	exitcode )
	    {
		    bRet = 0 ;
	    }
        libssh2_channel_free(channel);
        channel = NULL;
    }while( false );

	if( session != NULL )
    {
    	libssh2_session_disconnect(session, "Normal Shutdown ");
	    libssh2_session_free(session);
	}

#ifdef WIN32
    Sleep(1000);
    closesocket(sock);
#else
    sleep(1);
    close(sock);
#endif
    cout<< "\nall done\n";
   
 return bRet;
}

bool is32Bit( std::string ip, std::string passwd )
{
    std::string uname_a_Value;
	libssh2_exec(ip, passwd, "uname -a", uname_a_Value );
    trimSpaces( uname_a_Value );
    int i = 0;
    for( ;i < 10;i++ )
    {
        std::stringstream sub;
		sub<< "i" << i << "86";
        if( uname_a_Value.find( sub.str() ) != std::string::npos )
        {
            return 1;
        }
    }
    return 0;
}


bool  getRemoteOsType( std::string ip, std::string passwd , std::string& OS)
{
	cout<<"\n Entering getRemoteOsType() \n";
	bool ret = 1;
	std::string uname_Value;
	libssh2_exec( ip, passwd, "uname", uname_Value );
	trimSpaces( uname_Value );
	if( uname_Value.compare("Linux") == 0 )
	{
		std::string redhat_release_Value, SuSE_release_Value, debian_version_Value; 
		if( libssh2_exec( ip, passwd,"cat /etc/redhat-release", redhat_release_Value ) ) //TODO ...
		{
			trimSpaces( redhat_release_Value );
			if( redhat_release_Value.find("release 3") != std::string::npos )
			{
				OS="RHEL3-32";
			}
			else if( redhat_release_Value.find("release 4 (Nahant Update 3)") != std::string::npos || 
					redhat_release_Value.find("CentOS release 4.3 (Final)") != std::string::npos )
			{
				if( is32Bit( ip, passwd ) )
					OS="RHEL4U3-32";
				else
					OS="RHEL4U3-64";	
			}
			else if( redhat_release_Value.find("release 4 (Nahant Update 4)") != std::string::npos || 
					redhat_release_Value.find("CentOS release 4.4 (Final)") != std::string::npos )
			{
				if( is32Bit( ip, passwd ) )
					OS="RHEL4U4-32";
				else
					OS="RHEL4U4-64";
			}
			else if( redhat_release_Value.find("Enterprise Linux Enterprise Linux AS release 4 (October Update 4)") != std::string::npos ) 
			{
				if( is32Bit( ip, passwd ) )
					OS="ELORCL4U4-32";
			}
			else if( redhat_release_Value.find("release 4 (Nahant Update 5)") != std::string::npos || 
					redhat_release_Value.find("CentOS release 4.5 (Final)") != std::string::npos )
			{
				if( is32Bit( ip, passwd ) )
					OS="RHEL4U5-32";
				else
					OS="RHEL4U5-64";
			}
			else if( redhat_release_Value.find("release 4 (Nahant Update 6)") != std::string::npos || 
					redhat_release_Value.find("CentOS release 4.6 (Final)") != std::string::npos )
			{
				if( is32Bit( ip, passwd ) )
					OS="RHEL4U6-32";
				else
					OS="RHEL4U6-64";
			}
			else if( redhat_release_Value.find("release 4 (Nahant Update 7)") != std::string::npos || 
					redhat_release_Value.find("CentOS release 4.7 (Final)") != std::string::npos )
			{	
				if( is32Bit( ip, passwd ) )
					OS="RHEL4U7-32";
				else
					OS="RHEL4U7-64";
			}
			else if( redhat_release_Value.find("release 4 (Nahant Update 8)") != std::string::npos || 
					redhat_release_Value.find("CentOS release 4.8 (Final)") != std::string::npos )
			{
				if( is32Bit( ip, passwd ) )
					OS="RHEL4U8-32";
				else
					OS="RHEL4U8-64";
			}	
			else if( redhat_release_Value.find("release 5 (Tikanga)") != std::string::npos ) 
			{
				if( is32Bit( ip, passwd ) )
					OS="RHEL5-32";
				else
					OS="RHEL5-64";
			}
			else if( redhat_release_Value.find("release 5.1 (Tikanga)") != std::string::npos ) 
			{
				if( is32Bit( ip, passwd ) )
					OS="RHEL5U1-32";
				else
					OS="RHEL5U1-64";
			}
			else if( redhat_release_Value.find("release 5.2 (Tikanga)") != std::string::npos || 
					redhat_release_Value.find("CentOS release 5.2 (Final)") != std::string::npos )
			{
				if( is32Bit( ip, passwd ) )
					OS="RHEL5U2-32";
				else
					OS="RHEL5U2-64";
			}
			else if( redhat_release_Value.find("Red Hat Enterprise Linux Server release 5.3 (Tikanga") != std::string::npos || 
					redhat_release_Value.find("CentOS release 5.3 (Final)") != std::string::npos )
			{
				if( is32Bit( ip, passwd ) )
					OS="RHEL5U3-32";
				else
					OS="RHEL5U3-64";
			}
			else if( redhat_release_Value.find("CentOS release 5 (Final)") != std::string::npos ) 
			{
				std::string uname_r_Value;
				libssh2_exec( ip, passwd,"uname -r", uname_r_Value );
				trimSpaces( uname_r_Value );
				if( uname_r_Value.find("2.6.18-8") != std::string::npos )
				{
					if( is32Bit( ip, passwd ) )
						OS="RHEL5-32";
					else
						OS="RHEL5-64";
				}
				else if( uname_r_Value.find("2.6.18-53") != std::string::npos )
				{
					if( is32Bit( ip, passwd ) )
						OS="RHEL5U1-32";
					else
						OS="RHEL5U1-64";
				}
				else
				{
					cout<< "Unsupported CentOS Type \n";
					return 0;
				}	
			}
			else if( redhat_release_Value.find("XenServer release 4.1.0-7843p (xenenterprise)") != std::string::npos ) 
			{
				if( is32Bit( ip, passwd ) )
					OS="CITRIX-XEN-4.1.0-7843p-32";
				else
					OS="CITRIX-XEN-4.1.0-7843p-64";	
			}
			else if( redhat_release_Value.find("XenServer release 5.0.0-10918p (xenenterprise)") != std::string::npos ) 
			{
				if( is32Bit( ip, passwd ) )
					OS="CITRIX-XEN-5.0.0-10918p-32";
				else
					OS="CITRIX-XEN-5.0.0-10918p-64";
			}
			else if( redhat_release_Value.find("XenServer release 4.0.1-4249p (xenenterprise)") != std::string::npos ) 
			{
				if( is32Bit( ip, passwd ) )
					OS="CITRIX-XEN-4.0.1-4249p-32";
				else
					OS="CITRIX-XEN-4.0.1-4249p-64";
			}
			else
			{
				cout<< "Unsupported redhat-relese \n";
				return 0;
			}
		}
		else if( libssh2_exec( ip, passwd,"cat /etc/SuSE-release", SuSE_release_Value ) ) //TODO....
		{
			trimSpaces( SuSE_release_Value );
			if( SuSE_release_Value.find("VERSION = 10") != std::string::npos && 
				SuSE_release_Value.find("SUSE Linux Enterprise Server 10") != std::string::npos  &&
				SuSE_release_Value.find("PATCHLEVEL = 1") != std::string::npos )
			{
				if( is32Bit( ip, passwd ) )
					OS="SLES10-SP1-32";
				else
					OS="SLES10-SP1-64";
			}
			else if( SuSE_release_Value.find("VERSION = 10") != std::string::npos && 
				     SuSE_release_Value.find("SUSE Linux Enterprise Server 10") != std::string::npos &&
				     SuSE_release_Value.find("PATCHLEVEL = 2") != std::string::npos )
			{
				if( is32Bit( ip, passwd ) )
					OS="SLES10-SP2-32";
				else
					OS="SLES10-SP2-64";
			}
			else if( SuSE_release_Value.find("VERSION = 10") != std::string::npos && 
					SuSE_release_Value.find("PATCHLEVEL = 1") != std::string::npos )
			{
				if( is32Bit( ip, passwd ) )
					OS="OPENSUSE10-SP1-32";
				else
					OS="OPENSUSE10-SP1-64";
			}
			else if( SuSE_release_Value.find("VERSION = 9") != std::string::npos &&  
					SuSE_release_Value.find("PATCHLEVEL") != std::string::npos )
			{
				if( is32Bit( ip, passwd ) )
					OS="SLES9-32";
				else
					OS="SLES9-64";
			}
			else if( SuSE_release_Value.find("VERSION = 9") != std::string::npos &&  
					SuSE_release_Value.find("PATCHLEVEL = 2") != std::string::npos )
			{
				if( is32Bit( ip, passwd ) )
					OS="SLES9-SP2-32";
				else
					OS="SLES9-SP2-64";
			}
			else if( SuSE_release_Value.find("VERSION = 9") != std::string::npos &&  
					SuSE_release_Value.find("PATCHLEVEL = 3") != std::string::npos )
			{
				if( is32Bit( ip, passwd ) )
					OS="SLES9-SP3-32";
				else
					OS="SLES9-SP3-64";
			}
			else
			{
				cout<< "Unsupported SuSE-relese \n";
				return 0;
			}
		}
		else if( libssh2_exec( ip, passwd,"cat /etc/debian_version", debian_version_Value ))
		{
			trimSpaces( debian_version_Value );
			if( debian_version_Value.find("4.0") != std::string::npos )
			{
				if( is32Bit( ip, passwd ) )
					OS="DEBIAN-ETCH-32";
				else
					OS="DEBIAN-ETCH-64";
			}
			else
			{
				cout<< "Unsupported debain_version \n";
				return 0;
			}		
		}	
	}
	else if( uname_Value.compare("HP-UX") == 0 )
	{
		std::string uname_r_Value;
		libssh2_exec(ip, passwd,"uname -r", uname_r_Value);
		trimSpaces( uname_r_Value );	
		if( uname_r_Value.find( "B.11.23" ) == 0 )
		{
			OS="HP-UX-Itanium-11iv2";
		}
		else if( uname_r_Value.find( "B.11.31" ) == 0 )
		{
			OS="HP-UX-Itanium-11iv3";
		}
		else if( uname_r_Value.find( "B.11.11" ) == 0 )
		{
			OS="HP-UX-PA-RISC";
		}
		else
		{
			cout<< "Unsupported HP-UX \n";
			return 0;
		}
	}
	else if( uname_Value.compare("AIX") == 0 )
	{
		std::string uname_v_Value, uname_r_Value;
		libssh2_exec( ip, passwd,"uname -v", uname_v_Value );
        libssh2_exec(ip, passwd,"uname -r", uname_r_Value);
		trimSpaces( uname_v_Value );
		trimSpaces( uname_r_Value );
		if( uname_v_Value.compare("5") == 0 )
		{
			if( uname_r_Value.compare("2") == 0 )
			{
				OS="AIX52";
			}
			else if( uname_r_Value.compare("3") == 0 )
			{
				OS="AIX53";
			}
		}
		else if( uname_v_Value.compare("6") == 0 )
		{	
			if( uname_r_Value.compare("1") == 0 )
			{
				OS="AIX61";
			}
		}
		else
		{
			cout<< "Unsupported AIX \n";
			return 0;
		}
	}
	else if( uname_Value.compare("SunOS") == 0 )
	{
		std::string uname_r_Value, uname_p_Value;
        libssh2_exec( ip, passwd, "uname -r", uname_r_Value );
		libssh2_exec( ip, passwd, "uname -p", uname_p_Value );
		trimSpaces( uname_r_Value );
		trimSpaces( uname_p_Value );
		if( uname_r_Value.compare("5.8") == 0 && uname_p_Value.find("sparc") != std::string::npos )
		{
			OS="Solaris-5-8-Sparc";
		}
		else if( uname_r_Value.compare("5.9") == 0 && uname_p_Value.find("sparc") != std::string::npos )
		{
			OS="Solaris-5-9-Sparc";
		}
		else if( uname_r_Value.compare("5.10") == 0 && 
			   ( uname_p_Value.find("i386") != std::string::npos || uname_p_Value.find("x86") != std::string::npos) )
		{	
				std::string isainfo_b_Value;
				libssh2_exec ( ip, passwd, "isainfo -b", isainfo_b_Value );
				trimSpaces( isainfo_b_Value );
				if( isainfo_b_Value.compare("64") == 0 )
				{
					OS="Solaris-5-10-x86-64";
				}
			
		}
		else if( uname_r_Value.compare("5.10") == 0 && uname_p_Value.find("sparc") != std::string::npos )
		{
			OS="Solaris-5-10-Sparc";
		}
		else
		{
			cout<< "Unsupported SunOS \n";
			return 0;
		}
	}
	else
	{	
		cout<< "Unsupported OS \n";
		return 0;
	}
	return 1;
	///////////////////////////////////////////////////////////////////////////////////
	cout<<"\n Exiting getRemoteOsType()";
}

int main( int argc, char **argv )
{
    cout<<"Entering main() \n";
    std::string ip, os_Type, passwd;
	if( argc < 3 )
	{
		cout<< "Must provide Ip and password as Input\n";	
		return -1;
	}
	ip = argv[1];
	passwd = argv[2];
    if( getRemoteOsType( ip, passwd, os_Type) )
    {    
		trimSpaces( os_Type );
		cout << "\n The Os Type of " << ip << " is#########  " << os_Type<<"   ############\n";
    }
	else
        cout <<"\n Failed to Find Out the Ostype of " << ip <<"\n";
    cout<<"Exiting main() \n";
    return -1;
}

