/*
 * Copyright (c) 2003 Christian Loose <christian.loose@hamburg.de>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this program; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 *
 */

#include "sshagent.h"
#include "tdesvn-config.h"

#include <tqregexp.h>
#include <tdeapplication.h>
#include <tdeversion.h>
#include <kprocess.h>
#include <kdebug.h>

#include <stdlib.h>


// initialize static member variables
bool    SshAgent::m_isRunning  = false;
bool    SshAgent::m_isOurAgent = false;
bool    SshAgent::m_addIdentitiesDone = false;
TQString SshAgent::m_authSock   = TQString();
TQString SshAgent::m_pid        = TQString();


SshAgent::SshAgent(TQObject* parent, const char* name)
    : TQObject(parent, name)
{
}


SshAgent::~SshAgent()
{
}


bool SshAgent::querySshAgent()
{
    if( m_isRunning )
        return true;

    // Did the user already start a ssh-agent process?
    char* pid;
    if( (pid = ::getenv("SSH_AGENT_PID")) != 0 )
    {
        m_pid = TQString::fromLocal8Bit(pid);

        char* sock = ::getenv("SSH_AUTH_SOCK");
        if( sock )
            m_authSock = TQString::fromLocal8Bit(sock);
        /* make sure that we have a askpass program.
         * on some systems something like that isn't installed.*/
#ifdef FORCE_ASKPASS
        kdDebug()<<"Using test askpass"<<endl;
#ifdef HAS_SETENV
            ::setenv("SSH_ASKPASS",FORCE_ASKPASS,1);
#else
            ::putenv("SSH_ASKPASS="FORCE_ASKPASS);
#endif
#else
/*
        char*agent = ::getenv("SSH_ASKPASS");
        if (!agent) {
*/
#ifdef HAS_SETENV
            ::setenv("SSH_ASKPASS", "tdesvnaskpass",1);
#else
            ::putenv("SSH_ASKPASS=tdesvnaskpass");
#endif
/*
        }
*/
#endif
        m_isOurAgent = false;
        m_isRunning  = true;
    }
    // We have to start a new ssh-agent process
    else
    {
        m_isOurAgent = true;
        m_isRunning  = startSshAgent();
    }

    return m_isRunning;
}


bool SshAgent::addSshIdentities(bool force)
{
    if (m_addIdentitiesDone && !force) {
        return true;
    }


    if( !m_isRunning || (!m_isOurAgent&&!force)) {
        return false;
    }

    // add identities to ssh-agent
    TDEProcess proc;

    proc.setEnvironment("SSH_AGENT_PID", m_pid);
    proc.setEnvironment("SSH_AUTH_SOCK", m_authSock);

#ifdef FORCE_ASKPASS
    kdDebug()<<"Using test askpass"<<endl;
    proc.setEnvironment("SSH_ASKPASS",FORCE_ASKPASS);
#else
    char*agent = 0;
/*
    if (force) {
        agent = ::getenv("SSH_ASKPASS");
    }
*/
    if (!agent) {
        proc.setEnvironment("SSH_ASKPASS", "tdesvnaskpass");
    }
#endif

    proc << "ssh-add";

    connect(&proc, TQ_SIGNAL(receivedStdout(TDEProcess*, char*, int)),
            TQ_SLOT(slotReceivedStdout(TDEProcess*, char*, int)));
    connect(&proc, TQ_SIGNAL(receivedStderr(TDEProcess*, char*, int)),
            TQ_SLOT(slotReceivedStderr(TDEProcess*, char*, int)));

    proc.start(TDEProcess::DontCare, TDEProcess::AllOutput);

    // wait for process to finish
    // TODO CL use timeout?
    proc.wait();

    m_addIdentitiesDone = proc.normalExit() && proc.exitStatus() == 0;
    return m_addIdentitiesDone;
}


void SshAgent::killSshAgent()
{
    if( !m_isRunning || !m_isOurAgent )
        return;

    TDEProcess proc;

    proc << "kill" << m_pid;

    proc.start(TDEProcess::DontCare, TDEProcess::NoCommunication);
}


void SshAgent::slotProcessExited(TDEProcess*)
{
    TQRegExp cshPidRx("setenv SSH_AGENT_PID (\\d*);");
    TQRegExp cshSockRx("setenv SSH_AUTH_SOCK (.*);");

    TQRegExp bashPidRx("SSH_AGENT_PID=(\\d*).*");
    TQRegExp bashSockRx("SSH_AUTH_SOCK=(.*\\.\\d*);.*");
    TQStringList m_outputLines = TQStringList::split("\n",m_Output);

    TQStringList::Iterator it  = m_outputLines.begin();
    TQStringList::Iterator end = m_outputLines.end();
    for( ; it != end; ++it )
    {
        if( m_pid.isEmpty() )
        {
            int pos = cshPidRx.search(*it);
            if( pos > -1 )
            {
                m_pid = cshPidRx.cap(1);
                continue;
            }

            pos = bashPidRx.search(*it);
            if( pos > -1 )
            {
                m_pid = bashPidRx.cap(1);
                continue;
            }
        }

        if( m_authSock.isEmpty() )
        {
            int pos = cshSockRx.search(*it);
            if( pos > -1 )
            {
                m_authSock = cshSockRx.cap(1);
                continue;
            }

            pos = bashSockRx.search(*it);
            if( pos > -1 )
            {
                m_authSock = bashSockRx.cap(1);
                continue;
            }
        }
    }

}


void SshAgent::slotReceivedStdout(TDEProcess* proc, char* buffer, int buflen)
{
    Q_UNUSED(proc);

    TQString output = TQString::fromLocal8Bit(buffer, buflen);
    m_Output+=output;
}


void SshAgent::slotReceivedStderr(TDEProcess* proc, char* buffer, int buflen)
{
    Q_UNUSED(proc);

    TQString output = TQString::fromLocal8Bit(buffer, buflen);
    m_Output+=output;
}


bool SshAgent::startSshAgent()
{
    TDEProcess proc;

    proc << "ssh-agent";

    connect(&proc, TQ_SIGNAL(processExited(TDEProcess*)),
            TQ_SLOT(slotProcessExited(TDEProcess*)));
    connect(&proc, TQ_SIGNAL(receivedStdout(TDEProcess*, char*, int)),
            TQ_SLOT(slotReceivedStdout(TDEProcess*, char*, int)));
    connect(&proc, TQ_SIGNAL(receivedStderr(TDEProcess*, char*, int)),
            TQ_SLOT(slotReceivedStderr(TDEProcess*, char*, int)) );

    proc.start(TDEProcess::NotifyOnExit, TDEProcess::All);

    // wait for process to finish
    // TODO CL use timeout?
    proc.wait();

    return (proc.normalExit() && proc.exitStatus() == 0);
}

#include "sshagent.moc"
