    // @(#)root/proof:$Name:  $:$Id: TSlave.cxx,v 1.30 2004/06/25 17:27:09 rdm Exp $
    // Author: Fons Rademakers   14/02/97

    /*************************************************************************
     * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers.               *
     * All rights reserved.                                                  *
     *                                                                       *
     * For the licensing terms see $ROOTSYS/LICENSE.                         *
     * For the list of contributors see $ROOTSYS/README/CREDITS.             *
     *************************************************************************/

    //////////////////////////////////////////////////////////////////////////
    //                                                                      //
    // TSlave                                                               //
    //                                                                      //
    // This class describes a PROOF slave server.                           //
    // It contains information like the slaves host name, ordinal number,   //
    // performance index, socket, etc. Objects of this class can only be    //
    // created via TProof member functions.                                 //
    //                                                                      //
    //////////////////////////////////////////////////////////////////////////

    #include "TSlave.h"
    #include "TProof.h"
    #include "TSystem.h"
    #include "TEnv.h"
    #include "TROOT.h"
    #include "TUrl.h"
    #include "TMessage.h"

    ClassImp(TSlave)

    //______________________________________________________________________________
     TSlave::TSlave(const char *host, Int_t port, Int_t ord, Int_t perf,
                   const char *image, TProof *proof)
    {
       // Create a PROOF slave object. Called via the TProof ctor.

       fName     = host;
       fPort     = port;
       fImage    = image;
       fWorkDir  = kPROOF_WorkDir;
       fOrdinal  = ord;
       fPerfIdx  = perf;
       fSecContext = 0;
       fProof    = proof;
       fSocket   = 0;
       fInput    = 0;

       // The url contains information about the server type: make sure
       // it is 'proofd' or alike
       TString hurl(proof->GetUrlProtocol());
       hurl.Insert(5, 'd');
       // Add host, port (and user) information
       if (proof->GetUser() && strlen(proof->GetUser())) {
          hurl += TString(Form("://%s@%s:%d", proof->GetUser(), host, port));
       } else {
          hurl += TString(Form("://%s:%d", host, port));
       }

       // Add information about our status (Client or Master)
       TString iam;
       if (proof->IsMaster()) {
          iam = "Master";
          hurl += TString("/?M");
       } else {
          iam = "Local Client";
          hurl += TString("/?C");
       }

       // Open authenticated connection to remote PROOF slave server.
       Int_t wsize = 65536;
       fSocket = TSocket::CreateAuthSocket(hurl, 0, wsize);
       if (fSocket && fSocket->IsAuthenticated()) {

          // Remove socket from global TROOT socket list. Only the TProof object,
          // representing all slave sockets, will be added to this list. This will
          // ensure the correct termination of all proof servers in case the
          // root session terminates.
          gROOT->GetListOfSockets()->Remove(fSocket);

          // Fill some useful info
          fSecContext        = fSocket->GetSecContext();
          fUser              = fSecContext->GetUser();
          proof->fSecContext = fSecContext;
          proof->fUser       = fUser;
          Int_t ProofdProto  = fSocket->GetRemoteProtocol();

          PDB(kGlobal,3) {
             fSocket->GetSecContext()->Print("e");
             Info("TSlave",
                  "%s: fUser is .... %s", iam.Data(), proof->fUser.Data());
          }

          TString Details = fSocket->GetSecContext()->GetDetails();
          Int_t RemoteOffSet = fSocket->GetSecContext()->GetOffSet();

          char buf[512];
          fSocket->Recv(buf, sizeof(buf));

          if (strcmp(buf, "Okay")) {
             Printf("%s", buf);
             SafeDelete(fSocket);
          } else {
             // get back startup message of proofserv (we are now talking with
             // the real proofserver and not anymore with the proofd front-end)

             Int_t what;
             fSocket->Recv(buf, sizeof(buf), what);

             if (what == kMESS_NOTOK) {
                SafeDelete(fSocket);
                return;
             }

             // exchange protocol level between client and master and between
             // master and slave
             fSocket->Send(kPROOF_Protocol, kROOTD_PROTOCOL);
             fSocket->Recv(fProtocol, what);
             fProof->fProtocol = fProtocol;   // on master this is the protocol
                                              // of the last slave
             // send user name to remote host
             // for UsrPwd and SRP methods send also passwd, rsa encoded
             TMessage pubkey;
             TString passwd = "";
             Bool_t  pwhash = kFALSE;
             Bool_t  srppwd = kFALSE;
             Bool_t  sndsrp = kFALSE;

             TPwdCtx *pwdctx = 0;
             if (RemoteOffSet > -1 &&
                (fSecContext->IsA("UsrPwd") || fSecContext->IsA("SRP")))
                pwdctx = (TPwdCtx *)(fSecContext->GetContext());

             if (!fProof->IsMaster()) {
                if (gEnv->GetValue("Proofd.SendSRPPwd",0))
                   if (RemoteOffSet > -1)
                      sndsrp = kTRUE;
             } else {
                if (fSecContext->IsA("SRP") && pwdctx) {
                   if (pwdctx->GetPasswd() != "" && RemoteOffSet > -1)
                      sndsrp = kTRUE;
                }
             }

             if ((fSecContext->IsA("UsrPwd") && pwdctx) ||
                 (fSecContext->IsA("SRP")    && sndsrp)) {

                // Send offset to identify remotely the public part of RSA key
                fSocket->Send(RemoteOffSet,kROOTD_RSAKEY);

                if (pwdctx) {
                   passwd = pwdctx->GetPasswd();
                   pwhash = pwdctx->IsPwHash();
                }
                srppwd = fSecContext->IsA("SRP");

                if (fSocket->SecureSend(passwd,1,fSecContext->GetRSAKey()) == -1) {
                   if (RemoteOffSet > -1)
                      Warning("TSlave","problems secure-sending pass hash %s",
                              "- may result in failures");
                   // If non RSA encoding available try passwd inversion
                   if (fSecContext->IsA("UsrPwd")) {
                      for (int i = 0; i < passwd.Length(); i++) {
                         char inv = ~passwd(i);
                         passwd.Replace(i, 1, inv);
                      }
                      TMessage mess;
                      mess << passwd;
                      fSocket->Send(mess);
                   }
                }

             } else {

                // Send notification of no offset to be sent ...
                fSocket->Send(-2,kROOTD_RSAKEY);

             }

             TMessage mess;
             if (!fProof->IsMaster())
                mess << fUser << pwhash << srppwd << fProof->fConfFile;
             else
                mess << fUser << pwhash << srppwd << fOrdinal;

             fSocket->Send(mess);

             if (ProofdProto > 6) {
                // Now we send authentication details to access, eg, data servers
                // not in the proof cluster and to be propagated to slaves.
                // This is triggered by the 'proofserv <dserv1> <dserv2> ...'
                // card in .rootauthrc
                fSocket->SendHostAuth();
             }

             // set some socket options
             fSocket->SetOption(kNoDelay, 1);
          }
       } else
          SafeDelete(fSocket);
    }

    //______________________________________________________________________________
     TSlave::~TSlave()
    {
       // Destroy slave.

       Close();
    }

    //______________________________________________________________________________
     void TSlave::Close(Option_t *)
    {
       // Close slave socket.

       SafeDelete(fInput);
       SafeDelete(fSocket);
    }

    //______________________________________________________________________________
     Int_t TSlave::Compare(const TObject *obj) const
    {
       // Used to sort slaves by performance index.

       TSlave *sl = (TSlave *) obj;

       if (fPerfIdx > sl->GetPerfIdx()) return 1;
       if (fPerfIdx < sl->GetPerfIdx()) return -1;
       return 0;
    }

    //______________________________________________________________________________
     void TSlave::Print(Option_t *) const
    {
       // Printf info about slave.

       Printf("*** Slave %d  (%s)", fOrdinal, fSocket ? "valid" : "invalid");
       Printf("    Host name:               %s", GetName());
       Printf("    Port number:             %d", GetPort());
       Printf("    User:                    %s", GetUser());
       Printf("    Security context:        %s", fSecContext->AsString());
       Printf("    Proofd protocol version: %d", fSocket->GetRemoteProtocol());
       Printf("    Image name:              %s", GetImage());
       Printf("    Working directory:       %s", GetWorkDir());
       Printf("    Performance index:       %d", GetPerfIdx());
       Printf("    MB's processed:          %.2f", float(GetBytesRead())/(1024*1024));
       Printf("    MB's sent:               %.2f", fSocket ? float(fSocket->GetBytesRecv())/(1024*1024) : 0.0);
       Printf("    MB's received:           %.2f", fSocket ? float(fSocket->GetBytesSent())/(1024*1024) : 0.0);
       Printf("    Real time used (s):      %.3f", GetRealTime());
       Printf("    CPU time used (s):       %.3f", GetCpuTime());
    }

    //______________________________________________________________________________
     void TSlave::SetInputHandler(TFileHandler *ih)
    {
       // Adopt and register input handler for this slave. Handler will be deleted
       // by the slave.

       fInput = ih;
       fInput->Add();
    }