#include <errno.h>
#include <libssh/libssh.h>

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

/* translation notes : reponse = reply; debut = start; fin = end; sortie = out; erreur = error */


typedef struct reponse
{
	int etat; /* REPONSE_OK ou REPONSE_ERREUR */
	char sortie[BUF_SIZE]; /* stdout de la commande */
	char erreur[BUF_SIZE]; /* stderr de la commande */
} reponse;

#define DEBUT_REPONSE reponse * res = malloc(sizeof(reponse))
#define FIN_REPONSE free(res)


void select_loop(reponse *res, SSH_SESSION *session,CHANNEL *channel)
{
    fd_set fds;
    struct timeval timeout;
    BUFFER *readbuf=buffer_new();
    CHANNEL *channels[2];
    char buffer[10];
    int lus, maxfd, ret, eof=0;

    while(channel)
    {

        do
        {
        	// Init fd de base
            FD_ZERO(&fds);
            if(!eof)
                FD_SET(0,&fds);
            timeout.tv_sec=30;
            timeout.tv_usec=0;
            FD_SET(ssh_get_fd(session),&fds);
            maxfd=ssh_get_fd(session)+1;
           	// On attend qu'un channel soit operationnel
            ret=select(maxfd,&fds,NULL,NULL,&timeout);
            if(ret==EINTR)
                continue;
                
            if(FD_ISSET(0,&fds))
            {
                lus=read(0,buffer,10);
                if(lus)
                    channel_write(channel,buffer,lus);
                else 
                {
                    eof=1;
                    channel_send_eof(channel);
                }
            }
            
            	// On declare qu'y a de quoi lire apres l'avoir ecrit
            if(FD_ISSET(ssh_get_fd(session),&fds))
            {
                ssh_set_fd_toread(session);
            }
            
            channels[0]=channel; 
            channels[1]=NULL;
            ret=channel_select(channels,NULL,NULL,NULL); 

        } while (ret==EINTR || ret==SSH_EINTR);
        
        if(channel && channel_is_closed(channel))
        {
            channel_free(channel);
            channel=NULL;
            channels[0]=NULL;
        }
        
        // On lis la réponse du serveur
        if(channels[0])
        {
        	// stdout deja
            while(channel && channel_is_open(channel) && channel_poll(channel,0))
            {
                lus=channel_read(channel,readbuf,0,0);
                if(lus==-1)
                {
                	res->etat=REPONSE_ERREUR;
                    sprintf(res->erreur,"Erreur en lisant le channel : %s\n",ssh_get_error(session));
                    return;
                }
                if(lus==0)
                {
                    ssh_say(1,"EOF received\n");
                    channel_free(channel);
                    channel=channels[0]=NULL;
                } else // On ecrit le stdout
                {
                 	res->etat=REPONSE_OK;
                    sprintf(res->sortie, lus, buffer_get(readbuf));
                }    
            }
            	// stderr ensuite
            while(channel && channel_is_open(channel) && channel_poll(channel,1))
            { 
                lus=channel_read(channel,readbuf,0,1);
                if(lus==-1)
                {
                    sprintf(res,"error reading channel : %s\n",ssh_get_error(session));
                    return;
                }
                if(lus==0)
                {
                    ssh_say(1,"EOF received\n");
                    channel_free(channel);
                    channel=channels[0]=NULL;
                } else // On ecrit le stderr
                {
                	res->etat=REPONSE_OK;
                    sprintf(res->erreur, lus, buffer_get(readbuf));
                }
            }
        }
        if(channel && channel_is_closed(channel))
        {
            channel_free(channel);
            channel=NULL;
        }
    }
    buffer_free(readbuf);
}


void batch_shell(reponse* res, SSH_SESSION* session, char* cmd)
{
    CHANNEL* channel=channel_new(session);
    printf("%d\n", channel_open_session(channel));
    if(channel_request_exec(channel,cmd))
    {
    	res->etat=REPONSE_ERREUR;
        sprintf(res->erreur, "error executing \"%s\" : %s\n", cmd, ssh_get_error(session));
        return;
    }
//    select_loop(res,session,channel);
}

int ssh_init(SSH_SESSION* session, SSH_OPTIONS* options, char* user, char* passwd, char* host, char* pubkey, char* privkey)
{
    int auth=0, i;
    //STRING* st;
    char * st;
    PRIVATE_KEY * key;

/* On definit les params de la connexion ssh */

    ssh_options_set_username(options, user); 
    ssh_options_set_host(options,host);
    session=ssh_new();
    ssh_set_options(session,options);
    if(ssh_connect(session))
    {
        ssh_disconnect(session);
	ssh_finalize();
        return 1;
    }
    
    st=publickey_from_file(session, pubkey, &i);
    ssh_userauth_offer_pubkey(session,user,i,st);
    
    key=privatekey_from_file(session, privkey, i, passwd);
    auth=ssh_userauth_pubkey(session, user, st, key);
    
    free(st);
    private_key_free(key);
    
    return auth;
}

void ssh_exec(reponse* res, char* user, char* passwd, char* host, char* pubkey, char* privkey, char* cmd)
{
    int auth; 
    SSH_SESSION* session = ssh_new();
    SSH_OPTIONS* options = ssh_options_new();
 
    auth = ssh_init(session, options, user, passwd, host, pubkey, privkey);
 
    if(auth==SSH_AUTH_ERROR)
    {
    	res->etat=REPONSE_ERREUR;
        sprintf(res->erreur,"Mauvaise clé : %s\n",ssh_get_error(session));
	ssh_finalize();
        return;
    }

    if(auth!=SSH_AUTH_SUCCESS)
    {
    	res->etat=REPONSE_ERREUR;
        sprintf(res->erreur,"Authentification par mot de passe impossible !");
	ssh_finalize();
        return;
    }
 
    batch_shell(res, session, cmd);
	
    ssh_disconnect(session);
    ssh_finalize();
   
}

void main(int argc, char ** argv)
{
	DEBUT_REPONSE;
	ssh_exec(res,"user","pass","host","/to/id_rsa.pub", "/to/id_rsa", "ls");
	printf("%d :: %s :: %s \n", res->etat, res->sortie, res->erreur);
	FIN_REPONSE;
}

