/*
 * 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 <tqregexp.h>
#include <kapplication.h>
#include <kdebug.h>
#include <kdeversion.h>
#include <kprocess.h>

#include <stdlib.h>


// initialize static member variables
bool    SshAgent::m_isRunning  = false;
bool    SshAgent::m_isOurAgent = 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()
{
    kdDebug(8051) << "SshAgent::querySshAgent(): ENTER" << endl;

    if( m_isRunning )
        return true;

    // Did the user already start a ssh-agent process?
    char* pid;
    if( (pid = ::getenv("SSH_AGENT_PID")) != 0 )
    {
        kdDebug(8051) << "SshAgent::querySshAgent(): ssh-agent already exists"
                      << endl;

        m_pid = TQString::fromLocal8Bit(pid);

        char* sock = ::getenv("SSH_AUTH_SOCK");
        if( sock )
            m_authSock = TQString::fromLocal8Bit(sock);

        m_isOurAgent = false;
        m_isRunning  = true;
    }
    // We have to start a new ssh-agent process
    else
    {
        kdDebug(8051) << "SshAgent::querySshAgent(): start ssh-agent" << endl;

        m_isOurAgent = true;
        m_isRunning  = startSshAgent();
    }

    return m_isRunning;
}


bool SshAgent::addSshIdentities()
{
    kdDebug(8051) << "SshAgent::addSshIdentities(): ENTER" << endl;

    if( !m_isRunning || !m_isOurAgent )
        return false;

    // add identities to ssh-agent
    KProcess proc;

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

    proc << "ssh-add";

    connect(&proc, TQT_SIGNAL(receivedStdout(KProcess*, char*, int)),
            TQT_SLOT(slotReceivedStdout(KProcess*, char*, int)));
    connect(&proc, TQT_SIGNAL(receivedStderr(KProcess*, char*, int)),
            TQT_SLOT(slotReceivedStderr(KProcess*, char*, int)));

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

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

    kdDebug(8051) << "SshAgent::slotProcessExited(): added identities" << endl;

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


void SshAgent::killSshAgent()
{
    kdDebug(8051) << "SshAgent::killSshAgent(): ENTER" << endl;

    if( !m_isRunning || !m_isOurAgent )
        return;

    KProcess proc;

    proc << "kill" << m_pid;

    proc.start(KProcess::DontCare, KProcess::NoCommunication);

    kdDebug(8051) << "SshAgent::killSshAgent(): killed pid = " << m_pid << endl;
}


void SshAgent::slotProcessExited(KProcess*)
{
    kdDebug(8051) << "SshAgent::slotProcessExited(): ENTER" << endl;

    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::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;
            }
        }
    }

    kdDebug(8051) << "SshAgent::slotProcessExited(): pid = " << m_pid
                  << ", socket = " << m_authSock << endl;
}


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

    TQString output = TQString::fromLocal8Bit(buffer, buflen);
    m_outputLines += TQStringList::split("\n", output);

    kdDebug(8051) << "SshAgent::slotReceivedStdout(): output = " << output << endl;
}


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

    TQString output = TQString::fromLocal8Bit(buffer, buflen);
    m_outputLines += TQStringList::split("\n", output);

    kdDebug(8051) << "SshAgent::slotReceivedStderr(): output = " << output << endl;
}


bool SshAgent::startSshAgent()
{
    kdDebug(8051) << "SshAgent::startSshAgent(): ENTER" << endl;

    KProcess proc;

    proc << "ssh-agent";

    connect(&proc, TQT_SIGNAL(processExited(KProcess*)),
            TQT_SLOT(slotProcessExited(KProcess*)));
    connect(&proc, TQT_SIGNAL(receivedStdout(KProcess*, char*, int)),
            TQT_SLOT(slotReceivedStdout(KProcess*, char*, int)));
    connect(&proc, TQT_SIGNAL(receivedStderr(KProcess*, char*, int)),
            TQT_SLOT(slotReceivedStderr(KProcess*, char*, int)) );

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

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

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


#include "sshagent.moc"
