// @(#)root/net:$Id$
// Author: Fabio Hernandez 30/01/2013
//         based on an initial version by Marcelo Sousa (class THTTPMessage)

/*************************************************************************
 * 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.             *
 *************************************************************************/

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TS3HTTPRequest                                                       //
//                                                                      //
// An object of this class represents an HTTP request extended to be    //
// compatible with Amazon's S3 protocol.                                //
// Specifically, such a request contains an 'Authorization' header with //
// information used by the S3 server for authenticating this request.   //
// The authentication information is computed based on a pair of access //
// key and secret key which are both provided to the user by the S3     //
// service provider (e.g. Amazon, Google, etc.).                        //
// The secret key is used to compute a signature of selected fields in  //
// the request. The algorithm for computing the signature is documented //
// in:                                                                  //
//                                                                      //
// Google storage:                                                      //
// http://code.google.com/apis/storage/docs/reference/v1/developer-guidev1.html#authentication
//                                                                      //
// Amazon:                                                              //
// http://docs.aws.amazon.com/AmazonS3/latest/dev/S3_Authentication2.html
//                                                                      //
//////////////////////////////////////////////////////////////////////////

#include "TS3HTTPRequest.h"
#include "TBase64.h"
#if defined(MAC_OS_X_VERSION_10_7)
#include <CommonCrypto/CommonHMAC.h>
#define SHA_DIGEST_LENGTH 20
#else
#include <openssl/sha.h>
#include <openssl/hmac.h>
#include <openssl/evp.h>
#include <openssl/bio.h>
#include <openssl/buffer.h>
#endif

#include <stdio.h>
#include <time.h>
#include <string.h>

ClassImp(TS3HTTPRequest)

//______________________________________________________________________________
TS3HTTPRequest::TS3HTTPRequest()
               : fAuthType(kNoAuth), fHost("NoHost")
{
}

//______________________________________________________________________________
TS3HTTPRequest::TS3HTTPRequest(EHTTPVerb httpVerb, const TString& host,
   const TString& bucket, const TString& objectKey, EAuthType authType,
   const TString& accessKey, const TString& secretKey)
{
   // Default constructor

   fVerb      = httpVerb;
   fHost      = host;
   fBucket    = bucket;
   fObjectKey = objectKey;
   fAuthType  = authType;
   fAccessKey = accessKey;
   fSecretKey = secretKey;
}

//______________________________________________________________________________
TS3HTTPRequest::TS3HTTPRequest(const TS3HTTPRequest& r)
               : TObject(r)
{
   // Copy constructor

   fVerb      = r.fVerb;
   fHost      = r.fHost;
   fBucket    = r.fBucket;
   fObjectKey = r.fObjectKey;
   fAuthType  = r.fAuthType;
   fAccessKey = r.fAccessKey;
   fSecretKey = r.fSecretKey;
   fTimeStamp = r.fTimeStamp;
}

//______________________________________________________________________________
TString TS3HTTPRequest::ComputeSignature(TS3HTTPRequest::EHTTPVerb httpVerb) const
{
   // Returns this request's signature

   // Please note, the order of the fields used for computing
   // the signature is important. Make sure that the changes you
   // make are compatible with the reference documentation.
   //
   // Refs:
   //    AMAZON  http://awsdocs.s3.amazonaws.com/S3/latest/s3-qrc.pdf
   //    GOOGLE: http://code.google.com/apis/storage/docs/reference/v1/developer-guidev1.html#authentication

   TString toSign = TString::Format("%s\n\n\n%s\n",  // empty Content-MD5 and Content-Type
                                    (const char*)HTTPVerbToTString(httpVerb),
                                    (const char*)fTimeStamp);
   if (fAuthType == kGoogle) {
      // Must use API version 1. Google Storage API v2 only
      // accepts OAuth authentication.
      // This header is not strictly needed but if used for computing
      // the signature, the request must contain it as a header
      // (see method MakeAuthHeader)
      // Ref: https://developers.google.com/storage/docs/reference/v1/apiversion1
      toSign += "x-goog-api-version:1\n"; // Lowercase, no spaces around ':'
   }

   toSign += "/" + fBucket + fObjectKey;

   unsigned char digest[SHA_DIGEST_LENGTH] = {0};
#if defined(MAC_OS_X_VERSION_10_7)
   CCHmac(kCCHmacAlgSHA1, fSecretKey.Data(), fSecretKey.Length() , (unsigned char *)toSign.Data(), toSign.Length(), digest);
#else
   unsigned int *sd = NULL;
   HMAC(EVP_sha1(), fSecretKey.Data(), fSecretKey.Length() , (unsigned char *)toSign.Data(), toSign.Length(), digest, sd);
#endif

   return TBase64::Encode((const char *)digest, SHA_DIGEST_LENGTH);
}

//______________________________________________________________________________
TString TS3HTTPRequest::HTTPVerbToTString(TS3HTTPRequest::EHTTPVerb httpVerb) const
{
   switch (httpVerb) {
      case kGET:    return TString("GET");
      case kPOST:   return TString("POST");
      case kPUT:    return TString("PUT");
      case kDELETE: return TString("DELETE");
      case kHEAD:   return TString("HEAD");
      case kCOPY:   return TString("COPY");
      default:      return TString("");
   }
}

//______________________________________________________________________________
TS3HTTPRequest& TS3HTTPRequest::SetTimeStamp()
{
   // Sets this request's time stamp according to:
   //   http://code.google.com/apis/storage/docs/reference-headers.html#date

   time_t now = time(NULL);
   char result[128];
#ifdef _REENTRANT
   struct tm dateFormat;
   strftime(result, sizeof(result), "%a, %d %b %Y %H:%M:%S GMT",
      gmtime_r(&now, &dateFormat));
#else
   strftime(result, sizeof(result), "%a, %d %b %Y %H:%M:%S GMT",
      gmtime(&now));
#endif
   fTimeStamp = result;
   return *this;
}

//______________________________________________________________________________
TString TS3HTTPRequest::MakeRequestLine(TS3HTTPRequest::EHTTPVerb httpVerb) const
{
   // Returns the first line of a HTTP request for this object. Note that since
   // we don't use the virtual host syntax which is supported by Amazon, we
   // must include the bucket name in thr resource. For example, we don't use
   // http://mybucket.s3.amazonaws.com/path/to/my/file but instead
   // http://s3.amazonaws.com/mybucket/path/to/my/file so the HTTP request
   // will be of the form "GET /mybucket/path/to/my/file HTTP/1.1"
   // Also note that the path must include the leading '/'.

   return TString::Format("%s /%s%s HTTP/1.1",
                          (const char*)HTTPVerbToTString(httpVerb),
                          (const char*)fBucket,
                          (const char*)fObjectKey);
}

//______________________________________________________________________________
TString TS3HTTPRequest::MakeHostHeader() const
{
   // Returns the 'Host' header to include in the HTTP request.

   return "Host: " + fHost;
}

//______________________________________________________________________________
TString TS3HTTPRequest::MakeDateHeader() const
{
   // Returns the date header for this HTTP request

   return "Date: " + fTimeStamp;
}

//______________________________________________________________________________
TString TS3HTTPRequest::MakeAuthPrefix() const
{
   // Returns the authentication prefix

   switch (fAuthType) {
      case kNoAuth: return "";
      case kGoogle: return "GOOG1";
      case kAmazon:
      default:      return "AWS";
   }
}

//______________________________________________________________________________
TString TS3HTTPRequest::MakeAuthHeader(TS3HTTPRequest::EHTTPVerb httpVerb) const
{
   // Returns the authentication header for this HTTP request

   if (fAuthType == kNoAuth)
      return "";

   return TString::Format("Authorization: %s %s:%s%s",
      (const char*)MakeAuthPrefix(),
      (const char*)fAccessKey,
      (const char*)ComputeSignature(httpVerb),
      (fAuthType == kGoogle) ? "\r\nx-goog-api-version: 1" : "");
}

//______________________________________________________________________________
TString TS3HTTPRequest::GetRequest(TS3HTTPRequest::EHTTPVerb httpVerb, Bool_t appendCRLF)
{
   // Returns the HTTP request ready to be sent to the server

   // Set time stamp before computing this request's signature. The signature
   // includes the date.
   SetTimeStamp();
   TString request = TString::Format("%s\r\n%s\r\n%s\r\n",
      (const char*)MakeRequestLine(httpVerb),
      (const char*)MakeHostHeader(),
      (const char*)MakeDateHeader());
   TString authHeader = MakeAuthHeader(httpVerb);
   if (!authHeader.IsNull())
      request += authHeader + "\r\n";
   if (appendCRLF)
      request += "\r\n";
   return request;
}
 TS3HTTPRequest.cxx:1
 TS3HTTPRequest.cxx:2
 TS3HTTPRequest.cxx:3
 TS3HTTPRequest.cxx:4
 TS3HTTPRequest.cxx:5
 TS3HTTPRequest.cxx:6
 TS3HTTPRequest.cxx:7
 TS3HTTPRequest.cxx:8
 TS3HTTPRequest.cxx:9
 TS3HTTPRequest.cxx:10
 TS3HTTPRequest.cxx:11
 TS3HTTPRequest.cxx:12
 TS3HTTPRequest.cxx:13
 TS3HTTPRequest.cxx:14
 TS3HTTPRequest.cxx:15
 TS3HTTPRequest.cxx:16
 TS3HTTPRequest.cxx:17
 TS3HTTPRequest.cxx:18
 TS3HTTPRequest.cxx:19
 TS3HTTPRequest.cxx:20
 TS3HTTPRequest.cxx:21
 TS3HTTPRequest.cxx:22
 TS3HTTPRequest.cxx:23
 TS3HTTPRequest.cxx:24
 TS3HTTPRequest.cxx:25
 TS3HTTPRequest.cxx:26
 TS3HTTPRequest.cxx:27
 TS3HTTPRequest.cxx:28
 TS3HTTPRequest.cxx:29
 TS3HTTPRequest.cxx:30
 TS3HTTPRequest.cxx:31
 TS3HTTPRequest.cxx:32
 TS3HTTPRequest.cxx:33
 TS3HTTPRequest.cxx:34
 TS3HTTPRequest.cxx:35
 TS3HTTPRequest.cxx:36
 TS3HTTPRequest.cxx:37
 TS3HTTPRequest.cxx:38
 TS3HTTPRequest.cxx:39
 TS3HTTPRequest.cxx:40
 TS3HTTPRequest.cxx:41
 TS3HTTPRequest.cxx:42
 TS3HTTPRequest.cxx:43
 TS3HTTPRequest.cxx:44
 TS3HTTPRequest.cxx:45
 TS3HTTPRequest.cxx:46
 TS3HTTPRequest.cxx:47
 TS3HTTPRequest.cxx:48
 TS3HTTPRequest.cxx:49
 TS3HTTPRequest.cxx:50
 TS3HTTPRequest.cxx:51
 TS3HTTPRequest.cxx:52
 TS3HTTPRequest.cxx:53
 TS3HTTPRequest.cxx:54
 TS3HTTPRequest.cxx:55
 TS3HTTPRequest.cxx:56
 TS3HTTPRequest.cxx:57
 TS3HTTPRequest.cxx:58
 TS3HTTPRequest.cxx:59
 TS3HTTPRequest.cxx:60
 TS3HTTPRequest.cxx:61
 TS3HTTPRequest.cxx:62
 TS3HTTPRequest.cxx:63
 TS3HTTPRequest.cxx:64
 TS3HTTPRequest.cxx:65
 TS3HTTPRequest.cxx:66
 TS3HTTPRequest.cxx:67
 TS3HTTPRequest.cxx:68
 TS3HTTPRequest.cxx:69
 TS3HTTPRequest.cxx:70
 TS3HTTPRequest.cxx:71
 TS3HTTPRequest.cxx:72
 TS3HTTPRequest.cxx:73
 TS3HTTPRequest.cxx:74
 TS3HTTPRequest.cxx:75
 TS3HTTPRequest.cxx:76
 TS3HTTPRequest.cxx:77
 TS3HTTPRequest.cxx:78
 TS3HTTPRequest.cxx:79
 TS3HTTPRequest.cxx:80
 TS3HTTPRequest.cxx:81
 TS3HTTPRequest.cxx:82
 TS3HTTPRequest.cxx:83
 TS3HTTPRequest.cxx:84
 TS3HTTPRequest.cxx:85
 TS3HTTPRequest.cxx:86
 TS3HTTPRequest.cxx:87
 TS3HTTPRequest.cxx:88
 TS3HTTPRequest.cxx:89
 TS3HTTPRequest.cxx:90
 TS3HTTPRequest.cxx:91
 TS3HTTPRequest.cxx:92
 TS3HTTPRequest.cxx:93
 TS3HTTPRequest.cxx:94
 TS3HTTPRequest.cxx:95
 TS3HTTPRequest.cxx:96
 TS3HTTPRequest.cxx:97
 TS3HTTPRequest.cxx:98
 TS3HTTPRequest.cxx:99
 TS3HTTPRequest.cxx:100
 TS3HTTPRequest.cxx:101
 TS3HTTPRequest.cxx:102
 TS3HTTPRequest.cxx:103
 TS3HTTPRequest.cxx:104
 TS3HTTPRequest.cxx:105
 TS3HTTPRequest.cxx:106
 TS3HTTPRequest.cxx:107
 TS3HTTPRequest.cxx:108
 TS3HTTPRequest.cxx:109
 TS3HTTPRequest.cxx:110
 TS3HTTPRequest.cxx:111
 TS3HTTPRequest.cxx:112
 TS3HTTPRequest.cxx:113
 TS3HTTPRequest.cxx:114
 TS3HTTPRequest.cxx:115
 TS3HTTPRequest.cxx:116
 TS3HTTPRequest.cxx:117
 TS3HTTPRequest.cxx:118
 TS3HTTPRequest.cxx:119
 TS3HTTPRequest.cxx:120
 TS3HTTPRequest.cxx:121
 TS3HTTPRequest.cxx:122
 TS3HTTPRequest.cxx:123
 TS3HTTPRequest.cxx:124
 TS3HTTPRequest.cxx:125
 TS3HTTPRequest.cxx:126
 TS3HTTPRequest.cxx:127
 TS3HTTPRequest.cxx:128
 TS3HTTPRequest.cxx:129
 TS3HTTPRequest.cxx:130
 TS3HTTPRequest.cxx:131
 TS3HTTPRequest.cxx:132
 TS3HTTPRequest.cxx:133
 TS3HTTPRequest.cxx:134
 TS3HTTPRequest.cxx:135
 TS3HTTPRequest.cxx:136
 TS3HTTPRequest.cxx:137
 TS3HTTPRequest.cxx:138
 TS3HTTPRequest.cxx:139
 TS3HTTPRequest.cxx:140
 TS3HTTPRequest.cxx:141
 TS3HTTPRequest.cxx:142
 TS3HTTPRequest.cxx:143
 TS3HTTPRequest.cxx:144
 TS3HTTPRequest.cxx:145
 TS3HTTPRequest.cxx:146
 TS3HTTPRequest.cxx:147
 TS3HTTPRequest.cxx:148
 TS3HTTPRequest.cxx:149
 TS3HTTPRequest.cxx:150
 TS3HTTPRequest.cxx:151
 TS3HTTPRequest.cxx:152
 TS3HTTPRequest.cxx:153
 TS3HTTPRequest.cxx:154
 TS3HTTPRequest.cxx:155
 TS3HTTPRequest.cxx:156
 TS3HTTPRequest.cxx:157
 TS3HTTPRequest.cxx:158
 TS3HTTPRequest.cxx:159
 TS3HTTPRequest.cxx:160
 TS3HTTPRequest.cxx:161
 TS3HTTPRequest.cxx:162
 TS3HTTPRequest.cxx:163
 TS3HTTPRequest.cxx:164
 TS3HTTPRequest.cxx:165
 TS3HTTPRequest.cxx:166
 TS3HTTPRequest.cxx:167
 TS3HTTPRequest.cxx:168
 TS3HTTPRequest.cxx:169
 TS3HTTPRequest.cxx:170
 TS3HTTPRequest.cxx:171
 TS3HTTPRequest.cxx:172
 TS3HTTPRequest.cxx:173
 TS3HTTPRequest.cxx:174
 TS3HTTPRequest.cxx:175
 TS3HTTPRequest.cxx:176
 TS3HTTPRequest.cxx:177
 TS3HTTPRequest.cxx:178
 TS3HTTPRequest.cxx:179
 TS3HTTPRequest.cxx:180
 TS3HTTPRequest.cxx:181
 TS3HTTPRequest.cxx:182
 TS3HTTPRequest.cxx:183
 TS3HTTPRequest.cxx:184
 TS3HTTPRequest.cxx:185
 TS3HTTPRequest.cxx:186
 TS3HTTPRequest.cxx:187
 TS3HTTPRequest.cxx:188
 TS3HTTPRequest.cxx:189
 TS3HTTPRequest.cxx:190
 TS3HTTPRequest.cxx:191
 TS3HTTPRequest.cxx:192
 TS3HTTPRequest.cxx:193
 TS3HTTPRequest.cxx:194
 TS3HTTPRequest.cxx:195
 TS3HTTPRequest.cxx:196
 TS3HTTPRequest.cxx:197
 TS3HTTPRequest.cxx:198
 TS3HTTPRequest.cxx:199
 TS3HTTPRequest.cxx:200
 TS3HTTPRequest.cxx:201
 TS3HTTPRequest.cxx:202
 TS3HTTPRequest.cxx:203
 TS3HTTPRequest.cxx:204
 TS3HTTPRequest.cxx:205
 TS3HTTPRequest.cxx:206
 TS3HTTPRequest.cxx:207
 TS3HTTPRequest.cxx:208
 TS3HTTPRequest.cxx:209
 TS3HTTPRequest.cxx:210
 TS3HTTPRequest.cxx:211
 TS3HTTPRequest.cxx:212
 TS3HTTPRequest.cxx:213
 TS3HTTPRequest.cxx:214
 TS3HTTPRequest.cxx:215
 TS3HTTPRequest.cxx:216
 TS3HTTPRequest.cxx:217
 TS3HTTPRequest.cxx:218
 TS3HTTPRequest.cxx:219
 TS3HTTPRequest.cxx:220
 TS3HTTPRequest.cxx:221
 TS3HTTPRequest.cxx:222
 TS3HTTPRequest.cxx:223
 TS3HTTPRequest.cxx:224
 TS3HTTPRequest.cxx:225
 TS3HTTPRequest.cxx:226
 TS3HTTPRequest.cxx:227
 TS3HTTPRequest.cxx:228
 TS3HTTPRequest.cxx:229
 TS3HTTPRequest.cxx:230
 TS3HTTPRequest.cxx:231
 TS3HTTPRequest.cxx:232
 TS3HTTPRequest.cxx:233
 TS3HTTPRequest.cxx:234
 TS3HTTPRequest.cxx:235
 TS3HTTPRequest.cxx:236
 TS3HTTPRequest.cxx:237
 TS3HTTPRequest.cxx:238
 TS3HTTPRequest.cxx:239
 TS3HTTPRequest.cxx:240
 TS3HTTPRequest.cxx:241
 TS3HTTPRequest.cxx:242
 TS3HTTPRequest.cxx:243
 TS3HTTPRequest.cxx:244
 TS3HTTPRequest.cxx:245