#ifndef h_ssh2
#define h_ssh2

#include "libssh2_config.h"
#include <libssh2.h>
#include <netinet/in.h>
#include <sys/socket.h>
# ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
# ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#include <netdb.h>
#include <sys/types.h>
#include <termios.h>
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <ctype.h>

#include <string>
#include <vector>
#include <iostream>
#include <fstream>
#include <sstream>
#include <ctime>
#include <boost/tokenizer.hpp>

#ifndef TCP_NODELAY
#define TCP_NODELAY 1
#endif

#define ISIP(m) (!(inet_addr(m) ==-1))

using namespace std;

template<class IN, class TO> TO convert(IN input) {
        TO output;
        std::stringstream ss;
        ss << input;
        ss >> output;
        return output;
}

template<class IN> std::string toString(IN input) {
        return convert <IN, std::string> (input);
}

template<class IN> bool toBool(IN input) {
        return convert <IN, bool> (input);
}

template<class IN> int toInt(IN input) {
        return convert<IN, int>(input);
}

std::string timestamp(){
        struct tm *local;
        time_t t;
        char str[20];
        std::string timestr;

        t= time(NULL);
        local=localtime(&t);

        strftime(str, 20, "%Y-%m-%d %H:%M:%S", local);
        timestr=str;

        return timestr;
}

double diff_timestamp(std::string timestr1, std::string timestr0){
        time_t t1,t0;
        struct tm tm1,tm0;

        strptime(timestr1.c_str(),"%Y-%m-%d %H:%M:%S",&tm1);
        strptime(timestr0.c_str(),"%Y-%m-%d %H:%M:%S",&tm0);
        t1=mktime(&tm1);
        t0=mktime(&tm0);

        return difftime(t1,t0);
}

class SSH2{

private:
	string host;
	string user;
	string pwd;
	int sock;
	LIBSSH2_SESSION *session;
	LIBSSH2_CHANNEL *channel;
	bool connected;

public:

	SSH2(){
        	channel = NULL;
        	session = NULL;

		connected = true;
	}

	SSH2(string hostname, string username, string password){
		host = hostname;
		user = username;
		pwd = password;

        	channel = NULL;
        	session = NULL;

		connected = false;
	}

	~SSH2(){
		if (connected){
    			libssh2_session_disconnect(session, "Normal Shutdown, Thank you for playing");
    			libssh2_session_free(session);
    			sleep(1);
    			close(sock);
		}
	}

	int connection(){
    		struct sockaddr_in sin;
    		unsigned long hostaddr;
		struct  hostent    *hp;
    		char *userauthlist;
		const char *fingerprint;

 		if (ISIP(host.c_str())) {
			hostaddr = inet_addr(host.c_str());
		}
		else{
  			if ((hp = gethostbyname(host.c_str()))==NULL){
				printf("gethostbyname() failed check the existance of the host.\n");
    				return 0;
			}
			else{
  				hostaddr = inet_addr(inet_ntoa(*((struct in_addr *)hp->h_addr)));
			}
		}

    		sock = socket(AF_INET, SOCK_STREAM, 0);
    		sin.sin_family = AF_INET;
    		sin.sin_port = htons(22);
    		sin.sin_addr.s_addr = hostaddr;

		/*
    		int flagTcpNoDelay = 1;
    		int resTcpNoDelay = setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *) &flagTcpNoDelay, sizeof(int));
    		if (resTcpNoDelay < 0) {
			Log(1,"SSH2","connect()","Error setting TCP_NODELAY.");
			return 0;
    		}

    		struct timeval tv;
    		tv.tv_sec = 59;
    		tv.tv_usec = 0;
    		if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char *) &tv, sizeof tv)) {
			Log(1,"SSH2","connect()","Error setting socket timeout.");
			return 0;
    		}
		*/

    		if (connect(sock, (struct sockaddr*)(&sin), sizeof(struct sockaddr_in)) != 0){
			printf("Error connecting.\n");
			return 0;
    		}

    		/* Create a session instance and start it up
     		 * This will trade welcome banners, exchange keys, and setup crypto, compression, and MAC layers
     		 */
    		session = libssh2_session_init();
    		if (libssh2_session_startup(session, sock)) {
			printf("Failure establishing SSH session.\n");
			return 0;
    		}

    		/* At this point we havn't authenticated,
     		 * The first thing to do is check the hostkey's fingerprint against our known hosts
     		 * Your app may have it hard coded, may go to a file, may present it to the user, that's your call
     		 */
    		fingerprint = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_MD5);
		/*
    		printf("Fingerprint: ");
    		for(i = 0; i < 16; i++) {
        		printf("%02X ", (unsigned char)fingerprint[i]);
    		}
		*/

        	/* Authenticate via password */
        	if (libssh2_userauth_password(session, user.c_str(), pwd.c_str())) {
			printf("Authentication by password failed.\n");
    			libssh2_session_disconnect(session, "Normal Shutdown, Thank you for playing");
    			libssh2_session_free(session);
    			sleep(1);
    			close(sock);
			return 0;
        	}

    		/* Request a session */
    		if (!(channel = libssh2_channel_open_session(session))) {
			printf("Unable to open a session.\n");
    			libssh2_session_disconnect(session, "Normal Shutdown, Thank you for playing");
    			libssh2_session_free(session);
    			sleep(1);
    			close(sock);
			return 0;
    		}


    		/* Open a SHELL */
    		if (libssh2_channel_shell(channel)) {
			printf("Unable to request shell on allocated pty.\n");
    			libssh2_session_disconnect(session, "Normal Shutdown, Thank you for playing");
    			libssh2_session_free(session);
    			sleep(1);
    			close(sock);
			return 0;
    		}

		libssh2_channel_handle_extended_data(channel,LIBSSH2_CHANNEL_EXTENDED_DATA_IGNORE);
    		libssh2_channel_set_blocking(channel, 0);

		connected = true;

		return 1;
	}

	void disconnect(){
		if (connected){
			connected=false;
			if (session){
    				libssh2_session_disconnect(session, "Normal Shutdown, Thank you for playing");
    				libssh2_session_free(session);
    				sleep(1);
			}
 			close(sock);
		}
	}

	vector<string> exec(string scmd, int n){
		vector<string> results;
		string result;
		string s;
		string t0,t;
		int i,l;
		int diff;
		int pos;
		char buff[255];
		char cmd[255];

		if ( (!channel) || (!session) || (!connected) ){
			return results;
		}

   		memset( cmd, '\0', 255 );
		scmd.copy( cmd, scmd.size() );

    		libssh2_channel_write(channel, cmd, sizeof(cmd));

    		t0=timestamp();
		i=0;
		while(i<n){
			l=libssh2_channel_read(channel, buff, 255);
			if (l>0){
				s = buff;
				pos = s.find("\n");
				result = s.substr(0,pos+1);
				results.push_back(result);
				i++;
			}
        		t=timestamp();
			diff=(int)diff_timestamp(t,t0);
			if (diff>30){
				printf("Timeout executing command.\n");
				results.clear();
     				return results;
			}
		}
		return results;
	}
};

#endif

