PageRenderTime 42ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/classes/Facade/S3/Request.php

https://github.com/lox/facade
PHP | 192 lines | 116 code | 27 blank | 49 comment | 10 complexity | a9d205daffa9841bbf7d83c60531af8d MD5 | raw file
Possible License(s): MIT
  1. <?php
  2. /**
  3. * A request sent to Amazon's S3 service
  4. */
  5. class Facade_S3_Request implements Facade_Request
  6. {
  7. private $_headers;
  8. private $_stream;
  9. private $_accesskey;
  10. private $_method;
  11. private $_secret;
  12. private $_socket;
  13. private $_path;
  14. /**
  15. * Constructor
  16. */
  17. public function __construct($socket, $accesskey, $secret, $method, $path)
  18. {
  19. $this->_headers = new Facade_HeaderCollection();
  20. $this->_accesskey = $accesskey;
  21. $this->_secret = $secret;
  22. $this->_socket = $socket;
  23. $this->_method = $method;
  24. $this->_path = $path;
  25. }
  26. /**
  27. *
  28. */
  29. public function setContentType($mimetype)
  30. {
  31. return $this->setHeader('Content-Type: '.$mimetype);
  32. }
  33. /**
  34. *
  35. */
  36. public function setAcl($acl)
  37. {
  38. return $this->setHeader('x-amz-acl: '.$acl);
  39. }
  40. /**
  41. *
  42. */
  43. public function setDate($timestamp)
  44. {
  45. return $this->setHeader('Date: '. gmdate('D, d M Y H:i:s T', $timestamp));
  46. }
  47. /* (non-phpdoc)
  48. * @see Facade_Request::setStream()
  49. */
  50. public function setStream($stream)
  51. {
  52. $this->_stream = $stream;
  53. return $this;
  54. }
  55. /* (non-phpdoc)
  56. * @see Facade_Request::setStream()
  57. */
  58. public function setHeader($header)
  59. {
  60. $this->_headers->set($header);
  61. return $this;
  62. }
  63. /* (non-phpdoc)
  64. * @see Facade_Request::setStream()
  65. */
  66. public function getHeaders()
  67. {
  68. return $this->_headers;
  69. }
  70. /* (non-phpdoc)
  71. * @see Facade_Request::reset()
  72. */
  73. public function reset()
  74. {
  75. $this->_socket->connect();
  76. return $this;
  77. }
  78. /**
  79. * Sends the request
  80. */
  81. public function send()
  82. {
  83. $headers = $this->getHeaders();
  84. // clobber the date
  85. $this->setDate(time());
  86. // set default headers
  87. if(!$headers->contains('Host')) $this->setHeader('Host: '.Facade_S3::S3_HOST);
  88. if(!$headers->contains('x-amz-acl')) $this->setAcl('private');
  89. // if there is a stream, add a content length
  90. if(!is_null($this->_stream) && $this->_stream->getLength())
  91. $this->setHeader('Content-Length: '.$this->_stream->getLength());
  92. // add the amazon signature
  93. $this->setHeader(sprintf(
  94. 'Authorization: AWS %s:%s', $this->_accesskey, $this->signature()));
  95. // write the pre-amble
  96. $this->_socket->writeRequest(
  97. $this->_method,
  98. $this->_path,
  99. $this->getHeaders()
  100. );
  101. // most requests have a content stream
  102. if($headers->contains('Content-Length') && $headers->value('Content-Length'))
  103. {
  104. $bytes = $this->_socket->copy($this->_stream);
  105. // check we wrote enough data
  106. if($bytes < $headers->value('Content-Length'))
  107. {
  108. throw new Facade_StreamException(
  109. "Content stream was shorter than the Content-Length"
  110. );
  111. }
  112. }
  113. // we are done writing
  114. $this->_socket->setWritable(false);
  115. // build a response
  116. return new Facade_S3_Response($this->_socket, $this->_path);
  117. }
  118. // ---------------------------------------------------------
  119. // signature helper methods
  120. private function signature()
  121. {
  122. $headers = $this->getHeaders();
  123. $date = $headers->value('Date');
  124. $md5 = $headers->value('Content-MD5');
  125. $type = $headers->value('Content-Type');
  126. // canonicalize the amazon headers
  127. $amazonHeaders = $headers->filter('/^x-amz/i')->sort();
  128. $canonicalized = '';
  129. foreach ($amazonHeaders as $header)
  130. $canonicalized .= strtolower($header->getName()).':'.$header->getValue()."\n";
  131. // build the string to sign
  132. $plaintext = sprintf("%s\n%s\n%s\n%s\n%s%s",
  133. $this->_method,
  134. $md5,
  135. $type,
  136. $date,
  137. $canonicalized,
  138. $this->_path
  139. );
  140. return $this->base64($this->hmacsha1( $this->_secret, $plaintext));
  141. }
  142. /**
  143. * @see http://pear.php.net/package/Crypt_HMAC/
  144. */
  145. private function hmacsha1($key, $data)
  146. {
  147. if (strlen($key) > 64)
  148. $key = pack("H40", sha1($key));
  149. if (strlen($key) < 64)
  150. $key = str_pad($key, 64, chr(0));
  151. $ipad = (substr($key, 0, 64) ^ str_repeat(chr(0x36), 64));
  152. $opad = (substr($key, 0, 64) ^ str_repeat(chr(0x5C), 64));
  153. return sha1($opad . pack("H40", sha1($ipad . $data)));
  154. }
  155. /**
  156. * Returns the base64 version of a string
  157. */
  158. private function base64($str)
  159. {
  160. $ret = "";
  161. for($i = 0; $i < strlen($str); $i += 2)
  162. $ret .= chr(hexdec(substr($str, $i, 2)));
  163. return base64_encode($ret);
  164. }
  165. }