/****************************************************************************
 ** $Id: torclient.cpp,v 1.138 2009/10/13 20:19:51 hoganrobert Exp $
 *   Copyright (C) 2006 - 2008 Robert Hogan                                *
 *   robert@roberthogan.net                                                *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU 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 General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.              *
 ***************************************************************************/


#include <qsocket.h>
#include <qtextstream.h>
#include <qstringlist.h>
#include <qregexp.h>
#include "torclient.h"
#include "tork.h"
#include "torkconfig.h"
#include "dndlistview.h"
#include "functions.h"

#include <qtimer.h>
#include <klocale.h>
#include <assert.h>
#include <qfile.h>
#include <sys/socket.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <kstandarddirs.h>
#include <qdir.h>
#include "crypto.h"

#ifndef EXTERNAL_GEOIP
# include "GeoIP-1.4.0/libGeoIP/GeoIP.h"
#else
# include <GeoIP.h>
#endif

/* Linux-specific includes */
#include <dirent.h>
#include <unistd.h>

using namespace tk;


TorClient::TorClient( const QString &host, Q_UINT16 port ) 
{
    // create the socket and connect various of its signals
    socket = new QSocket( this );
    connect( socket, SIGNAL(connected()),
            SLOT(socketConnected()) );
    connect( socket, SIGNAL(connectionClosed()),
            SLOT(socketConnectionClosed()) );
    connect( socket, SIGNAL(readyRead()),
            SLOT(socketReadyRead()) );
    connect( socket, SIGNAL(error(int)),
            SLOT(socketError(int)) );

    // connect to the server
    //infoText->append( tr("Trying to connect to the server\n") );
    socket->connectToHost( host, port );
    m_expectingCircuitInfo= false;
    m_expectingStreamInfo= false;
    m_expectingOrconnInfo= false;
    m_expectingGuardsInfo= false;
    m_expectingDirStatus= false;
    m_expectingServerInfo= false;
    m_controllerWorking= false;
    m_firstloadcomplete = false;
    m_resolvingServerAddress = false;

    clearServers();

    serverReport.append("<font color='#990000'>Status Not Known</font>");
    clientReport.append("<font color='#990000'>Status Not Known</font>");
    serverStatus["DANGEROUS_VERSION"] = "<font color='#990000'>Server Requires Upgrade</font>";
    serverStatus["TOO_MANY_CONNECTIONS"] = "<font color='#990000'>Recently Exceeded Local Connection Limit. Check Local System</font>";
    serverStatus["CLOCK_SKEW"] = "<font color='#990000'>Your Local Clock Is Skewed. Check Local System.</font>";
    serverStatus["BAD_LIBEVENT"] = "<font color='#990000'>Libevent Installation Requires Upgrade</font>";
    serverStatus["DIR_ALL_UNREACHABLE"] = "<font color='#990000'>Tor Network Unreachable.</font>";
//    serverStatus["NAMESERVER_ALL_DOWN"] = "Your DNS Servers are down.";
    serverStatus["DNS_HIJACKED"] = "<font color='#990000'>Your Provider is Hijacking DNS Requests.</font>";
    serverStatus["DNS_USELESS"] = "<font color='#990000'>Your Provider is Hijacking DNS Requests.</font>";
    serverStatus["EXTERNAL_ADDRESS"] = "Using Address ADDRESS";

    serverStatus["REACHABILITY_SUCCEEDED"] = "Reachable By Tor Network";
    serverStatus["REACHABILITY_FAILED"] = "<font color='#990000'>Reachability Tests Failed. Trying again..</font>";
    serverStatus["BAD_SERVER_DESCRIPTOR"] = "<font color='#990000'>Server Not Accepted By Tor Network Yet.</font>";
    serverStatus["GOOD_SERVER_DESCRIPTOR"] = "Accepted By Tor Network";

    serverStatusIcon["REACHABILITY_SUCCEEDED"] = "tork_green";
    serverStatusIcon["REACHABILITY_FAILED"] = "tork_orange";
    serverStatusIcon["BAD_SERVER_DESCRIPTOR"] = "tork_little";
    serverStatusIcon["GOOD_SERVER_DESCRIPTOR"] = "tork_green";
    serverStatusIcon["DIR_ALL_UNREACHABLE"] = "tork_red";

    clientStatus["NOT_ENOUGH_DIR_INFO"] = "<font color='#990000'>Not Enough Info To Try Network Yet</font>";
    clientStatus["ENOUGH_DIR_INFO"] = "Enough Info To Try Network";
    clientStatus["CIRCUIT_ESTABLISHED"] = "Connected to Network.";
    clientStatus["CIRCUIT_NOT_ESTABLISHED"] = "<font color='#990000'>Still Trying Network</font>";

    clientStatusIcon[clientStatus["NOT_ENOUGH_DIR_INFO"]] = "tork_red";
    clientStatusIcon[clientStatus["ENOUGH_DIR_INFO"]] = "tork_little";
    clientStatusIcon[clientStatus["CIRCUIT_ESTABLISHED"]] = "tork_green";
    clientStatusIcon[clientStatus["CIRCUIT_NOT_ESTABLISHED"]] = "tork_orange";

    portMessage["23"] = " Port 23 is used by telnet, which transmits usernames "
                        "and passwords unencrypted.";
    portMessage["110"] = " Port 110 is used to download email, so your login "
                        "details can be transmitted unencrypted.";
    portMessage["109"] = " Port 109 is used to download email, so your login "
                        "details can be transmitted unencrypted.";
    portMessage["143"] = " Port 143 is used to download email, so your login "
                        "details can be transmitted unencrypted.";


    statusMessage["DANGEROUS_PORT"] = "QUESTIONMESSAGETraffic on Port PORT "
                                      "has been rejected by Tor.";
    statusMessage["DANGEROUS_VERSION"] = "QUESTIONMESSAGEYou are using Tor CURRENT."
                                         " This version is REASON. "
                                         "You should use Tor RECOMMENDED instead";
    statusMessage["TOO_MANY_CONNECTIONS"] = "MESSAGETor has reached its native" 
                                            " limit on file descriptors: CURRENT. ";
    statusMessage["BUG"] = "WARNINGMESSAGETor encountered an unexpected error: REASON. ";
    statusMessage["CLOCK_SKEW"] = "WARNINGMESSAGEYour local clock is skewed by SKEW seconds. ";
    statusMessage["BAD_LIBEVENT"] = "WARNINGMESSAGEYour version of libevent, VERSION, is BADNESS. ";
    statusMessage["DIR_ALL_UNREACHABLE"] = "WARNINGMESSAGEAll known directory servers are unreachable. ";
    statusMessage["ENOUGH_DIR_INFO"] = "WARNINGMESSAGETor has gathered enough info to start working. ";
    statusMessage["NOT_ENOUGH_DIR_INFO"] = "WARNINGMESSAGETor does not have enough info to work. ";
    statusMessage["CIRCUIT_ESTABLISHED"] = "WARNINGMESSAGETor has gathered enough info to start working. ";
    statusMessage["CIRCUIT_NOT_ESTABLISHED"] = "WARNINGMESSAGETor does not have enough info to work. ";
    statusMessage["SOCKS_BAD_HOSTNAME"] = "WARNINGMESSAGESome application gave us" 
                                          " a funny-looking hostname."
                                          "Perhaps it is broken?";
    statusMessage["NAMESERVER_ALL_DOWN"] = "WARNINGMESSAGEAll your configured nameservers appear to be down.";
    statusMessage["DNS_HIJACKED"] = "WARNINGMESSAGEYour DNS requests are being hijacked by your provider.";
    statusMessage["DNS_USELESS"] = "WARNINGMESSAGEYour DNS requests are being hijacked by your provider.";
    statusMessage["BAD_SERVER_DESCRIPTOR"] = "WARNINGMESSAGEYour descriptor was rejected by DIRAUTH "
                                             "because of REASON.";

    m_statustip = i18n("<b>Name:</b> $SERVERNAME<br>"
                     "<b>IP:</b> $IP ($HOSTNAME) <b>Port:</b> $PORT<br>"
                     "<b>Country:</b> $COUNTRY <br>"
                     "<b>Version:</b> $VERSION <b>OS:</b> $OS<br>"
                     "<b>Published:</b> $PUBLISHED <b>Up Time:</b> $UPTIME minutes<br>"
                     "<center><b>Avg BW up to $INTERVALTIME</b></center>"
                     "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"
                     "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"
                     "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<b>24 hrs</b>"
                     "&nbsp;&nbsp;&nbsp;" // 1 space
                     "&nbsp;&nbsp;&nbsp;" // 1 space
                     "&nbsp;&nbsp;&nbsp;" // 1 space
                     "&nbsp;&nbsp;&nbsp;" // 1 space
                     "&nbsp;&nbsp;&nbsp;<b>12 hrs</b>"
                     "&nbsp;&nbsp;&nbsp;" // 1 space
                     "&nbsp;&nbsp;&nbsp;" // 1 space
                     "&nbsp;&nbsp;&nbsp;" // 1 space
                     "&nbsp;&nbsp;&nbsp;" // 1 space
                     "&nbsp;&nbsp;&nbsp;<b>6 hrs</b>"
                     "&nbsp;&nbsp;&nbsp;" // 1 space
                     "&nbsp;&nbsp;&nbsp;" // 1 space
                     "&nbsp;&nbsp;&nbsp;" // 1 space
                     "&nbsp;&nbsp;&nbsp;" // 1 space
                     "&nbsp;&nbsp;&nbsp;" // 1 space
                     "&nbsp;&nbsp;&nbsp;<b>1 hr</b><br>"
                     "<b>Up</b>"
                     "&nbsp;&nbsp;&nbsp;&nbsp;" // 1 space
                     "&nbsp;&nbsp;&nbsp;&nbsp;" // 1 space
                     "&nbsp;&nbsp;&nbsp;&nbsp;" // 1 space
                     "<font color='#990000'>$BWUP</font><br>"
                     "<b>Down</b>"
                     "&nbsp;&nbsp;&nbsp;" // 1 space
                     "&nbsp;&nbsp;&nbsp;" // 1 space
                     "<font color='#1c9a1c'>$BWDN</font><br>"
                     );

}

void TorClient::configureServer( int orPort, int dirPort)
{

    sendToServer(QString("SETCONF ContactInfo=%1").arg(TorkConfig::contactInfo())) ;

    sendToServer(QString("SETCONF ClientOnly=%1").arg(TorkConfig::clientOnly())) ;


    if (TorkConfig::middleMan())
        ( sendToServer(QString("SETCONF ExitPolicy=%1").arg(("\"reject *:*\"")))) ;
    else
        ( sendToServer(QString("SETCONF ExitPolicy=\"%2\"").arg( TorkConfig::exitPolicy().join(","))));

    sendToServer(QString("SETCONF NickName=%1").arg(TorkConfig::nickName())) ;
    if (!TorkConfig::clientOnly()){
      //We send the orport configs together to avoid error messages from Tor
      //telling us that one cannot be set without the other.
      sendToServer(QString("SETCONF ORPort=%1 "
                           "ORListenAddress=0.0.0.0:%2")
                           .arg(orPort)
                           .arg(TorkConfig::oRListenAddress())) ;
      //We send the dirport configs together to avoid error messages from Tor
      //telling us that one cannot be set without the other.
      sendToServer(QString("SETCONF DIRPort=%1 "
                           "DIRListenAddress=0.0.0.0:%2")
                           .arg(dirPort)
                           .arg(TorkConfig::dirListenAddress())) ;
      sendToServer(QString("SETCONF BridgeRelay=%1").arg(TorkConfig::bridgeRelay())) ;
      setBandwidth(QString("%1").arg(TorkConfig::bandwidthRate()),
                   QString("%1").arg(TorkConfig::bandwidthBurst()),
                   QString("%1").arg(TorkConfig::maxAdvertisedBandwidth()));
    }else{
      sendToServer(QString("SETCONF ORPort= ORListenAddress=")) ;
      sendToServer(QString("SETCONF DirPort= DirListenAddress=")) ;
      sendToServer(QString("SETCONF BridgeRelay=")) ;
    }

    if (TorkConfig::clientOnly())
        resetClientReport();
    TorkConfig::writeConfig();
}
void TorClient::clearNodes( )
{
    
    sendToServer("SETCONF ExcludeNodes=");
    sendToServer("SETCONF ExitNodes=");
    sendToServer("SETCONF EntryNodes=");
    TorkConfig::setCurrentExcludeNodes("");
    TorkConfig::setCurrentEntryNodes("");
    TorkConfig::setCurrentExitNodes("");
    emit copyOldConfig();
}

void TorClient::updateExcludeNodes( )
{
    ////kdDebug() << "SETCONF ExcludeNodes=" + TorkConfig::currentExcludeNodes().join(",") << endl;
    sendToServer("SETCONF ExcludeNodes=" + TorkConfig::currentExcludeNodes().join(","));
    sendToServer("GETCONF ExcludeNodes");
    sendToServer("signal newnym");

}

void TorClient::updateExitNodes( )
{
    ////kdDebug() << "SETCONF ExitNodes=" + TorkConfig::currentExitNodes().join(",") << endl;
    sendToServer("SETCONF ExitNodes=" + TorkConfig::currentExitNodes().join(","));
    sendToServer("GETCONF ExitNodes");
    sendToServer("signal newnym");

}

void TorClient::strictExitNodes( bool strict )
{
    if (strict)
        sendToServer("SETCONF StrictExitNodes=1");
    else
        sendToServer("SETCONF StrictExitNodes=0");

}

void TorClient::safeLogging( bool safe )
{
    if (safe)
        sendToServer("SETCONF SafeLogging=1");
    else
        sendToServer("SETCONF SafeLogging=0");

}

void TorClient::updateEntryNodes( )
{
    ////kdDebug() << "SETCONF EntryNodes=" + TorkConfig::currentEntryNodes().join(",") << endl;
    sendToServer("SETCONF EntryNodes=" + TorkConfig::currentEntryNodes().join(","));
    sendToServer("GETCONF EntryNodes");
    sendToServer("signal newnym");

}

void TorClient::enableDNS( bool set )
{
    if (set)
        sendToServer("SETCONF DNSPort=9999");
    else
        sendToServer("SETCONF DNSPort=");

}

void TorClient::enableTransPort( bool set )
{
    if (set)
        sendToServer("SETCONF TransPort=9040");
    else
        sendToServer("SETCONF TransPort=");

}

void TorClient::fetchServerInfo( const QString & server)
{

    QString fp = getFPFromFPDigest(server);
    fp.replace("$","");

    kdDebug() << fp << endl;
//    emit showServerBW(fp);

    sendToServer("GETINFO dir/server/fp/" + fp);
}

void TorClient::fetchServerInfoByNick( const QString & server)
{

    
    QString fp = getFPFromNickName(server);
    fp.replace("$","");

    sendToServer("GETINFO dir/server/fp/" + fp);
}

void TorClient::slotCheckTorNet()
{
    sendToServer("GETINFO ns/all");
}

void TorClient::slotCheckGuards()
{

    sendToServer("GETINFO entry-guards");

}

void TorClient::terminateTor()
{

    sendToServer("SIGNAL SHUTDOWN");

}

void TorClient::createService(const QString &dir, const QString &port)
{
    sendToServer(QString("setconf hiddenservicedir=\"%1\" hiddenserviceport=\"%2\"").arg(dir).arg(port));
}

void TorClient::authenticate()
{

//     if (TorkConfig::defaultRunningNormalOptions()){
//         sendToServer("AUTHENTICATE");
//         return;
//     }

    if (TorkConfig::cookieAuthentication()){
        if (!readCookie()){
            emit processQuestion("cookienotfound", 
                                QString("Couldn't find authentication" 
                                        " cookie in %1/.tor!").arg(getenv("HOME")));
            emit fatalError();
        }
    }else if (!TorkConfig::hashedControlPassword().isEmpty())
        sendToServer(QString("AUTHENTICATE \"%1\"").arg(TorkConfig::hashedControlPassword()));
    else{
        sendToServer("AUTHENTICATE");
        /* Lock the control port */
        if (TorkConfig::generateRandomPassword()){
            crypto_seed_rng();
            sendToServer(QString("setconf HashedControlPassword=16:%2")
                         .arg(hashPassword(crypto_rand_string(16))));
        }
    }

}


bool TorClient::readCookie()
{

    QString hex;
    char hx[2];

    QStringList cookieCandidates;
    cookieCandidates << QString("%1/.tor/control_auth_cookie").arg(getenv("HOME"));
    cookieCandidates << QString("/var/lib/tor/control_auth_cookie");

    for ( QStringList::Iterator it = cookieCandidates.begin(); it != cookieCandidates.end(); ++it ) {
        QFile inf((*it));
        if ( inf.open(IO_ReadOnly) ) {
            QByteArray array = inf.readAll();
            inf.close();
            if (array.size() != 32)
                continue;
            for ( unsigned int i = 0; i < array.size(); i++ ) {
                sprintf(hx,"%02x",array[i]);
                hex += QString(hx).right(2);
            }
            sendToServer(QString("AUTHENTICATE %1").arg(hex));
            return true;
    
        }
    }

    return false; 

}

void TorClient::readRouters()
{

    QFile inf(QString("%1/.tor/cached-status/7EA6EAD6FD83083C538F44038BBFA077587DD755").arg(getenv("HOME")));
    if ( inf.open(IO_ReadOnly) ) {
        QTextStream stream( &inf );
        QString line;
        while ( !stream.atEnd() ) {
            line = stream.readLine(); // line of text excluding '\n'
            parseDirStatus(line);
        }
        inf.close();
    }

 
}

void TorClient::newIdentity()
{
    kdDebug() << "changing id" << endl;
    sendToServer("signal newnym");

}

void TorClient::bandwidth()
{

    sendToServer("usefeature verbose_names");
    sendToServer("usefeature extended_events");
    sendToServer("GETINFO ns/all");
    sendToServer("GETINFO circuit-status");
    sendToServer("GETINFO stream-status");
    sendToServer("GETINFO orconn-status");
    sendToServer("GETINFO version");
    sendToServer("GETINFO status/enough-dir-info");
    sendToServer("GETINFO status/good-server-descriptor");
    sendToServer("GETINFO status/reachability-succeeded/or");

    //Always enable for each session, user can disable through yes/no
    //interface when warned - but only for that session.
    m_portsToReject.clear();
    m_portsToReject << "23" << "109" << "110" << "143";
    sendToServer(QString("SETCONF WarnPlainTextPorts=%1")
                 .arg(m_portsToReject.join(",")));
    sendToServer(QString("SETCONF RejectPlainTextPorts=%1")
                .arg(m_portsToReject.join(",")));
    sendToServer("SETEVENTS EXTENDED CIRC  STREAM  ORCONN  NOTICE "
                  "WARN  ERR  ADDRMAP BW STREAM_BW NS STATUS_GENERAL "
                  "STATUS_CLIENT STATUS_SERVER GUARD");
    sendToServer(QString("SETCONF __ReloadTorrcOnSIGHUP=0"));


}

void TorClient::handle250(const QString &lin)
{


    QString line = lin;

    if ((line.contains("250-circuit-status="))){
        if (line != ".")
            parseEvent("CIRC",line.replace("250-circuit-status=",""));
        else
            m_expectingCircuitInfo = false;
    }else if ((line.contains("250-orconn-status="))){
        if (line != ".")
            parseEvent("ORCONN",line.replace("250-orconn-status=",""));
        else
            m_expectingOrconnInfo = false;
    }else if ((line.contains("250-stream-status="))){
        if (line != ".")
            parseEvent("STREAM",line.replace("250-stream-status=",""));
        else
            m_expectingStreamInfo = false;
    }else if (line.contains("250+circuit-status="))
        m_expectingCircuitInfo= true;
    else if (line.contains("250+orconn-status="))
        m_expectingOrconnInfo= true;
    else if (line.contains("250+stream-status="))
        m_expectingStreamInfo= true;
    else if (line.contains("250+entry-guards="))
        m_expectingGuardsInfo= true;
    else if (line.contains("250+dir/server/fp/"))
        m_expectingServerInfo= true;
    else if (line.contains("250+extra-info/digest/"))
        m_expectingServerInfo= true;
    else if (line.contains("250+ns/all=")){
        m_expectingDirStatus= true;
        emit whatImDoing("Inspecting the Tor network..");
    }else if (line.contains("250-ns/all=")){
        emit warnNoServerInfo();
        emit shouldIApplySettings();
    }else if (line.contains("250-version="))
        handleTorVersion(line.section("=",1,1));
    else if (line.contains("250 BandwidthRate="))
        m_CurBandwidthRate = line.section("=",1,1).toInt();
    else if (line.contains("250 BandwidthBurst="))
        m_CurBandwidthBurst = line.section("=",1,1).toInt();
    else if (line.contains("250 MaxAdvertisedBandwidth="))
        m_CurMaxAdvertisedBandwidth = line.section("=",1,1).toInt();
    else if (line.contains("250 ExcludeNodes="))
        TorkConfig::setCurrentExcludeNodes(QStringList::split(",",line.replace("250 ExcludeNodes=","")));
    else if (line.contains("250 EntryNodes="))
        TorkConfig::setCurrentEntryNodes(QStringList::split(",",line.replace("250 EntryNodes=","")));
    else if (line.contains("250 ExitNodes="))
        TorkConfig::setCurrentExitNodes(QStringList::split(",",line.replace("250 ExitNodes=","")));
    else if (line.contains("250-status/circuit-established=1"))
        updateClientReport("CIRCUIT_ESTABLISHED");
    else if (line.contains("250-status/circuit-established=0"))
        updateClientReport("CIRCUIT_NOT_ESTABLISHED");
    else if (line.contains("250-status/enough-dir-info=1")){
        updateClientReport("ENOUGH_DIR_INFO");
        sendToServer("GETINFO status/circuit-established");
    }else if (line.contains("250-status/enough-dir-info=0"))
        updateClientReport("NOT_ENOUGH_DIR_INFO");
    else if (line.contains("250-status/good-server-descriptor=1"))
        updateServerReport("GOOD_SERVER_DESCRIPTOR", QString());
    else if (line.contains("250-status/good-server-descriptor=0"))
        updateServerReport("BAD_SERVER_DESCRIPTOR", QString());
    else if (line.contains("250-status/reachability-succeeded/or=1"))
        updateServerReport("REACHABILITY_SUCCEEDED", QString());
    else if (line.contains("250-status/reachability-succeeded/or=0"))
        updateServerReport("REACHABILITY_FAILED", QString());

}

void TorClient::socketReadyRead()
{
    QString line;
    // read from the server
    while ( socket->canReadLine() ) {

        line = (socket->readLine()).stripWhiteSpace();

        if (line.contains("250 OK")){
            if (!m_controllerWorking){
                bandwidth();
                emit authenticated();
                m_controllerWorking = true;
            }
            continue;
        }

        if (m_expectingDirStatus){
            if (!(line == ".")){
                parseDirStatus(line);
            }else{
                m_expectingDirStatus = false;
                sendToServer("GETINFO entry-guards");
                if (!m_firstloadcomplete)
                    emit shouldIApplySettings();
                m_firstloadcomplete = true;
            }
            continue;
        }else if ((m_expectingCircuitInfo)){
            if (line != "."){
                parseEvent("CIRC",line);
            }else
                m_expectingCircuitInfo = false;
            continue;
        }else if ((m_expectingOrconnInfo)){
            if (line != "."){
                parseEvent("ORCONN",line);
            }else
                m_expectingOrconnInfo = false;
            continue;
        }else if ((m_expectingStreamInfo)){
            if (line != "."){
                parseEvent("STREAM",line);
            }else
                m_expectingStreamInfo = false;
            continue;
        }else if (m_expectingServerInfo){
            if (line != "."){
                parseEvent("SERVER",line);
            }else
                m_expectingServerInfo = false;
            continue;
        }else if (m_expectingGuardsInfo){
            if (line != "."){
                parseEvent("GUARDS",line);
                emit whatImDoing(i18n("Ready for use."));
            }else{
                m_expectingGuardsInfo = false;
            }
            continue;
        }

        if (line.contains("552 Unrecognized key \"ns/all\"")){
            emit needAlphaVersion();
            emit shouldIApplySettings();
            continue;
        }

        QString code = line.left(3);

        if (code == "250")
            handle250(line);
        else if (code == "650"){
            if (line.contains("650+NS")){
                m_expectingDirStatus= true;
                continue;
            }
            QString eventType = line.section(" ",1,1);
            QString eventInfo = line.section(" ",2);
            if (eventInfo.contains("circuit_testing_failed"))
                emit serverError();
            parseEvent(eventType,eventInfo);
        }else if (code == "552"){
            QString eventInfo = line.section(" ",1);
            emit displayError("Sorry!", eventInfo);
        }else if (code == "514"){
            QString eventInfo = line.section(" ",1);
            emit processWarning("authenticationrequired", eventInfo);
            emit fatalError();
        }else if (code == "515"){
            QString eventInfo = line.section(" ",1);
            if (eventInfo.contains("Wrong length"))
                emit processQuestion("cookierequired", eventInfo);
            else{
                if (TorkConfig::generateRandomPassword())
                    emit processQuestion("passwordlost", eventInfo);
                else
                    emit processWarning("authenticationfailed", eventInfo);
            }
			//Only used by the first-run wizard
            emit authenticationFailed();
        }


    }
}

void TorClient::parseEvent(const QString &type, const QString &info)
{

    if (info.isEmpty())
        return;

    if (type == "STREAM")
        parseStream(info);
    else if (type == "ORCONN")
        parseORConn(info);
    else if (type == "CIRC")
        parseCircuit(info);
    else if (type == "GUARDS")
        parseGuards(info);
    else if (type == "GUARD")
        parseGuards(info.section(" ",1));
    else if (type == "SERVER")
        parseServer(info);
    else if (type == "DIRSTATUS")
        parseDirStatus(info);
    else if (type == "BW")
        parseBW(info);
    else if (type == "STREAM_BW")
        parseStreamBW(info);
    else if (type.contains( "STATUS_"))
        parseStatusGeneral(info);
    else if (type == "ADDRMAP")
        parseAddrmap(info);
/*    else if (type == "STREAM_PORT")
        parseStreamPort(info);*/
    else if ((type == "WARN") || (type == "NOTICE") || (type == "ERR"))
        parseInfo(type,info);
}

void TorClient::parseStatusGeneral(const QString &info)
{
    QString severity = info.section(" ",0,0);
    QString action = info.section(" ",1,1);
    QString message = statusMessage[action];

    if (!serverStatus[action].isEmpty())
        updateServerReport(action, info);

    if (!clientStatus[action].isEmpty())
        updateClientReport(action);

/*    kdDebug() << info << endl;
    kdDebug() << info.section(" ",2) << endl;*/
    populateMessageFromStatusDetail(info.section(" ",2), message);

    if (message.left(14) == "WARNINGMESSAGE"){
        message.replace("WARNINGMESSAGE","");
        emit processWarning(action, message);
    }else{
        message.replace("QUESTIONMESSAGE","");
        emit processQuestion(action, message);
    }

}

void TorClient::populateMessageFromStatusDetail(const QString &line, QString &message)
{
    QRegExp rx("[\\sA-Z0-9]+[=]([\\-\\:\\.\\(\\)a-zA-Z0-9]+|\\\"[\\-\\.\\,a-zA-Z0-9\\s]+\\\")");
    int pos = 0;
    while ( (pos = rx.search(line, pos)) != -1 ) {
/*        kdDebug() << rx.cap(0) << endl;*/
        QString keyword = rx.cap(0).section("=",0,0).stripWhiteSpace();
        QString value = rx.cap(0).section("=",1,1).stripWhiteSpace();
        message.replace(keyword,value);
        pos += rx.matchedLength();
        if (keyword=="PORT"){
            m_WarnedPorts << value;
            message.append(portMessage[value]);
        }
    }
}

void TorClient::updateServerReport(const QString &message, const QString &info)
{
//     kdDebug() << serverStatusIcon[message] << endl;
//     kdDebug() << message << endl;

    //If we're back to being a client, ensure the server symbol is removed from the tray icon
    if (TorkConfig::clientOnly()){
        emit updateTrayIcon(serverStatusIcon[message].replace("server",""));
        return;
    }
    if (!serverStatusIcon[message].isEmpty())
        emit updateTrayIcon(serverStatusIcon[message]);

    serverReport.remove("<font color='#990000'>Status Not Known</font>");
    QString msg = serverStatus[message];

    if (message.contains("EXTERNAL_ADDRESS")){
        for ( QStringList::Iterator it = serverReport.begin(); it != serverReport.end(); ++it )
        {
            // XXX Fixme
            if ((*it).contains("Using Address")){
                serverReport.remove((*it));
                break;
            }
        }
        populateMessageFromStatusDetail(info.section(" ",2),msg);
    }else if (message.contains("REACHABILITY")){
        serverReport.remove(serverStatus["REACHABILITY_FAILED"]);
        serverReport.remove(serverStatus["REACHABILITY_SUCCEEDED"]);
    }else if (message.contains("SERVER_DESCRIPTOR")){
        serverReport.remove(serverStatus["DIR_ALL_UNREACHABLE"]);
        serverReport.remove(serverStatus["BAD_SERVER_DESCRIPTOR"]);
        serverReport.remove(serverStatus["GOOD_SERVER_DESCRIPTOR"]);
    }else // Prevent multiple reports
        serverReport.remove(msg);

    serverReport.append(msg);
}

void TorClient::updateClientReport(const QString &message)
{
    if (!clientStatusIcon[message].isEmpty() && (TorkConfig::clientOnly()))
        emit updateTrayIcon(clientStatusIcon[clientStatus[message]]);
    clientReport.clear();
    clientReport.append(clientStatus[message]);
}

void TorClient::resetClientReport()
{
    emit updateTrayIcon(clientStatusIcon[clientReport.first()]);
}

void TorClient::parseBW(const QString &info)
{

    QString in = info.section(" ",0,0);
    QString out = info.section(" ",1,1);

    emit bwUpdate(in, out);

}


void TorClient::parseStreamBW(const QString &info)
{

    QString stream = info.section(" ",0,0);
    /* Tor spec had it wrong way round! */
    QString out = info.section(" ",1,1);
    QString in = info.section(" ",2,2);

    emit streamBwUpdate(stream, in, out);

}


void TorClient::parseStream(const QString &info)
{


    QString streamID = info.section(" ",0,0);
    QString status = info.section(" ",1,1);
    QString circID = info.section(" ",2,2);
    QString Target = info.section(" ",3,3);

    //We ignore REMAPs because we don't do anything with them
    if (status == "REMAP")
        return;

    emit streamStatusUpdate(streamID, status, circID, Target, info);

}

void TorClient::parseServer(const QString &info)
{

     kdDebug() << "server info " << info << endl;
    if (info.left(7) == "router "){
        QString ip = info.section(" ",2,2);
        QString cc;

        GeoIP * gi = 0;

        if (geoip_db)
#ifndef EXTERNAL_GEOIP
            gi = GeoIP_open(locate("data", "tork/geoip/GeoIP.dat").ascii(),0);
#else
            gi = GeoIP_new(GEOIP_STANDARD);
#endif

        if (gi){
            int country_id = 0;
            country_id = GeoIP_id_by_name(gi, ip);
            cc = GeoIP_country_name[country_id];
            GeoIP_delete(gi);
        }else
            cc = "a1";

        m_statustiptmp = m_statustip;
        m_statustipIP = ip;

        m_statustiptmp.replace("$SERVERNAME",info.section(" ",1,1));
        m_statustiptmp.replace("$IP",ip);
        m_statustiptmp.replace("$PORT",info.section(" ",3,3));
        m_statustiptmp.replace("$COUNTRY",cc);

    }else if (info.left(8) == "platform"){
        m_statustiptmp.replace("$VERSION",info.section(" ",1,2));
        m_statustiptmp.replace("$OS",info.section(" ",4).section("{",0,0));

    }else if (info.left(9) == "published"){
        m_statustiptmp.replace("$PUBLISHED",info.section(" ",1));

    }else if (info.left(6) == "uptime"){
        //from the clever ktorrent
        KLocale* loc = KGlobal::locale();
        QTime t;
        int nsecs = info.section(" ",1).toInt();
        int ndays = (nsecs) / 86400;
        t = t.addSecs(nsecs % 86400);
        QString s = loc->formatTime(t,true,true);
        if (ndays > 0)
            s = i18n("1 day ","%n days ",ndays) + s;

        m_statustiptmp.replace("$UPTIME",s);


    }else if (info.left(20).contains("write-history")){
        QStringList bwlist = QStringList::split(",",info.section(" ",-1));

        QValueList<int> bws;
        bws << 4 << 24 << 48 << bwlist.count();

        QString bwup;
        QString avgbw;
        for ( QValueList<int>::Iterator it = bws.begin(); it != bws.end(); ++it ){
          avgbw = calcBW(bwlist, (*it));
          for (int i = avgbw.length(); i < 14; i++)
              avgbw.append("&nbsp;&nbsp;");
          bwup.append(avgbw);
        }

        m_statustiptmp.replace("$BWUP",bwup);

        m_statustiptmp.replace("$INTERVALTIME", info.section(" ",2,3));

    }else if (info.left(20).contains("read-history")){
        QStringList bwlist = QStringList::split(",",info.section(" ",-1));
        QValueList<int> bws;
        bws << 4 << 24 << 48 << bwlist.count();

        QString bwup;
        QString avgbw;
        for ( QValueList<int>::Iterator it = bws.begin(); it != bws.end(); ++it ){
          avgbw = calcBW(bwlist, (*it));
          for (int i = avgbw.length(); i < 14; i++)
              avgbw.append("&nbsp;&nbsp;");
          bwup.append(avgbw);
        }
        m_statustiptmp.replace("$BWDN",bwup);

        
        if (m_currentTorVersion.left(3) == "0.2"){
            m_resolvingServerAddress=true;
            sendToServer("RESOLVE mode=reverse " + m_statustipIP);
        }else{
            m_statustiptmp.replace("($HOSTNAME)","");
            emit displayServer("Server Info", m_statustiptmp);
        }
    }else if (info.left(25).contains("opt extra-info-digest")){
        if (m_currentTorVersion.left(3) == "0.2"){
            sendToServer("GETINFO extra-info/digest/" + info.section(" ",2));
        }else{
            m_statustiptmp.replace("($HOSTNAME)","");
            m_statustiptmp.replace("$BWDN","Unavailable");
            m_statustiptmp.replace("$BWUP","Unavailable");
            m_statustiptmp.replace("$INTERVALTIME", "Unavailable");
            emit displayServer("Server Info", m_statustiptmp);
        }

    }
}

void TorClient::parseAddrmap(const QString &info)
{


    QString type = info.section(" ",0,0);
    QString address = info.section(" ",1,1);

    // If this is a request to resolve a hostname/address from the traffic
    // logs
    if (logResolveList.contains(type)){
        emit resolvedAddress(info);
        logResolveList.remove(logResolveList.find(type));
        return;
    }

    if (!m_resolvingServerAddress)
        return;

    // If this is a request to resolve a node name from the server list
    if (type.startsWith("REVERSE")){
        m_statustiptmp.replace("$HOSTNAME",address);
    }else
        m_statustiptmp.replace("$HOSTNAME","Cannot Resolve Hostname.");

    emit displayServer("Server Info", m_statustiptmp);
    m_resolvingServerAddress = false;

}

void TorClient::parseGuards(const QString &info)
{

    QString fp_identity = info.section(" ",0,0);
    QString status = info.section(" ",1,1);
    QRegExp rx("(\\$[A-Z0-9]{40})");
    rx.search(fp_identity);
    QString server = getNickNameFromFP(rx.cap(0));

    if (!server.isEmpty()){
        emit guardStatusUpdate(server, status);
    }
}

void TorClient::parseCircuit(const QString &info)
{

    if (info.contains("FAILED"))
        emit displayError("Circuit Failed - "+info.section(" ",3,3).replace("REASON=",""), "Circuit: " + info.section(" ",2,2));

    QString circuitID = info.section(" ",0,0).stripWhiteSpace();
    QString status = info.section(" ",1,1).stripWhiteSpace();
    QString path = info.section(" ",2,2).stripWhiteSpace();

    //Get the FP Digest (if any) of the last server in the circuit
    QString exit;
    QRegExp rx("(\\$[A-Z0-9]{40})");
    int count = 0; 
    int pos = 0;
    while ( (pos = rx.search(path, pos)) != -1 ) {
        count++;
        pos += rx.matchedLength();
        exit = rx.cap(0);
    }
    if (!exit.isEmpty())
        exit = getFPDigestFromFP(exit);
    //Strip out the FPs from the circuit, if any
    path.replace(QRegExp("(\\$[A-Z0-9]{40})(~|=)"),"");

    emit circuitStatusUpdate(circuitID, status, path, exit);

}

void TorClient::parseORConn(const QString &info)
{
    QString serverID = info.section(" ",0,0);
    QString status = info.section(" ",1,1);

    if (serverID.startsWith("$")){
        QString server = getNickNameFromFP(serverID);
        if (!server.isEmpty())
            serverID = server;
    }

    if (!status.contains("NEW")){
        serverID.replace(QRegExp("^[A-Z0-9$=~]{42}"),"");
        emit ORStatusUpdate(serverID, status);
    }
}

void TorClient::parseInfo(const QString &type,const QString &info)
{

    QString message = info;
    message.replace(QRegExp("^[a-zA-Z0-9_]+\\(\\):"),"");
//     QString summary = info.section(":",0,0);
//     QString data = info.section(":",1);

    if (info.contains("Servers unavailable"))
        emit displayServer("Server Info", "<b>Server appears to be down!</b>");
    emit infoUpdate(type, message, QString());

}

void TorClient::updateCandidateServers(const QString &path)
{

   	QStringList servers = QStringList::split(",", path);
    QStringList existingServers = TorkConfig::serversHistory();
	for ( QStringList::Iterator it = servers.begin(); it != servers.end(); ++it )
	{
		if ((*it).isEmpty())
			continue;
        if (existingServers.find(*it) == existingServers.end())
            existingServers.append(*it);
	}
    TorkConfig::setServersHistory(existingServers);

    TorkConfig::writeConfig();
}

void TorClient::attemptAttach(const QString &circid, const QString &streamid)
{

    QStringList streams = QStringList::split( " ", streamid);
    for ( QStringList::Iterator it = streams.begin(); it != streams.end(); ++it )
	{
		if ((*it).isEmpty())
			continue;
        sendToServer(QString("ATTACHSTREAM %1 %2").arg(*it).arg(circid));
    }

}

void TorClient::attemptExtendCircuit(const QString &circid, const QString &serverlist, bool usefp)
{


    QStringList servers = QStringList::split( " ", serverlist);
    QStringList circuitlist;
    for ( QStringList::Iterator it = servers.begin(); it != servers.end(); ++it )
	{
		if ((*it).isEmpty())
			continue;
        if (usefp)
            circuitlist.append(getFPFromFPDigest((*it)));
        else
            circuitlist.append((*it));
    }

    QString circuit = circuitlist.join(",");
    sendToServer(QString("EXTENDCIRCUIT %1 %2").arg(circid).arg(circuit));
}

void TorClient::attemptCreateCircuit(const QString &serverlist, bool usefp)
{

    QStringList servers = QStringList::split( " ", serverlist);
    QStringList circuitlist;
    for ( QStringList::Iterator it = servers.begin(); it != servers.end(); ++it )
	{
		if ((*it).isEmpty())
			continue;
        if (usefp)
            circuitlist.append(getFPFromFPDigest((*it)));
        else
            circuitlist.append((*it));
    }

    QString circuit = circuitlist.join(",");
    sendToServer(QString("EXTENDCIRCUIT 0 %1").arg(circuit));
}

void TorClient::attemptCloseStream(const QString &streamid)
{
    sendToServer(QString("CLOSESTREAM %1 1").arg(streamid));
}

void TorClient::attemptAttachStreams( bool attachStreams)
{
    sendToServer(QString("SETCONF __LeaveStreamsUnattached=%1").arg(attachStreams));
}

void TorClient::attemptCloseCircuit(const QString &circuitid)
{
    sendToServer(QString("CLOSECIRCUIT %1").arg(circuitid));
}


void TorClient::updatePrevConfig(PrevConfig::PrevConfigList prevlist)
{

    m_previtems = prevlist;
}


void TorClient::applySettingsToRunningTor()
{

    //FIXME: use function pointers and a list to do this


    switch (TorkConfig::quickConfigure()) {
            case 0 : //Tor client and server with default settings
                return;
            case 1 : //Tor client with default settings
                return;
            case 2 : //Tor server with default settings
                return;
            case 3 : //Tor server with default settings
                return;
            case 4 : //Tor server with default settings
                return;
            default:
                break;
    }

//     kdDebug() << "1" << endl;
    KConfigSkeletonItem::List items = TorkConfig::self()->items();
    KConfigSkeletonItem::List::ConstIterator it;
    
    for( it = items.begin(); it != items.end(); ++it ) {
        if (elementShouldBeUsed((*it))){
            if (noSpecialProcessing((*it))){
                PrevConfig::PrevConfigList::iterator mit;
                QVariant oldvalue;
                for( mit = m_previtems.begin(); mit != m_previtems.end(); ++mit ) {
                    if ((*mit).name() == (*it)->name()){
                        oldvalue = (*mit).property();
                        continue;
                    }
                }


                if ( (*it)->property().type() == QVariant::String ) {
                    if ((oldvalue !=(*it)->property())){
                        ( sendToServer(QString("SETCONF %1=%2").arg((*it)->name()).arg((*it)->property().toString())));
                    }
                }else if ( (*it)->property().type() == QVariant::StringList ) {
                    if ((oldvalue !=(*it)->property())){
                        ( sendToServer(QString("SETCONF %1=\"%2\"").arg((*it)->name()).arg( (*it)->property().toStringList().join(","))));
                    }
                }else if ( (*it)->property().type() == QVariant::Int ) {
                    if ((oldvalue !=(*it)->property())){
                        ( sendToServer(QString("SETCONF %1=%2").arg((*it)->name()).arg( (*it)->property().toString())));
                    }
                }else if ( (*it)->property().type() == QVariant::Bool ) {
                    if ((oldvalue !=(*it)->property())){
                        ( sendToServer(QString("SETCONF %1=%2").arg((*it)->name()).arg( (*it)->property().toInt())));
                    }
                }

            }
        }
    }

    if (TorkConfig::useProxy()){
      if ((TorkConfig::httpProxyPort() > 0) && (!TorkConfig::httpProxyHost().isEmpty()))  
          ( sendToServer(QString("SETCONF HttpProxy=%1:%2").arg(TorkConfig::httpProxyHost()).arg(TorkConfig::httpProxyPort()))) ;
      if ((TorkConfig::httpsProxyPort() > 0) && (!TorkConfig::httpsProxyHost().isEmpty()))  
          ( sendToServer(QString("SETCONF HttpsProxy=%1:%2").arg(TorkConfig::httpsProxyHost()).arg(TorkConfig::httpsProxyPort()))) ;
  
      if ((!TorkConfig::httpProxyAuthenticatorUserName().isEmpty()) && (!TorkConfig::httpProxyAuthenticatorPassword().isEmpty()))
          ( sendToServer(QString("SETCONF HttpProxyAuthenticator=%1:%2").arg(TorkConfig::httpProxyAuthenticatorUserName()).arg(TorkConfig::httpProxyAuthenticatorPassword())));
  
      if ((!TorkConfig::httpsProxyAuthenticatorUserName().isEmpty()) && (!TorkConfig::httpsProxyAuthenticatorPassword().isEmpty()))  
          ( sendToServer(QString("SETCONF HttpsProxyAuthenticator=%1:%2").arg(TorkConfig::httpsProxyAuthenticatorUserName() ).arg(TorkConfig::httpsProxyAuthenticatorPassword())));
    }else{
          ( sendToServer(QString("SETCONF HttpProxy=")));
          ( sendToServer(QString("SETCONF HttpsProxy=")));
          ( sendToServer(QString("SETCONF HttpProxyAuthenticator=")));
          ( sendToServer(QString("SETCONF HttpsProxyAuthenticator=")));
    }

    if ((!TorkConfig::sOCKSBindAddressHost().isEmpty()) && (TorkConfig::sOCKSBindAddressPort()  > -1))
        ( sendToServer(QString("SETCONF SOCKSListenAddress=%1:%2").arg(TorkConfig::sOCKSBindAddressHost()).arg( TorkConfig::sOCKSBindAddressPort()))) ;

    if ((TorkConfig::sOCKSBindAddressHost().isEmpty()) && (TorkConfig::sOCKSBindAddressPort()  > -1))
        ( sendToServer(QString("SETCONF SOCKSPort=%2").arg(TorkConfig::sOCKSBindAddressPort()))) ;

    emit copyOldConfig();
    emit makeTorkStoppable();
    sendToServer(QString("GETCONF ExcludeNodes")) ;
    sendToServer(QString("GETCONF ExitNodes")) ;
    sendToServer(QString("GETCONF EntryNodes")) ;

}

bool TorClient::elementShouldBeUsed(const KConfigSkeletonItem* it)
{

        //Maxmin Settings are always applied, defaults if 'let Tor decide' selected.
    if (((*it).group() == "MaxMin") ||
       ((!(TorkConfig::clientOnly())) && ((*it).group() == "DefaultServerAddress")) ||
       ((*it).group() == "FirewallEvasion") ||
       ((*it).group() == "Censorship") ||
       (((*it).group() == "RunningSpecial")) ||
       (((*it).group() == "Servers")) ||
       // Server settings are applied by calling configureServer() later
       //(((*it).group() == "MyServer")) ||
       (((*it).group() == "Usability")) ||
       (((*it).group() == "UsingTor")) ||
       (((*it).group() == "MyHiddenServices")) ||
       //Serverperformance Settings are always applied, defaults if 'let Tor decide' selected.
       (((*it).group() == "ServerPerformance")))
            return true;


    //Excluded:
    //((*it).group() == "RunningNormal")) - Because they aren't appropriate for a running Tor

    return false;
}

bool TorClient::noSpecialProcessing(const KConfigSkeletonItem* it)
{

    if (((*it).name() == "PublishServerDescriptor") && (TorkConfig::bridgeRelay())){
        sendToServer(QString("setconf PublishServerDescriptor=bridge"));
        return false;
    }

    if ((*it).name() == "HashedControlPassword"){
        if ((*it).property().toString().isEmpty() &&
            (!TorkConfig::cookieAuthentication()) &&
            TorkConfig::generateRandomPassword()){
            crypto_seed_rng();
            sendToServer(QString("setconf %1=16:%2")
                         .arg((*it).name())
                         .arg(hashPassword(crypto_rand_string(16))));
            return false; 
        }
        return true;
    }

    if ((*it).group() == "DefaultServerAddress"){
        if ((*it).name() == "ORPort")
            ( sendToServer(QString("SETCONF %1=%2").arg((*it).name())
                           .arg( (*it).property().toString())));
        return false; 
    }

    if (((*it).name() == "BandwidthBurst") || ((*it).name() == "BandwidthRate")){
        ( sendToServer(QString("SETCONF BandwidthBurst=%1KB BandwidthRate=%2KB")
                       .arg(TorkConfig::bandwidthBurst()).arg(TorkConfig::bandwidthRate())));
        getBandwidth();
        return false;
    }

    if ((*it).name() == "MaxAdvertisedBandwidth"){
        ( sendToServer(QString("SETCONF %1=%2KB").arg((*it).name()).arg( (*it).property().toString())));
        getBandwidth();
        return false;
    }

    if ((*it).name() == "AccountingMax"){
        ( sendToServer(QString("SETCONF %1=\"%2 bytes\"").arg((*it).name()).arg( ((*it).property().toInt() * 1024 * 1024))));
        return false;
    }

    if ((*it).name() == "AccountingStart"){
       if ((*it).property().toString() == "day")
            ( sendToServer(QString("SETCONF %1=\"%2 00:00\"").arg((*it).name()).arg( (*it).property().toString())));
       else
            ( sendToServer(QString("SETCONF %1=\"%2 1 00:00\"").arg((*it).name()).arg( (*it).property().toString())));
       return false;
    }


    if ((*it).name() == "KeepalivePeriod"){
        if (!TorkConfig::reachableAddresses().isEmpty()){
            ( sendToServer(QString("SETCONF %1=%2").arg((*it).name()).arg( ((*it).property().toInt() * 60)))) ;
        }
        return false;
    }

    if ((*it).name() == "TrackHostExits"){
        if (!TorkConfig::trackHostExits().isEmpty()){
            ( sendToServer(QString("SETCONF %1=%2").arg((*it).name()).arg( ((*it).property().toStringList().join(","))))) ;
            if (TorkConfig::trackHostExitsExpire() > 0)  
                ( sendToServer(QString("SETCONF TrackHostExitsExpire=%2").arg((TorkConfig::trackHostExitsExpire() * 60)))) ;
        }
        return false;
    }


    if ((*it).name() == "SOCKSBindAddressMany"){

        if (!TorkConfig::sOCKSBindAddressMany().isEmpty()){
            QStringList socksbind = TorkConfig::sOCKSBindAddressMany();
            for ( QStringList::Iterator it = (socksbind).begin(); it != (socksbind).end(); it++ )
            {
                if ((*it).isEmpty())
                    continue;
                ( sendToServer(QString("SETCONF SOCKSListenAddress=%2").arg((*it)))) ;
            }
        }
        return false;
    }

    if ((*it).name() == "ExitPolicy"){
        if (TorkConfig::middleMan())
            ( sendToServer(QString("SETCONF ExitPolicy=%1").arg(("\"reject *:*\"")))) ;
        else
            ( sendToServer(QString("SETCONF %1=\"%2\"").arg((*it).name()).arg( (*it).property().toStringList().join(","))));
        return false;
    }


    if ((*it).name() == "HiddenServices"){
        QStringList hiddenServices = TorkConfig::hiddenServices();
        QString allservices;
        for ( QStringList::Iterator it = (hiddenServices).begin(); it != (hiddenServices).end(); it++ )
        {
            if ((*it).isEmpty())
                continue;
            allservices += (QString("HiddenServiceDir=\"%1\" HiddenServicePort=\"%2 %3\" ").arg((*it).section("\n",-1)).arg((*it).section("\n",-4,-4)).arg((*it).section("\n",-3,-3))) ;
        }
        if (!allservices.isEmpty())
            ( sendToServer(QString("SETCONF %1").arg(allservices))) ;
        else
            ( sendToServer(QString("SETCONF HiddenServiceDir= HiddenServicePort="))) ;

        return false;
    }

    if ((TorkConfig::useBridges()) && ((*it).name() == "Bridge")){

        QStringList bridges = TorkConfig::bridge();
        QString allbridges;
        for ( QStringList::Iterator it = (bridges).begin(); it != (bridges).end(); it++ )
        {
            if ((*it).isEmpty())
                continue;
            allbridges += (QString("Bridge=\"%1\" ")
                           .arg((*it))) ;
        }
        if (!allbridges.isEmpty())
            ( sendToServer(QString("SETCONF %1").arg(allbridges))) ;
        else
            ( sendToServer(QString("SETCONF Bridge="))) ;

        return false;
    }

    if ((*it).name() == "MyFamily"){

        QStringList family = TorkConfig::myFamily();
        QStringList allfamily;
        for ( QStringList::Iterator it = (family).begin(); it != (family).end(); it++ )
        {
            if ((*it).isEmpty())
                continue;
            QString node = "$"+getFPFromFPDigest((*it).section("-",1,1));
            allfamily.append(node) ;
        }
        if (!allfamily.isEmpty())
            ( sendToServer(QString("SETCONF MyFamily=%1").arg(allfamily.join(",")))) ;
        else
            ( sendToServer(QString("SETCONF MyFamily="))) ;

        return false;
    }

    if ((*it).name() == "MapAddress"){

        QStringList maps = TorkConfig::mapAddress();
        for ( QStringList::Iterator it = (maps).begin();
              it != (maps).end(); it++ )
        {
            if ((*it).isEmpty())
                continue;
            ( sendToServer(QString("SETCONF MapAddress=%2").arg((*it)))) ;
        }
        return false;
    }
    
    if ((!TorkConfig::fascistFirewall()) && ((*it).name() == "ReachableAddresses")){
        ( sendToServer(QString("SETCONF ReachableAddresses="))) ;
        return false;
    }
    return true;
}


void TorClient::cleanUp()
{

    if (TorkConfig::hashedControlPassword().isEmpty() &&
        (!TorkConfig::cookieAuthentication()) &&
        TorkConfig::generateRandomPassword()){
          sendToServer(QString("setconf HashedControlPassword="));
          socket->flush();
        }
}

TorClient::~TorClient()
{

}

void TorClient::parseDirStatus(const QString &info)
{


    if (info.left(2) == "r "){
        ds_identity = info.section(" ",2,2);
        ds_ip = info.section(" ",6,6);
        ds_server = info.section(" ",1,1);
        ds_date = info.section(" ",4,4);
        return;
    }

    if (info.left(2) == "s "){

        if (QDate::currentDate().daysTo(QDate(ds_date.left(4).toInt(),
            ds_date.mid(5,2).toInt(),ds_date.right(2).toInt())) > -30) {
            if (server(ds_identity).isEmpty())
                storeServer(ds_server,ds_identity);
    
            ds_statuses = info;
            emit updateServerStatus(ds_ip, ds_identity, ds_server, ds_statuses);
        }
    }

}

bool TorClient::isControllerWorking()
{
    return m_controllerWorking;
 
}

void TorClient::setBandwidth(const QString &rate, const QString &burst, const QString &max)
{
   ( sendToServer(QString("SETCONF BandwidthRate=%2KB BandwidthBurst=%2KB")
                  .arg(rate).arg(burst)));
   if (!max.isEmpty())
    ( sendToServer(QString("SETCONF MaxAdvertisedBandwidth=%2KB").arg(max)));
   getBandwidth();
}

void TorClient::getBandwidth()
{
   ( sendToServer(QString("GETCONF BandwidthRate")));
   ( sendToServer(QString("GETCONF BandwidthBurst")));
   ( sendToServer(QString("GETCONF MaxAdvertisedBandwidth")));
}

void TorClient::handleTorVersion( const QString &caption)
{
    m_currentTorVersion = caption;
    if (m_currentTorVersion.left(3) == "0.2")
        sendToServer("SETCONF DownloadExtraInfo=1");
     emit setTorCaption(caption);
}

void TorClient::closeAllCircuits( QListView* &circuitList)
{
        QListViewItemIterator cit(circuitList);
        while ( cit.current() ) {
            attemptCloseCircuit(cit.current()->text(0));
            ++cit;
        }
}

void TorClient::assignPortToRemove()
{
    //called by the warning message to get the port to remove in case
    // user requests it.
    if (m_WarnedPorts[0].isNull())
      return;
    kdDebug() << "warnedports " << m_WarnedPorts[0] << endl;
    m_portToRemove = m_WarnedPorts[0];
    kdDebug() << "porttoremove" << m_portToRemove << endl;
    m_WarnedPorts.pop_front();
    kdDebug() << "porttoremove" << m_portToRemove << endl;

}

void TorClient::allowPlainTextPorts()
{

    kdDebug() << "m_portsToReject " << m_portsToReject << endl;

    m_portsToReject.remove(m_portsToReject.find(m_portToRemove));
    sendToServer(QString("SETCONF WarnPlainTextPorts=%1")
                 .arg(m_portsToReject.join(",")));
    sendToServer(QString("SETCONF RejectPlainTextPorts=%1")
                .arg(m_portsToReject.join(",")));
}

void TorClient::resolveAddress(const QString &address)
{
    QRegExp rx("[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}");
    rx.search(address);
    QString tmp = rx.cap(0);
    if (tmp.isEmpty()) {
      sendToServer("RESOLVE " + address);
      logResolveList << address;
    }else{
      sendToServer("RESOLVE mode=reverse " + tmp);
      logResolveList << QString("REVERSE[%1]").arg(tmp);
    }

}

#include "torclient.moc"

