// @(#)root/net:$Id$
// Author: Fabio Hernandez   22/01/2013
//         extending an initial version by Marcelo Sousa (class TAS3File)

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

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TS3WebFile                                                           //
//                                                                      //
// A TS3WebFile is a TWebFile which retrieves the file contents from a  //
// web server implementing the REST API of the Amazon S3 protocol. This //
// class is meant to be as generic as possible to be used with files    //
// hosted not only by Amazon S3 servers but also by other providers     //
// implementing the core of the S3 protocol.                            //
//                                                                      //
// The S3 protocol works on top of HTTPS (and HTTP) and imposes that    //
// each HTTP request be signed using a specific convention: the request //
// must include an 'Authorization' header which contains the signature  //
// of a concatenation of selected request fields. For signing the       //
// request, an 'Access Key Id' and a 'Secret Access Key' need to be     //
// known. These keys are used by the S3 servers to identify the client  //
// and to authenticate the request as genuine.                          //
//                                                                      //
// As an end user, you must know the Access Key and Secret Access Key   //
// in order to access each S3 file. They are provided to you by your S3 //
// service provider. Those two keys can be provided to ROOT when        //
// initializing an object of this class by two means:                   //
// a) by using the environmental variables S3_ACCESS_KEY and            //
//    S3_SECRET_KEY, or                                                 //
// b) by specifying them when opening each file.                        //
//                                                                      //
// The first method is convenient if all the S3 files you want to       //
// access are hosted by a single provider. The second one is more       //
// flexible as it allows you to specify which credentials to use        //
// on a per-file basis. See the documentation of the constructor of     //
// this class for details on the syntax.                                //
//                                                                      //
// For generating and signing the HTTP request, this class uses         //
// TS3HTTPRequest.                                                      //
//                                                                      //
// For more information on the details of S3 protocol please refer to:  //
// "Amazon Simple Storage Service Developer Guide":                     //
// http://docs.amazonwebservices.com/AmazonS3/latest/dev/Welcome.html   //
//                                                                      //
// "Amazon Simple Storage Service REST API Reference"                   //
//  http://docs.amazonwebservices.com/AmazonS3/latest/API/APIRest.html  //
//////////////////////////////////////////////////////////////////////////

#include "TS3WebFile.h"
#include "TROOT.h"
#include "TError.h"
#include "TSystem.h"
#include "TPRegexp.h"
#include "TEnv.h"


ClassImp(TS3WebFile)

//_____________________________________________________________________________
TS3WebFile::TS3WebFile(const char* path, Option_t* options)
           : TWebFile(path, "IO")
{
   // Construct a TS3WebFile object. The path argument is a URL of one of the
   // following forms:
   //
   //         s3://host.example.com/bucket/path/to/my/file
   //     s3http://host.example.com/bucket/path/to/my/file
   //    s3https://host.example.com/bucket/path/to/my/file
   //        as3://host.example.com/bucket/path/to/my/file
   // 
   // For files hosted by Google Storage, use the following forms:
   //
   //        gs://storage.googleapis.com/bucket/path/to/my/file
   //    gshttp://storage.googleapis.com/bucket/path/to/my/file
   //  gsthttps://storage.googleapis.com/bucket/path/to/my/file
   // 
   // The 'as3' scheme is accepted for backwards compatibility but its usage is
   // deprecated.
   //
   // The recommended way to create an instance of this class is through
   // TFile::Open, for instance:
   //
   // TFile* f1 = TFile::Open("s3://host.example.com/bucket/path/to/my/file")
   // TFile* f2 = TFile::Open("gs://storage.googleapis.com/bucket/path/to/my/file")
   //
   // The specified scheme (i.e. s3, s3http, s3https, ...) determines the underlying
   // transport protocol to use for downloading the file contents, namely HTTP or HTTPS.
   // The 's3', 's3https', 'gs' and 'gshttps' schemes imply using HTTPS as the transport
   // protocol. The 's3http', 'as3' and 'gshttp' schemes imply using HTTP as the transport
   // protocol.
   //
   // The 'options' argument can contain 'NOPROXY' if you want to bypass
   // the HTTP proxy when retrieving this file's contents. As for any TWebFile-derived
   // object, the URL of the web proxy can be specified by setting an environmental
   // variable 'http_proxy'. If this variable is set, we ask that proxy to route our
   // requests HTTP(S) requests to the file server.
   //
   // In addition, you can also use the 'options' argument to provide the access key
   // and secret key to be used for authentication purposes for this file by using a
   // string of the form "AUTH=myAccessKey:mySecretkey". This may be useful to
   // open several files hosted by different providers in the same program/macro,
   // where the environemntal variables solution is not convenient (see below).
   //
   // If you need to specify both NOPROXY and AUTH separate them by ' '
   // (blank), for instance: 
   // "NOPROXY AUTH=F38XYZABCDeFgH4D0E1F:V+frt4re7J1euSNFnmaf8wwmI4AAAE7kzxZ/TTM+"
   //
   // Examples:
   //    TFile* f1 = TFile::Open("s3://host.example.com/bucket/path/to/my/file",
   //                            "NOPROXY AUTH=F38XYZABCDeFgH4D0E1F:V+frt4re7J1euSNFnmaf8wwmI4AAAE7kzxZ/TTM+");
   //    TFile* f2 = TFile::Open("s3://host.example.com/bucket/path/to/my/file",
   //                            "AUTH=F38XYZABCDeFgH4D0E1F:V+frt4re7J1euSNFnmaf8wwmI4AAAE7kzxZ/TTM+");
   //
   // If there is no authentication information in the 'options' argument
   // (i.e. not AUTH="....") the values of the environmental variables
   // S3_ACCESS_KEY and S3_SECRET_KEY (if set) are expected to contain
   // the access key id and the secret access key, respectively. You have
   // been provided with these credentials by your S3 service provider.
   //
   // If neither the AUTH information is provided in the 'options' argument
   // nor the environmental variables are set, we try to open the file
   // without providing any authentication information to the server. This
   // is useful when the file is set an access control that allows for 
   // any unidentified user to read the file.

   // Make sure this is a valid S3 path. We accept 'as3' as a scheme, for
   // backwards compatibility
   Bool_t doMakeZombie = kFALSE;
   TString errorMsg;
   TString accessKey;
   TString secretKey;
   TPMERegexp rex("^([a]?s3|s3http[s]?|gs|gshttp[s]?){1}://([^/]+)/([^/]+)/([^/].*)", "i");
   if (rex.Match(TString(path)) != 5) {
      errorMsg = TString::Format("invalid S3 path '%s'", path);
      doMakeZombie = kTRUE;
   }
   else if (!ParseOptions(options, accessKey, secretKey)) {
      errorMsg = TString::Format("could not parse options '%s'", options);
      doMakeZombie = kTRUE;
   }

   // Should we stop initializing this object?
   if (doMakeZombie) {
      Error("TS3WebFile", "%s", (const char*)errorMsg);
      MakeZombie();
      gDirectory = gROOT;
      return;      
   }

   // Set this S3 object's URL, the bucket name this file is located in
   // and the object key
   fS3Request.SetBucket(rex[3]);
   fS3Request.SetObjectKey(TString::Format("/%s", (const char*)rex[4]));
 
   // Initialize super-classes data members (fUrl is a data member of
   // super-super class TFile)
   TString protocol = "https";
   if (rex[1].EndsWith("http", TString::kIgnoreCase) || 
       rex[1].EqualTo("as3", TString::kIgnoreCase))
      protocol = "http";
   fUrl.SetUrl(TString::Format("%s://%s/%s/%s", (const char*)protocol,
      (const char*)rex[2], (const char*)rex[3], (const char*)rex[4]));
      
   // Set S3-specific data members. If the access and secret keys are not
   // provided in the 'options' argument we look in the environmental 
   // variables.
   const char* kAccessKeyEnv = "S3_ACCESS_KEY";
   const char* kSecretKeyEnv = "S3_SECRET_KEY";
   if (accessKey.IsNull())
      GetCredentialsFromEnv(kAccessKeyEnv, kSecretKeyEnv, accessKey, secretKey);

   // Initialize the S3 HTTP request
   fS3Request.SetHost(fUrl.GetHost());
   if (accessKey.IsNull() || secretKey.IsNull()) {
      // We have no authentication information, neither in the options
      // nor in the enviromental variables. So may be this is a
      // world-readable file, so let's continue and see if
      // we can open it.
      fS3Request.SetAuthType(TS3HTTPRequest::kNoAuth);
   } else {
      // Set the authentication information we need to use
      // for this file
      fS3Request.SetAuthKeys(accessKey, secretKey);
      if (rex[1].BeginsWith("gs"))
         fS3Request.SetAuthType(TS3HTTPRequest::kGoogle);
      else
         fS3Request.SetAuthType(TS3HTTPRequest::kAmazon);
   }
   
   // Assume this server does not serve multi-range HTTP GET requests. We
   // will detect this when the HTTP headers of this files are retrieved
   // later in the initialization process
   fUseMultiRange = kFALSE;
      
   // Call super-class initializer
   TWebFile::Init(kFALSE);

   // Were there some errors opening this file?
   if (IsZombie() && (accessKey.IsNull() || secretKey.IsNull())) {
      // We could not open the file and we have no authentication information
      // so inform the user so that they can check.
      Error("TS3WebFile", "could not find authentication info in "\
         "'options' argument and at least one of the environment variables '%s' or '%s' is not set",
         kAccessKeyEnv, kSecretKeyEnv);     
   }
}


//_____________________________________________________________________________
Bool_t TS3WebFile::ParseOptions(Option_t* options, TString& accessKey, TString& secretKey)
{
   // Extracts the S3 authentication key pair (access key and secret key)
   // from the options. The authentication credentials can be specified in
   // the options provided to the constructor of this class as a string
   // containing: "AUTH=<access key>:<secret key>" and can include other
   // options, for instance "NOPROXY" for not using the HTTP proxy for
   // accessing this file's contents.
   // For instance:
   // "NOPROXY AUTH=F38XYZABCDeFgHiJkLm:V+frt4re7J1euSNFnmaf8wwmI401234E7kzxZ/TTM+"
   
   TString optStr = (const char*)options;
   if (optStr.IsNull())
      return kTRUE;
      
   fNoProxy = kFALSE;
   if (optStr.Contains("NOPROXY", TString::kIgnoreCase))
      fNoProxy = kTRUE;
   CheckProxy();
   
   // Look in the options string for the authentication information.
   TPMERegexp rex("(^AUTH=|^.* AUTH=)([a-z0-9]+):([a-z0-9+/]+)[\\s]*.*$", "i");
   if (rex.Match(optStr) < 4) {
      Error("ParseOptions", "expecting options of the form \"AUTH=myAccessKey:mySecretKey\"");
      return kFALSE;
   }
   accessKey = rex[2];
   secretKey = rex[3];
   if (gDebug > 0)
      Info("ParseOptions", "using authentication information from 'options' argument");
   return kTRUE;
}


//_____________________________________________________________________________
Int_t TS3WebFile::GetHead()
{
   // Overwrites TWebFile::GetHead() for retrieving the HTTP headers of this
   // file. Uses TS3HTTPRequest to generate an HTTP HEAD request which includes
   // the authorization header expected by the S3 server.
   fMsgGetHead = fS3Request.GetRequest(TS3HTTPRequest::kHEAD);
   return TWebFile::GetHead();
}


//_____________________________________________________________________________
void TS3WebFile::SetMsgReadBuffer10(const char* redirectLocation, Bool_t tempRedirect)
{
   // Overwrites TWebFile::SetMsgReadBuffer10() for setting the HTTP GET
   // request compliant to the authentication mechanism used by the S3
   // protocol. The GET request must contain an "Authorization" header with
   // the signature of the request, generated using the user's secret access
   // key.

   TWebFile::SetMsgReadBuffer10(redirectLocation, tempRedirect);
   fMsgReadBuffer10 = fS3Request.GetRequest(TS3HTTPRequest::kGET, kFALSE) + "Range: bytes=";
   return;
}


//_____________________________________________________________________________
Bool_t TS3WebFile::ReadBuffers(char* buf, Long64_t* pos, Int_t* len, Int_t nbuf)
{

   // Overwrites TWebFile::ReadBuffers() for reading specified byte ranges.
   // According to the kind of server this file is hosted by, we use a
   // single HTTP request with a muti-range header or we generate multiple
   // requests with a single range each.
   
   // Does this server support multi-range GET requests?
   if (fUseMultiRange)
      return TWebFile::ReadBuffers(buf, pos, len, nbuf);

   // Send multiple GET requests with a single range of bytes
   // Adapted from original version by Wang Lu
   for (Int_t i=0, offset=0; i < nbuf; i++) {
      TString rangeHeader = TString::Format("Range: bytes=%lld-%lld\r\n\r\n",
         pos[i], pos[i] + len[i] - 1);
      TString s3Request = fS3Request.GetRequest(TS3HTTPRequest::kGET, kFALSE) + rangeHeader;
      if (GetFromWeb10(&buf[offset], len[i], s3Request) == -1)
         return kTRUE;
      offset += len[i];
   }
   return kFALSE;
}


//_____________________________________________________________________________
void TS3WebFile::ProcessHttpHeader(const TString& headerLine)
{
   // This method is called by the super-class TWebFile when a HTTP header
   // for this file is retrieved. We scan the 'Server' header to detect the
   // type of S3 server this file is hosted on and to determine if it is
   // known to support multi-range HTTP GET requests. Some S3 servers (for
   // instance Amazon's) do not support that feature and when they
   // receive a multi-range request they sent back the whole file contents.
   // For this class, if the server do not support multirange requests
   // we issue multiple single-range requests instead.
   
   TPMERegexp rex("^Server: (.+)", "i");
   if (rex.Match(headerLine) != 2)
      return;

   // Extract the identity of this server and compare it to the
   // identify of the servers known to support multi-range requests.
   // The list of server identities is expected to be found in ROOT
   // configuration.
   TString serverId = rex[1].ReplaceAll("\r", "").ReplaceAll("\n", "");
   TString multirangeServers(gEnv->GetValue("TS3WebFile.Root.MultiRangeServer", ""));
   fUseMultiRange = multirangeServers.Contains(serverId, TString::kIgnoreCase) ? kTRUE : kFALSE;
}


//_____________________________________________________________________________
Bool_t TS3WebFile::GetCredentialsFromEnv(const char* accessKeyEnv, const char* secretKeyEnv,
                                         TString& outAccessKey, TString& outSecretKey)
{
   // Sets the access and secret keys from the environmental variables, if
   // they are both set.

   // Look first in the recommended environmental variables. Both variables 
   // must be set.
   TString accKey = gSystem->Getenv(accessKeyEnv);
   TString secKey = gSystem->Getenv(secretKeyEnv);
   if (!accKey.IsNull() && !secKey.IsNull()) {
      outAccessKey = accKey;
      outSecretKey = secKey;
      if (gDebug > 0)
         Info("GetCredentialsFromEnv", "using authentication information from environmental variables '%s' and '%s'",
            accessKeyEnv, secretKeyEnv);
      return kTRUE;
   }

   // Look now in the legacy environmental variables, for keeping backwards
   // compatibility.
   accKey = gSystem->Getenv("S3_ACCESS_ID"); // Legacy access key
   secKey = gSystem->Getenv("S3_ACCESS_KEY"); // Legacy secret key
   if (!accKey.IsNull() && !secKey.IsNull()) {
      Warning("SetAuthKeys", "usage of S3_ACCESS_ID and S3_ACCESS_KEY environmental variables is deprecated.");
      Warning("SetAuthKeys", "please use S3_ACCESS_KEY and S3_SECRET_KEY environmental variables.");
      outAccessKey = accKey;
      outSecretKey = secKey;
      return kTRUE;
   }

   return kFALSE;
}

 TS3WebFile.cxx:1
 TS3WebFile.cxx:2
 TS3WebFile.cxx:3
 TS3WebFile.cxx:4
 TS3WebFile.cxx:5
 TS3WebFile.cxx:6
 TS3WebFile.cxx:7
 TS3WebFile.cxx:8
 TS3WebFile.cxx:9
 TS3WebFile.cxx:10
 TS3WebFile.cxx:11
 TS3WebFile.cxx:12
 TS3WebFile.cxx:13
 TS3WebFile.cxx:14
 TS3WebFile.cxx:15
 TS3WebFile.cxx:16
 TS3WebFile.cxx:17
 TS3WebFile.cxx:18
 TS3WebFile.cxx:19
 TS3WebFile.cxx:20
 TS3WebFile.cxx:21
 TS3WebFile.cxx:22
 TS3WebFile.cxx:23
 TS3WebFile.cxx:24
 TS3WebFile.cxx:25
 TS3WebFile.cxx:26
 TS3WebFile.cxx:27
 TS3WebFile.cxx:28
 TS3WebFile.cxx:29
 TS3WebFile.cxx:30
 TS3WebFile.cxx:31
 TS3WebFile.cxx:32
 TS3WebFile.cxx:33
 TS3WebFile.cxx:34
 TS3WebFile.cxx:35
 TS3WebFile.cxx:36
 TS3WebFile.cxx:37
 TS3WebFile.cxx:38
 TS3WebFile.cxx:39
 TS3WebFile.cxx:40
 TS3WebFile.cxx:41
 TS3WebFile.cxx:42
 TS3WebFile.cxx:43
 TS3WebFile.cxx:44
 TS3WebFile.cxx:45
 TS3WebFile.cxx:46
 TS3WebFile.cxx:47
 TS3WebFile.cxx:48
 TS3WebFile.cxx:49
 TS3WebFile.cxx:50
 TS3WebFile.cxx:51
 TS3WebFile.cxx:52
 TS3WebFile.cxx:53
 TS3WebFile.cxx:54
 TS3WebFile.cxx:55
 TS3WebFile.cxx:56
 TS3WebFile.cxx:57
 TS3WebFile.cxx:58
 TS3WebFile.cxx:59
 TS3WebFile.cxx:60
 TS3WebFile.cxx:61
 TS3WebFile.cxx:62
 TS3WebFile.cxx:63
 TS3WebFile.cxx:64
 TS3WebFile.cxx:65
 TS3WebFile.cxx:66
 TS3WebFile.cxx:67
 TS3WebFile.cxx:68
 TS3WebFile.cxx:69
 TS3WebFile.cxx:70
 TS3WebFile.cxx:71
 TS3WebFile.cxx:72
 TS3WebFile.cxx:73
 TS3WebFile.cxx:74
 TS3WebFile.cxx:75
 TS3WebFile.cxx:76
 TS3WebFile.cxx:77
 TS3WebFile.cxx:78
 TS3WebFile.cxx:79
 TS3WebFile.cxx:80
 TS3WebFile.cxx:81
 TS3WebFile.cxx:82
 TS3WebFile.cxx:83
 TS3WebFile.cxx:84
 TS3WebFile.cxx:85
 TS3WebFile.cxx:86
 TS3WebFile.cxx:87
 TS3WebFile.cxx:88
 TS3WebFile.cxx:89
 TS3WebFile.cxx:90
 TS3WebFile.cxx:91
 TS3WebFile.cxx:92
 TS3WebFile.cxx:93
 TS3WebFile.cxx:94
 TS3WebFile.cxx:95
 TS3WebFile.cxx:96
 TS3WebFile.cxx:97
 TS3WebFile.cxx:98
 TS3WebFile.cxx:99
 TS3WebFile.cxx:100
 TS3WebFile.cxx:101
 TS3WebFile.cxx:102
 TS3WebFile.cxx:103
 TS3WebFile.cxx:104
 TS3WebFile.cxx:105
 TS3WebFile.cxx:106
 TS3WebFile.cxx:107
 TS3WebFile.cxx:108
 TS3WebFile.cxx:109
 TS3WebFile.cxx:110
 TS3WebFile.cxx:111
 TS3WebFile.cxx:112
 TS3WebFile.cxx:113
 TS3WebFile.cxx:114
 TS3WebFile.cxx:115
 TS3WebFile.cxx:116
 TS3WebFile.cxx:117
 TS3WebFile.cxx:118
 TS3WebFile.cxx:119
 TS3WebFile.cxx:120
 TS3WebFile.cxx:121
 TS3WebFile.cxx:122
 TS3WebFile.cxx:123
 TS3WebFile.cxx:124
 TS3WebFile.cxx:125
 TS3WebFile.cxx:126
 TS3WebFile.cxx:127
 TS3WebFile.cxx:128
 TS3WebFile.cxx:129
 TS3WebFile.cxx:130
 TS3WebFile.cxx:131
 TS3WebFile.cxx:132
 TS3WebFile.cxx:133
 TS3WebFile.cxx:134
 TS3WebFile.cxx:135
 TS3WebFile.cxx:136
 TS3WebFile.cxx:137
 TS3WebFile.cxx:138
 TS3WebFile.cxx:139
 TS3WebFile.cxx:140
 TS3WebFile.cxx:141
 TS3WebFile.cxx:142
 TS3WebFile.cxx:143
 TS3WebFile.cxx:144
 TS3WebFile.cxx:145
 TS3WebFile.cxx:146
 TS3WebFile.cxx:147
 TS3WebFile.cxx:148
 TS3WebFile.cxx:149
 TS3WebFile.cxx:150
 TS3WebFile.cxx:151
 TS3WebFile.cxx:152
 TS3WebFile.cxx:153
 TS3WebFile.cxx:154
 TS3WebFile.cxx:155
 TS3WebFile.cxx:156
 TS3WebFile.cxx:157
 TS3WebFile.cxx:158
 TS3WebFile.cxx:159
 TS3WebFile.cxx:160
 TS3WebFile.cxx:161
 TS3WebFile.cxx:162
 TS3WebFile.cxx:163
 TS3WebFile.cxx:164
 TS3WebFile.cxx:165
 TS3WebFile.cxx:166
 TS3WebFile.cxx:167
 TS3WebFile.cxx:168
 TS3WebFile.cxx:169
 TS3WebFile.cxx:170
 TS3WebFile.cxx:171
 TS3WebFile.cxx:172
 TS3WebFile.cxx:173
 TS3WebFile.cxx:174
 TS3WebFile.cxx:175
 TS3WebFile.cxx:176
 TS3WebFile.cxx:177
 TS3WebFile.cxx:178
 TS3WebFile.cxx:179
 TS3WebFile.cxx:180
 TS3WebFile.cxx:181
 TS3WebFile.cxx:182
 TS3WebFile.cxx:183
 TS3WebFile.cxx:184
 TS3WebFile.cxx:185
 TS3WebFile.cxx:186
 TS3WebFile.cxx:187
 TS3WebFile.cxx:188
 TS3WebFile.cxx:189
 TS3WebFile.cxx:190
 TS3WebFile.cxx:191
 TS3WebFile.cxx:192
 TS3WebFile.cxx:193
 TS3WebFile.cxx:194
 TS3WebFile.cxx:195
 TS3WebFile.cxx:196
 TS3WebFile.cxx:197
 TS3WebFile.cxx:198
 TS3WebFile.cxx:199
 TS3WebFile.cxx:200
 TS3WebFile.cxx:201
 TS3WebFile.cxx:202
 TS3WebFile.cxx:203
 TS3WebFile.cxx:204
 TS3WebFile.cxx:205
 TS3WebFile.cxx:206
 TS3WebFile.cxx:207
 TS3WebFile.cxx:208
 TS3WebFile.cxx:209
 TS3WebFile.cxx:210
 TS3WebFile.cxx:211
 TS3WebFile.cxx:212
 TS3WebFile.cxx:213
 TS3WebFile.cxx:214
 TS3WebFile.cxx:215
 TS3WebFile.cxx:216
 TS3WebFile.cxx:217
 TS3WebFile.cxx:218
 TS3WebFile.cxx:219
 TS3WebFile.cxx:220
 TS3WebFile.cxx:221
 TS3WebFile.cxx:222
 TS3WebFile.cxx:223
 TS3WebFile.cxx:224
 TS3WebFile.cxx:225
 TS3WebFile.cxx:226
 TS3WebFile.cxx:227
 TS3WebFile.cxx:228
 TS3WebFile.cxx:229
 TS3WebFile.cxx:230
 TS3WebFile.cxx:231
 TS3WebFile.cxx:232
 TS3WebFile.cxx:233
 TS3WebFile.cxx:234
 TS3WebFile.cxx:235
 TS3WebFile.cxx:236
 TS3WebFile.cxx:237
 TS3WebFile.cxx:238
 TS3WebFile.cxx:239
 TS3WebFile.cxx:240
 TS3WebFile.cxx:241
 TS3WebFile.cxx:242
 TS3WebFile.cxx:243
 TS3WebFile.cxx:244
 TS3WebFile.cxx:245
 TS3WebFile.cxx:246
 TS3WebFile.cxx:247
 TS3WebFile.cxx:248
 TS3WebFile.cxx:249
 TS3WebFile.cxx:250
 TS3WebFile.cxx:251
 TS3WebFile.cxx:252
 TS3WebFile.cxx:253
 TS3WebFile.cxx:254
 TS3WebFile.cxx:255
 TS3WebFile.cxx:256
 TS3WebFile.cxx:257
 TS3WebFile.cxx:258
 TS3WebFile.cxx:259
 TS3WebFile.cxx:260
 TS3WebFile.cxx:261
 TS3WebFile.cxx:262
 TS3WebFile.cxx:263
 TS3WebFile.cxx:264
 TS3WebFile.cxx:265
 TS3WebFile.cxx:266
 TS3WebFile.cxx:267
 TS3WebFile.cxx:268
 TS3WebFile.cxx:269
 TS3WebFile.cxx:270
 TS3WebFile.cxx:271
 TS3WebFile.cxx:272
 TS3WebFile.cxx:273
 TS3WebFile.cxx:274
 TS3WebFile.cxx:275
 TS3WebFile.cxx:276
 TS3WebFile.cxx:277
 TS3WebFile.cxx:278
 TS3WebFile.cxx:279
 TS3WebFile.cxx:280
 TS3WebFile.cxx:281
 TS3WebFile.cxx:282
 TS3WebFile.cxx:283
 TS3WebFile.cxx:284
 TS3WebFile.cxx:285
 TS3WebFile.cxx:286
 TS3WebFile.cxx:287
 TS3WebFile.cxx:288
 TS3WebFile.cxx:289
 TS3WebFile.cxx:290
 TS3WebFile.cxx:291
 TS3WebFile.cxx:292
 TS3WebFile.cxx:293
 TS3WebFile.cxx:294
 TS3WebFile.cxx:295
 TS3WebFile.cxx:296
 TS3WebFile.cxx:297
 TS3WebFile.cxx:298
 TS3WebFile.cxx:299
 TS3WebFile.cxx:300
 TS3WebFile.cxx:301
 TS3WebFile.cxx:302
 TS3WebFile.cxx:303
 TS3WebFile.cxx:304
 TS3WebFile.cxx:305
 TS3WebFile.cxx:306
 TS3WebFile.cxx:307
 TS3WebFile.cxx:308
 TS3WebFile.cxx:309
 TS3WebFile.cxx:310
 TS3WebFile.cxx:311
 TS3WebFile.cxx:312
 TS3WebFile.cxx:313
 TS3WebFile.cxx:314
 TS3WebFile.cxx:315
 TS3WebFile.cxx:316
 TS3WebFile.cxx:317
 TS3WebFile.cxx:318
 TS3WebFile.cxx:319
 TS3WebFile.cxx:320
 TS3WebFile.cxx:321
 TS3WebFile.cxx:322
 TS3WebFile.cxx:323
 TS3WebFile.cxx:324
 TS3WebFile.cxx:325
 TS3WebFile.cxx:326
 TS3WebFile.cxx:327
 TS3WebFile.cxx:328
 TS3WebFile.cxx:329
 TS3WebFile.cxx:330
 TS3WebFile.cxx:331
 TS3WebFile.cxx:332
 TS3WebFile.cxx:333
 TS3WebFile.cxx:334
 TS3WebFile.cxx:335
 TS3WebFile.cxx:336
 TS3WebFile.cxx:337
 TS3WebFile.cxx:338
 TS3WebFile.cxx:339
 TS3WebFile.cxx:340
 TS3WebFile.cxx:341
 TS3WebFile.cxx:342
 TS3WebFile.cxx:343
 TS3WebFile.cxx:344
 TS3WebFile.cxx:345
 TS3WebFile.cxx:346
 TS3WebFile.cxx:347
 TS3WebFile.cxx:348
 TS3WebFile.cxx:349
 TS3WebFile.cxx:350
 TS3WebFile.cxx:351
 TS3WebFile.cxx:352
 TS3WebFile.cxx:353
 TS3WebFile.cxx:354
 TS3WebFile.cxx:355
 TS3WebFile.cxx:356
 TS3WebFile.cxx:357
 TS3WebFile.cxx:358
 TS3WebFile.cxx:359
 TS3WebFile.cxx:360
 TS3WebFile.cxx:361
 TS3WebFile.cxx:362
 TS3WebFile.cxx:363
 TS3WebFile.cxx:364