    // @(#)root/netx:$Name:  $:$Id: TXNetFile.cxx,v 1.9 2005/05/12 12:25:26 rdm Exp $
    // Author: Alvise Dorigo, Fabrizio Furano

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

    //////////////////////////////////////////////////////////////////////////
    //                                                                      //
    // TXNetFile                                                            //
    //                                                                      //
    // TXNetFile is an extension of TNetFile able to deal with new xrootd   //
    // server. Its new features are:                                        //
    //  - Automatic server kind recognition (xrootd load balancer, xrootd   //
    //    data server, old rootd)                                           //
    //  - Backward compatibility with old rootd server (acts as an old      //
    //    TNetFile)                                                         //
    //  - Fault tolerance for read/write operations (read/write timeouts    //
    //    and retry)                                                        //
    //  - Internal connection timeout (tunable indipendently from the OS    //
    //    one) handled by threads                                           //
    //  - handling of redirections from server                              //
    //  - Single TCP physical channel for multiple TXNetFile's instances    //
    //    inside the same application                                       //
    //    So, each TXNetFile object client must send messages containing    //
    //    its ID (streamid). The server, of course, will respond with       //
    //    messages containing the client's ID, in order to make the client  //
    //    able to recognize its message by matching its streamid with that  //
    //    one contained in the server's response.                           //
    //  - Tunable log verbosity level (0 = nothing, 3 = dump read/write     //
    //    buffers too!)                                                     //
    //  - Many parameters configurable via TEnv facility (see SetParm()     //
    //    methods)                                                          //
    //                                                                      //
    //////////////////////////////////////////////////////////////////////////

    #include "TError.h"
    #include "TEnv.h"
    #include "TXNetFile.h"
    #include "TXDebug.h"
    #include "TXError.h"
    #include "TXUrl.h"
    #include "TXNetConn.h"

    ClassImp(TXNetFile);

    void (*evtFunc)();

    Bool_t TXNetFile::fgTagAlreadyPrinted = kFALSE;

    Short_t gXDebugLevel = 0;

    //_____________________________________________________________________________
     TXNetFile::TXNetFile(const char *url, Option_t *option, const char* ftitle,
    		     Int_t compress, Int_t netopt) :
                  TNetFile(url, ftitle, compress, kFALSE)
    {
      // Create a TXNetFile object. A TXNetFile object is the same as a TNetFile
      // (from which the former derives) except that the protocol is extended to
      // support dealing with new xrootd data server or xrootd load balancer
      // server.
      //
      // The "url" argument must be of the form
      //
      //   root://server1:port1[,server2:port2,...,serverN:portN]/pathfile,
      //
      // Note that this means that multiple servers (>= 1) can be specified in
      // the url. The connection will try to connect to the first server:port
      // and if that does not succeed, it will try the second one, and so on
      // until it finds a server that will respond.
      //
      // See the TNetFile documentation for the description of the other arguments.
      //
      // The creation consists of internal variable settings (most important is
      // the client's domain), creation of a TXUrl array containing all specified
      // urls (a single url is serverX:portX/pathfile), trying to connect to the
      // servers calling Connect() method, getting a valid access to the remote
      // server the client is connected to using GetAccessToSrv() method,
      // recognizing the remote server (if an old rootd the TNetFile's Create
      // method will be called)

      // Set debug level
      gXDebugLevel = gEnv->GetValue("XNet.Debug", 0);

      CreateTXNf(url, option, ftitle, compress, netopt);
    }

    //_____________________________________________________________________________
     TXNetFile::~TXNetFile()
    {
       // Destructor
       if (IsOpen()) Close(0);

       SafeDelete(fConnModule);
    }

    //_____________________________________________________________________________
     void TXNetFile::CreateTXNf(const char *url, Option_t *option, const char* ftitle,
    		       Int_t compress, Int_t netopt)
    {
      short locallogid;
      Bool_t validDomain = kFALSE;
      fOpenWithRefresh = kFALSE;
      fAlreadyStated = kFALSE;
      fCreateMode = kFALSE;
      fAlreadyDetected = kFALSE;
      fIsROOT = kFALSE;
      fSize = 0;
      if (gEnv->GetValue("XNet.PrintTAG",0) == 1)
         if (!fgTagAlreadyPrinted) {
            Info("CreateTXNf","(C) 2004 SLAC TXNetFile (eXtended TNetFile) %s",
                 gROOT->GetVersion());
            fgTagAlreadyPrinted = kTRUE;
         }

      // Setup modified Error Handler which prints time stamps. Note that this
      // check will be done once per TXNetFile creation. It probably should be
      // moved someplace where it gets done only once per job. (And probably
      // could even be a standard option of the default ErrorHandler.)
      if (gEnv->GetValue("XNet.DebugTimestamp",0) == 1) {
         SetErrorHandler(TXNErrorHandler);
      }

      // Using ROOT mechanism to IGNORE SIGPIPE signal
      gSystem->IgnoreSignal(kSigPipe);

      fOpenPars.FileOpened = kFALSE;
      // But we initialize the internal params...
      fOpenPars.option = "";
      if (option)
        fOpenPars.option = option;

      fOpenPars.fTitle = "";
      if (ftitle)
        fOpenPars.fTitle = ftitle;

      fOpenPars.compress = compress;
      fOpenPars.netopt = netopt;

      // Now we try to set up the first connection
      // We cycle through the list of urls given as a single TUrl parameter

      fConnModule = new TXNetConn();
      if (!fConnModule) {
         Error("CreateTXNf","Fatal ERROR *** Object creation with new failed !"
                      " Probable system resources exhausted.");
         gSystem->Abort();
      }
      fConnModule->SetRedirHandler(this);

      // Max number of tries
      Int_t connectMaxTry = gEnv->GetValue("XNet.TryConnectServersList",
                                           DFLT_TRYCONNECTSERVERSLIST);
      // List of regular expressions to match
      TString allowRE = gEnv->GetValue("XNet.ConnectDomainAllowRE",
                                       fConnModule->GetClientHostDomain().Data());
      TString denyRE  = gEnv->GetValue("XNet.ConnectDomainDenyRE",
                                       "<unknown>");

      TXUrl urlArray(url);
      if (!urlArray.IsValid()) {
         Error("CreateTXNf", "The URL(s) provided are incorrect."
                         " Going into zombie state.");
         goto zombie;
      }

      for (Short_t jj=0; jj <=urlArray.Size()-1; jj++) {
         TUrl *thisUrl;
         thisUrl = urlArray.GetNextUrl();
         fUrl = *thisUrl;
         if (fConnModule->CheckHostDomain(fUrl.GetHost(), allowRE, denyRE)) {
    	validDomain = kTRUE;
    	break;
         }
      }

      if (!validDomain) {
         Error("CreateTXNf", "All the specified servers are disallowed. "
                         "Going into zombie state.");
         goto zombie;
      }

      urlArray.Rewind();
      locallogid = -1;
      for (Int_t connectTry = 0;
          (connectTry < connectMaxTry) && (!fConnModule->IsConnected());
           connectTry++) {

         TUrl *thisUrl;

         // Get an url from the available set
         thisUrl = urlArray.GetARandomUrl();

         if (thisUrl) {
            fUrl = *thisUrl;
            if (fConnModule->CheckHostDomain(fUrl.GetHost(), allowRE, denyRE)) {
               if (DebugLevel() >= kHIDEBUG)
                  Info("CreateTXNf", "Trying to connect to %s:%d. Connect try %d.",
                               fUrl.GetHost(), fUrl.GetPort(), connectTry+1);
               locallogid = fConnModule->Connect(fUrl.GetHost(),
                                                 fUrl.GetPort(), netopt);
            }
         }

         // We are connected to a host. Let's handshake with it.
         if (fConnModule->IsConnected()) {

            // Now the have the logical Connection ID, that we can use as streamid for
            // communications with the server
            if (DebugLevel() >= kHIDEBUG)
               Info("CreateTXNf", "The logical connection id is %d. This will be the"
                            " streamid for this client",fConnModule->GetLogConnID());
            fConnModule->SetUrl(fUrl);

            if (DebugLevel() >= kHIDEBUG)
    	  Info("CreateTXNf", "Working url is [%s]", fUrl.GetUrl());

            // after connection deal with server
            if (!fConnModule->GetAccessToSrv()) {
               if (fConnModule->fOpenError == kXR_NotAuthorized) {
                  // Authentication error: does not make much sense to retry
                  fConnModule->Disconnect(kTRUE);
                  TString msg = fConnModule->fOpenErrorMsg;
                  msg.Remove(msg.Last(':'));
                  Error("CreateTXNf","Authentication failure: %s",msg.Data());
                  goto zombie;
               } else {
                  Error("CreateTXNf",
                        "Access to server failed (%d)",fConnModule->fOpenError);
               }
            } else {
               if (DebugLevel() >= kUSERDEBUG)
    	      Info("CreateTXNf", "Access to server granted.");
               break;
    	}
         }

         // We force a physical disconnection in this special case
         if (DebugLevel() >= kHIDEBUG)
            Info("CreateTXNf", "Disconnecting.");

         fConnModule->Disconnect(kTRUE);

         if (DebugLevel() >= kUSERDEBUG)
            Info("CreateTXNf", "Connection attempt cycle failed. Sleeping %d seconds.",
                         gEnv->GetValue("XNet.ReconnectTimeout",
                                        DFLT_RECONNECTTIMEOUT));

         gSystem->Sleep(1000 * gEnv->GetValue("XNet.ReconnectTimeout",
                                             DFLT_RECONNECTTIMEOUT) );

      } //for connect try

      if (!fConnModule->IsConnected()) {
         Error("CreateTXNf", "Some severe error occurred while opening a connection"
                         " with the servers [%s]. Program exits",
                         urlArray.GetServers().Data());
         goto zombie;
      }

      if (locallogid != fConnModule->GetLogConnID()) {
         Error("CreateTXNf", "Internal error. The logids do not match (%d, %d)",
                         locallogid, fConnModule->GetLogConnID());
         //    abort();
         goto zombie;
      }

      //
      // Variable initialization
      // If the server is a new xrootd ( load balancer or data server)
      //
      if ((fConnModule->GetServerType() != TXNetConn::kSTRootd) &&
          (fConnModule->GetServerType() != TXNetConn::kSTNone)) {
         // Now we are connected to a server that didn't redirect us after the
         // login/auth phase
         // let's continue with the openfile sequence
         // We try to recycle the TNetFile procedure as much as possible, traslating
         // the TNetFile's open modes into the kXR_xxx modes...
         if (DebugLevel() >= kUSERDEBUG)
    	Info("CreateTXNf", "Opening the remote file %s", fUrl.GetFile());

         // url is not needed because already stored in fUrl
         // Here we have to init our TFile ancestor
         if (!Open(option, fTitle, compress, netopt, kTRUE)) {
    	Error("CreateTXNf", "Error opening the file %s on host %s:%d",
                            fUrl.GetFile(), fUrl.GetHost(), fUrl.GetPort());
              fConnModule->Disconnect(kTRUE);
    	  goto zombie;
         } else {
    	if (DebugLevel() >= kUSERDEBUG)
    	   Info("CreateTXNf", "File opened succesfully.");
         }
      } else {
         // the server is an old rootd
         if (fConnModule->GetServerType() == TXNetConn::kSTRootd) {
            TSocket *rootdSocket = fConnModule->GetRootdSocket();
            if (rootdSocket && rootdSocket->GetRemoteProtocol() > 13) {
               // Remote support for reuse of open connection
               TNetFile::Create(rootdSocket, option, netopt);
            } else {
               // Open connection has been closed because could
               // not be reused; TNetFile will open a new connection
               TNetFile::Create(fUrl.GetUrl(), option, netopt);
            }
         }
         if (fConnModule->GetServerType() == TXNetConn::kSTNone) {
            goto zombie;
         }
      }

      return;

    zombie:
       // error in file opening occured, make this object a zombie
       MakeZombie();
       gDirectory = gROOT;
    }

    //_____________________________________________________________________________
     Bool_t TXNetFile::ReadBuffer(char *buffer, Int_t BufferLength)
    {
       // Override TNetFile::ReadBuffer to deal with the xrootd server.
       // Returns kTRUE in case of errors.

       if (IsZombie()) {
          Error("ReadBuffer", "ReadBuffer is not possible because object"
                " is in 'zombie' state");
          return kTRUE;
       }

       if (fConnModule->GetServerType() == TXNetConn::kSTRootd) {
          if (DebugLevel() >= kHIDEBUG)
             Info("ReadBuffer","Calling TNetFile::ReadBuffer");
          return TNetFile::ReadBuffer(buffer, BufferLength);
       }

       if (!IsOpen()) {
          Error("ReadBuFfer","The remote file %s is not open", fUrl.GetFile());
          return kTRUE;
       }

       Bool_t result = kFALSE;

       if (fCache) {
          Int_t st;
          Long64_t off = fOffset;
          if ((st = fCache->ReadBuffer(fOffset, buffer, BufferLength)) < 0) {
             Error("ReadBuffer", "error reading from cache");
             return kTRUE;
          }
          if (st > 0) {
             // fOffset might have been changed via TCache::ReadBuffer(), reset it
             Seek(off + BufferLength);
             return result;
          }
       }

       // Prepare request
       ClientRequest readFileRequest;
       memset( &readFileRequest, 0, sizeof(readFileRequest) );
       fConnModule->SetSID(readFileRequest.header.streamid);
       readFileRequest.read.requestid = kXR_read;
       memcpy( readFileRequest.read.fhandle, fHandle, sizeof(fHandle) );
       readFileRequest.read.offset = fOffset;
       readFileRequest.read.rlen = BufferLength;
       readFileRequest.read.dlen = 0;

       if (DebugLevel() >= kHIDEBUG)
          Info("ReadBuffer", "Calling TXNetConn::SendGenCommand to read %d"
               " bytes of data at offset %Ld.",
               readFileRequest.read.rlen,
               readFileRequest.read.offset);

       // Original version, without caching
       result = !fConnModule->SendGenCommand(&readFileRequest, 0, 0, buffer,
                                             kFALSE, (char *)"TXNetFile::ReadBuffer");
       if (!result) {
          fOffset += BufferLength;
          fBytesRead += BufferLength;
    #ifdef WIN32
          SetFileBytesRead(GetFileBytesRead() + BufferLength);
    #else
          fgBytesRead += BufferLength;
    #endif
       }
       return result;
    }

    //_____________________________________________________________________________
     Bool_t TXNetFile::WriteBuffer(const char *buffer, Int_t BufferLength)
    {
       // Override TNetFile::WriteBuffer to deal with the xrootd server.
       // Returns kTRUE in case of errors.

       if (IsZombie()) {
          Error("WriteBuffer", "WriteBuffer is not possible because object"
                " is in 'zombie' state");
          return kTRUE;
       }

       fAlreadyStated = kFALSE;

       if (fConnModule->GetServerType() == TXNetConn::kSTRootd) {
          if (DebugLevel() >= kHIDEBUG)
             Info("WriteBuffer","Calling TNetFile::WriteBuffer");
          return TNetFile::WriteBuffer(buffer, BufferLength );
       }

       if (!IsOpen()) {
          Error("WriteBuffer","The remote file %s is not open", fUrl.GetFile());
          return kTRUE;
       }

       Bool_t result = kFALSE;

       if (fCache) {
          Int_t st;
          Long64_t off = fOffset;
          if ((st = fCache->WriteBuffer(fOffset, buffer, BufferLength)) < 0) {
             SetBit(kWriteError);
             Error("WriteBuffer", "error writing to cache");
             return kTRUE;
          }
          if (st > 0) {
             // fOffset might have been changed via TCache::WriteBuffer(), reset it
             Seek(off + BufferLength);
             return result;
          }
       }

       // Prepare request
       ClientRequest writeFileRequest;
       memset( &writeFileRequest, 0, sizeof(writeFileRequest) );
       fConnModule->SetSID(writeFileRequest.header.streamid);
       writeFileRequest.write.requestid = kXR_write;
       memcpy( writeFileRequest.write.fhandle, fHandle, sizeof(fHandle) );
       writeFileRequest.write.offset = fOffset;
       writeFileRequest.write.dlen = BufferLength;

       if (DebugLevel() >= kHIDEBUG)
          Info("WriteBuffer", "Calling TXNetConn::SendGenCommand...");

       result = !fConnModule->SendGenCommand(&writeFileRequest, buffer, 0, 0,
                                    kFALSE, (char *)"TXNetFile::WriteBuffer");
       if (!result) {
          fOffset += BufferLength;
          fBytesWrite += BufferLength;
    #ifdef WIN32
          SetFileBytesWritten(GetFileBytesWritten() + BufferLength);
    #else
          fgBytesWrite += BufferLength;
    #endif
       }
       return result;
    }

    //_____________________________________________________________________________
     Bool_t TXNetFile::IsOpen() const
    {
      // Return kTRUE if the file is open, kFALSE otherwise

      if (fConnModule->GetServerType() == TXNetConn::kSTRootd) {
         if (DebugLevel() >= kHIDEBUG)
            Info("IsOpen","Calling TNetFile::IsOpen");
         return TNetFile::IsOpen();
      }

      return fOpenPars.FileOpened;
    }

    //_____________________________________________________________________________
     Int_t TXNetFile::ReOpen(const Option_t *Mode)
    {
      // Re-open the file (see TNetFile::ReOpen() or TFile::ReOpen()
      // for more details)

      if (fConnModule->GetServerType() == TXNetConn::kSTRootd) {
         if (DebugLevel() >= kHIDEBUG)
            Info("ReOpen","Calling TNetFile::ReOpen");
         return TNetFile::ReOpen(Mode);
      }

      fAlreadyStated = kFALSE;
      fSize = 0;

      return TFile::ReOpen(Mode);
    }

    //_____________________________________________________________________________
     void TXNetFile::Close(const Option_t *opt)
    {
      // Close the file (see TNetFile::Close() or TFile::Close()
      // for more details)

      if (fConnModule->GetServerType() == TXNetConn::kSTRootd) {
         if (DebugLevel() >= kHIDEBUG)
            Info("Close","Calling TNetFile::Close");
         TNetFile::Close(opt);
         return;
      }

      TFile::Close(opt);

      fSize = 0;
      fAlreadyStated = kFALSE;
      fAlreadyDetected = kFALSE;
      fIsROOT = kFALSE;
    }

    //_____________________________________________________________________________
     void TXNetFile::Flush()
    {
       // Flushes un-written data

       if (IsZombie()) {
          Error("Flush", "Flush is not possible because object is"
                         " in 'zombie' state");
          return;
       }

       if (fConnModule->GetServerType() == TXNetConn::kSTRootd) {
          if (DebugLevel() >= kHIDEBUG)
             Info("Flush","Calling TNetFile::Flush");
          TNetFile::Flush();
          return;
       }

       if (!IsOpen()) {
          Error("Flush","The remote file %s is not open", fUrl.GetFile());
          return;
       }

       // Prepare request
       ClientRequest flushFileRequest;
       memset( &flushFileRequest, 0, sizeof(flushFileRequest) );
       fConnModule->SetSID(flushFileRequest.header.streamid);
       flushFileRequest.sync.requestid = kXR_sync;
       memcpy(flushFileRequest.sync.fhandle, fHandle, sizeof(fHandle));
       flushFileRequest.sync.dlen = 0;
       if (DebugLevel() >= kHIDEBUG)
          Info("Flush", "Calling TXNetConn::SendGenCommand...");

       Bool_t cmdres = fConnModule->SendGenCommand(&flushFileRequest, 0, 0, 0,
                                           kFALSE, (char *)"TXNetFile::Flush");

       if (!cmdres)
          Error("Flush","SendGenCommand returned false. Command failed.");
    }

    //_____________________________________________________________________________
     Bool_t TXNetFile::Open(Option_t *option, const char* ftitle, Int_t compress,
    		       Int_t netopt, Bool_t DoInit)
    {
      // High level open routine; if the remote server is an old rootd the file
      // has been already opened by the TNetFile's Create() method

      fAlreadyStated = kFALSE;
      fSize = 0;

      if (fConnModule->GetServerType() == TXNetConn::kSTRootd) {
        // Do nothing because the file is already open
        // In fact when TNetFile is first instantiated it immediately open the
        // remote file
        return kTRUE;
      }

      // First attempt to open a remote file without the kXR_refresh option ON
      Bool_t lowopenRes = LowOpen(fUrl.GetFileAndOptions(), option, ftitle,
                                  compress, netopt, DoInit);
      if (lowopenRes) {
         // Let's remember that we succesfully opened a file without refresh
         fOpenWithRefresh = kFALSE;
         return kTRUE;
      }

      // If the open request failed for the error "file not found" proceed,
      // otherwise return kFALSE
      if (fConnModule->GetOpenError() != kXR_NotFound)
         return kFALSE;

      // If connected to a load balancer that says "File not Found" then we
      // try again one more time with refresh before giving up
      if ((fConnModule->GetServerType() == TXNetConn::kSTBaseXrootd) &&
          (!fOpenWithRefresh)) {
         Info("Open", "Trying to re-open the file with REFRESH option...");

         if (!LowOpen(fUrl.GetFileAndOptions(), option, ftitle, compress,
                      netopt, DoInit, kTRUE)) {
    	// Even if after a "resfresh-ed open" the file has not been found
    	// goto in zombie state and return; and let's remember that we used
            // the refresh option
    	//	if(DebugLevel() >= kUSERDEBUG)
    	Error("Open", "Error opening the remote file even after a refresh"
                          " of the load balance. Going into 'zombie' state");
    	fOpenWithRefresh = kTRUE;
    	return kFALSE;
         } else {
    	// Open succeded after the refresh;
    	// Remember that we used the refresh option in order to not use it again
    	fOpenWithRefresh = kTRUE;
    	return kTRUE;
         }
      }

      // If we're here who reported an open error was a data server (rootd or
      // xrootd), then we've to check if we already tried a refresh-ed open
      // request or not...
      if (!fOpenWithRefresh) {

        // if did not use refresh in the last open, and we do not come from a
        // load balancer then goto into zombie state and return
        if (fConnModule->GetLBSUrl() == 0) {
           Error("Open","The remote data server declared 'File Not Found'"
                        " and there's not any load balancer to go back and"
                        " refresh. Going into 'zombie' state...");
           fOpenWithRefresh = kFALSE;
           return kFALSE;
        }

        TString lbsHost = fConnModule->GetLBSUrl()->GetHost();
        Int_t   lbsPort = fConnModule->GetLBSUrl()->GetPort();

        fConnModule->Disconnect(kFALSE);

        Info("Open","The current data server did not find the file. Going back"
                    " to the load balancer and reopen the file in REFRESH mode");
        fConnModule->GoToAnotherServer(lbsHost, lbsPort, netopt);

        // now try to open with refresh...
        Bool_t secondTry = LowOpen(fUrl.GetFileAndOptions(), option, ftitle,
                                   compress, netopt, DoInit, kTRUE);
        if (!secondTry) {
           Error("Open","File not found even after open with REFRESH mode ON.");
           return kFALSE;
        } else
           return kTRUE;
      }

      // if we're here it means that we already tried to open a file in refresh
      // mode and it failed again... then give up...
      return kFALSE;
    }

    //_____________________________________________________________________________
     Bool_t TXNetFile::LowOpen(const char* file, Option_t *option,
                              const char* title, Int_t compress,
    			  Int_t netopt, Bool_t DoInit, Bool_t refresh_open)
    {
      // Low level Open method; deals with xrootd server

      kXR_int16 openOpt; // = 0;

      memset(&openOpt, 0, sizeof(openOpt));

      fOption = option;
      Bool_t forceOpen = kFALSE;
      if (option[0] == '-') {
        fOption   = &option[1];
        forceOpen = kTRUE;
        openOpt |= kXR_force;
      }
      // accept 'f', like 'frecreate' still for backward compatibility
      if (option[0] == 'F' || option[0] == 'f') {
        fOption   = &option[1];
        forceOpen = kTRUE;
        openOpt |= kXR_force;
      }
      Bool_t forceRead = kFALSE;
      if (!strcasecmp(option, "+read")) {
        fOption   = &option[1];
        forceRead = kTRUE;
        openOpt |= kXR_force;
      }
      fOption.ToUpper();
      if (!fOption.CompareTo("NEW")) {
        fOption = "CREATE";
        fWritable = kTRUE;
      }
      if (!fOption.CompareTo("CREATE")) {
        fOption = "CREATE";
        fWritable = kTRUE;
      }
      if (!fOption.CompareTo("UPDATE")) {
        fOption = "UPDATE";
        fWritable = kTRUE;
      }

      Bool_t create   = (!fOption.CompareTo("CREATE"));
      Bool_t recreate = (!fOption.CompareTo("RECREATE"));
      Bool_t update   = (!fOption.CompareTo("UPDATE"));
      Bool_t read     = (!fOption.CompareTo("READ"));

      if (!create && !recreate && !update && !read) {
        read    = kTRUE;
        fOption = "READ";
      }

      Bool_t __recreate = kFALSE;;
      if (recreate) {
        recreate = kFALSE;
        create   = kTRUE;
        fOption  = "CREATE";
        openOpt |= kXR_delete;
        __recreate = kTRUE;
      }
      if (update)
        openOpt |= kXR_open_updt;
      if (read)
        openOpt |= kXR_open_read;

      fCreateMode = create;

      if (create && (!__recreate))
        openOpt |= kXR_new;

      // Send a kXR_open request in order to open the remote file...
      // after formatting the proper data structure...
      ClientRequest openFileRequest;

      struct ServerResponseBody_Open openresp;

      memset(&openFileRequest, 0, sizeof(openFileRequest));
      fConnModule->SetSID(openFileRequest.header.streamid);

      openFileRequest.header.requestid = kXR_open;

      // Now set the options field basing on user's requests
      memset(&openFileRequest.open.options, 0, sizeof(openFileRequest.open.options));
      memcpy(&openFileRequest.open.options, &openOpt, sizeof(openOpt));

      if( refresh_open )
        openFileRequest.open.options |= kXR_refresh;

      // Set the open mode field
      openFileRequest.open.mode =
               kXR_or | kXR_gr | kXR_ur | kXR_uw; // open in rw-r-r mode

      // Set the length of the data (in this case data describes the path and
      // file name)
      openFileRequest.open.dlen = strlen( file );

      // Send request to server and receive response
      bool resp = fConnModule->SendGenCommand(&openFileRequest, (const void *)file,
    					 0, &openresp, kFALSE, (char *)"Open");

      if (resp) {
        // Get the file handle to use for future read/write...
        // Note that in case of heavy load the file could have been opened by an
        // internal retry of the kxr_open
        if (!fOpenPars.FileOpened)
          memcpy( fHandle, openresp.fhandle, sizeof(fHandle) );

        fOpenPars.FileOpened = kTRUE;

        // The call seems successful. We copy the parameters for later use
        if (option)
          fOpenPars.option = option;

        if (title)
          fOpenPars.fTitle = title;

        fOpenPars.compress = compress;
        fOpenPars.netopt = netopt;

        if (DoInit)
          Init(create);
      }

      return fOpenPars.FileOpened;
    }

    //_____________________________________________________________________________
     Int_t TXNetFile::SysStat(Int_t fd, Long_t *id, Long64_t *size, Long_t *flags,
                             Long_t *modtime)
    {
       // Override TNetFile::SysStat (see parent's method for more details)

       if (IsZombie()) {
          Error("SysStat", "SysStat is not possible because object is"
                           " in 'zombie' state");
          *size = 0;
          return 0;
       }

       if (fConnModule->GetServerType() == TXNetConn::kSTRootd) {
          if (DebugLevel() >= kHIDEBUG)
             Info("SysStat","Calling TNetFile::SysStat");
          return TNetFile::SysStat(fd, id, size, flags, modtime);
       }

       if (!IsOpen()) {
          Error("SysStat","The remote file %s is not open",fUrl.GetFileAndOptions());
          *size = 0;
          return 0;
       }

       // Return file stat information. The interface and return value is
       // identical to TSystem::GetPathInfo().

       // asks the server for stat file informations
       ClientRequest statFileRequest;

       memset(&statFileRequest, 0, sizeof(ClientRequest));

       fConnModule->SetSID(statFileRequest.header.streamid);

       statFileRequest.stat.requestid = kXR_stat;
       memset(statFileRequest.stat.reserved, 0,
              sizeof(statFileRequest.stat.reserved));
       statFileRequest.stat.dlen = strlen(fUrl.GetFileAndOptions());

       if (DebugLevel() >= kHIDEBUG)
          Info("SysStat", "Calling TXNetConn::SendGenCommand...");

       char fStats[2048];

       fConnModule->SendGenCommand(&statFileRequest, fUrl.GetFileAndOptions(),
                                   0, fStats , kFALSE, (char *)"SysStat");

       if (DebugLevel() >= kHIDEBUG)
          Info("SysStat", "Returned stats=[%s]",fStats);

       sscanf(fStats, "%ld %lld %ld %ld", id, size, flags, modtime);

       if (*id == -1)
          return 1;

       return 0;
    }

    //_____________________________________________________________________________
     Int_t TXNetFile::SysClose(Int_t fd)
    {
       // Override TNetFile::SysClose (see parent's method for more details)

       if (IsZombie()) {
          Error("SysClose", "SysClose is not possible because object is"
                            " in 'zombie' state");
          return 0;
       }

       if (fConnModule->GetServerType() == TXNetConn::kSTRootd) {
          if (DebugLevel() >= kHIDEBUG)
             Info("SysClose","Calling TNetFile::SysClose");
          return TNetFile::SysClose(fd);
       }

       ClientRequest closeFileRequest;

       memset(&closeFileRequest, 0, sizeof(closeFileRequest) );

       fConnModule->SetSID(closeFileRequest.header.streamid);

       closeFileRequest.close.requestid = kXR_close;
       memcpy(closeFileRequest.close.fhandle, fHandle, sizeof(fHandle) );
       closeFileRequest.close.dlen = 0;

       if (DebugLevel() >= kHIDEBUG)
          Info("SysClose", "Calling TXNetConn::SendGenCommand...");

       fConnModule->SendGenCommand(&closeFileRequest, 0,
    			       0, 0, kFALSE, (char *)"Close");

       // No file is opened for now
       fOpenPars.FileOpened = kFALSE;

       return 0;
    }

    //_____________________________________________________________________________
     Int_t TXNetFile::SysOpen(const char* pathname, Int_t flags, UInt_t mode)
    {
      // Override TNetFile::SysOpen (see parent's method for more details)

      if (fConnModule->GetServerType() == TXNetConn::kSTRootd) {
         if (DebugLevel() >= kHIDEBUG)
            Info("SysOpen", "Calling TNetFile::SysOpen");
         return TNetFile::SysOpen(pathname, flags, mode);
      }

      // url is not needed because already stored in fUrl
      // Here we have to init our TFile ancestor
      if( !Open(fOpenPars.option.Data(), fOpenPars.fTitle.Data(),
                fOpenPars.compress, fOpenPars.netopt, kTRUE) ) {
          Error("SysOpen", "Error opening the file %s on host %s:%d",
                           fUrl.GetFile(),fUrl.GetHost(), fUrl.GetPort());
          return -1;
      }
      return -2;
    }

    //_____________________________________________________________________________
     void TXNetFile::Streamer(TBuffer &R__b)
    {
       // Stream an object of class TXNetFile.
       // Dummy Streamer: rootcint chokes trying to generate one, but ROOT wants
       // one to load the shared library.

       UInt_t R__s, R__c;
       if (R__b.IsReading()) {
          Version_t R__v = R__b.ReadVersion(&R__s, &R__c); if (R__v) { }
          R__b.CheckByteCount(R__s, R__c, TXNetFile::IsA());
       } else {
          R__c = R__b.WriteVersion(TXNetFile::IsA(), kTRUE);
          R__b.SetByteCount(R__c, kTRUE);
       }
    }

    //_____________________________________________________________________________
     Bool_t TXNetFile::OpenFileWhenRedirected(char *newfhandle, Bool_t &wasopen)
    {
       // Called by the comm module when it needs to reopen a file
       // after a redir

       wasopen = fOpenPars.FileOpened;

       if (!fOpenPars.FileOpened)
          return kTRUE;

       fOpenPars.FileOpened = kFALSE;

       if (DebugLevel() >= kHIDEBUG)
          Info("OpenFileWhenRedirected", "Trying to reopen the same file." );

       // After a redirection we must not reinit the TFile ancestor...
       if (Open(fOpenPars.option.Data(), fOpenPars.fTitle.Data(),
                fOpenPars.compress, fOpenPars.netopt, kFALSE)) {

          fOpenPars.FileOpened = kTRUE;

          if (DebugLevel() >= kHIDEBUG)
             Info("OpenFileWhenRedirected",
                  "Open successful. (handle='%s')", fHandle );

          memcpy(newfhandle, fHandle, sizeof(fHandle));

          return kTRUE;
       } else {
          Error("OpenFileWhenRedirected",
    	    "New redir destination server refuses to open the file.");
          MakeZombie();
          return kFALSE;
       }
    }

    //_____________________________________________________________________________
     Long_t TXNetFile::GetRemoteFile(void **bufFile)
    {
       // Retrieve a remote file a store it in memory

       if (IsZombie()) {
          Error("GetRemoteFile", "GetRemoteFile is not possible because"
                                 " object is in 'zombie' state");
          return -1;
       }

       if (fConnModule->GetServerType() == TXNetConn::kSTRootd) {
          Error("GetRemoteFile",
    	    "Using TNetFile to talk to old rootd server, but TNetFile"
                " doesn't support GetFile method");
          return -1;
       }

       if (!IsOpen()) {
          Error("GetRemoteFile","The remote file %s is not open", fUrl.GetFile());
          return -1;
       }

       // First of all we get the file size we want to trasnfer
       Long_t id, flags, modtime;
       Long64_t size;

       this->SysStat(0/* for TNetFile compatibility */, &id, &size, &flags, &modtime);

       ClientRequest readFileRequest;
       memset(&readFileRequest, 0, sizeof(readFileRequest));
       fConnModule->SetSID(readFileRequest.header.streamid);
       readFileRequest.read.requestid = kXR_read;
       memcpy( readFileRequest.read.fhandle, fHandle, sizeof(fHandle) );
       readFileRequest.read.offset = 0;  // we want to read starting from 0 position
                                         // of the file
       readFileRequest.read.rlen = size; // we want to read all the file and put it
                                         // in memory
       readFileRequest.read.dlen = 0;

       // We assume the buffer has been pre-allocated to contain BufferLength
       // bytes by the caller of this function
       if (DebugLevel() >= kHIDEBUG)
          Info("GetRemoteFile",
    	   "Calling TXNetConn::SendGenCommand to read %d bytes of data at"
               " offset %Ld.", readFileRequest.read.rlen, readFileRequest.read.offset);

       Bool_t fail = fConnModule->SendGenCommand(&readFileRequest, 0, bufFile,
    				     0, kTRUE, (char *)"TXNetFile::GetRemoteFile");

       if (!fail) {
          Error("GetRemoteFile", "SendGenCommand returned error");
          return -1;
       }
       return size;
    }

    //_____________________________________________________________________________
     Bool_t TXNetFile::ProcessUnsolicitedMsg(TXUnsolicitedMsgSender *,
                                            TXMessage *)
    {
       // We are here if an unsolicited response comes from a logical conn
       // The response comes in the form of an TXMessage *, that must NOT be
       // destroyed after processing. It is destroyed by the first sender.
       // Remember that we are in a separate thread, since unsolicited
       // responses are asynchronous by nature.

       Info("ProcessUnsolicitedMsg", "Processing unsolicited response");

       // Local processing ....

       return kTRUE;
    }

    //_____________________________________________________________________________
     Int_t TXNetFile::LastBytesSent(void)
    {
       // Return number of bytes last sent

       if (fConnModule)
          return fConnModule->LastBytesSent();
       else
          return 0;
    }

    //_____________________________________________________________________________
     Int_t TXNetFile::LastBytesRecv(void)
    {
       // Return number of bytes last received

       if (fConnModule)
          return fConnModule->LastBytesRecv();
       else
          return 0;
    }

    //_____________________________________________________________________________
     Int_t TXNetFile::LastDataBytesSent(void)
    {
       // Return number of data bytes last sent

       if (fConnModule)
          return fConnModule->LastDataBytesSent();
       else
          return 0;
    }

    //_____________________________________________________________________________
     Int_t TXNetFile::LastDataBytesRecv(void)
    {
       // Return number of data bytes last received

       if (fConnModule)
          return fConnModule->LastDataBytesRecv();
       else
          return 0;
    }

    //_____________________________________________________________________________
     Long64_t TXNetFile::Size(void)
    {
       // Return file size

       if (fAlreadyStated)
          return fSize;

       Long64_t size;
       Long_t i, f, m;

       this->SysStat((Int_t)0, &i, &size, &f, &m);
       fAlreadyStated = kTRUE;

       memcpy((void *)&fSize, (const void*)&size, sizeof(size));
       return fSize;
    }