Просмотр файла engine/classes/DropboxUploader.php

Размер файла: 8.93Kb
  1. <?php
  2. /**
  3. * Dropbox Uploader
  4. *
  5. * Copyright (c) 2009 Jaka Jancar
  6. *
  7. * Permission is hereby granted, free of charge, to any person obtaining a copy
  8. * of this software and associated documentation files (the "Software"), to deal
  9. * in the Software without restriction, including without limitation the rights
  10. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  11. * copies of the Software, and to permit persons to whom the Software is
  12. * furnished to do so, subject to the following conditions:
  13. *
  14. * The above copyright notice and this permission notice shall be included in
  15. * all copies or substantial portions of the Software.
  16. *
  17. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  20. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  21. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  22. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  23. * THE SOFTWARE.
  24. *
  25. * @author Jaka Jancar [jaka@kubje.org] [http://jaka.kubje.org/]
  26. * @version 1.1.12
  27. */
  28. class DropboxUploader {
  29. /**
  30. * Certificate Authority Certificate source types
  31. */
  32. const CACERT_SOURCE_SYSTEM = 0;
  33. const CACERT_SOURCE_FILE = 1;
  34. const CACERT_SOURCE_DIR = 2;
  35. /**
  36. * Dropbox configuration
  37. */
  38. const DROPBOX_UPLOAD_LIMIT_IN_BYTES = 314572800;
  39. const HTTPS_DROPBOX_COM_HOME = 'https://www.dropbox.com/home';
  40. const HTTPS_DROPBOX_COM_LOGIN = 'https://www.dropbox.com/login';
  41. const HTTPS_DROPBOX_COM_UPLOAD = 'https://dl-web.dropbox.com/upload';
  42. /**
  43. * DropboxUploader Error Flags and Codes
  44. */
  45. const FLAG_DROPBOX_GENERIC = 0x10000000;
  46. const FLAG_LOCAL_FILE_IO = 0x10010000;
  47. const CODE_FILE_READ_ERROR = 0x10010101;
  48. const CODE_TEMP_FILE_CREATE_ERROR = 0x10010102;
  49. const CODE_TEMP_FILE_WRITE_ERROR = 0x10010103;
  50. const FLAG_PARAMETER_INVALID = 0x10020000;
  51. const CODE_PARAMETER_TYPE_ERROR = 0x10020101;
  52. const CODE_FILESIZE_TOO_LARGE = 0x10020201;
  53. const FLAG_REMOTE = 0x10040000;
  54. const CODE_CURL_ERROR = 0x10040101;
  55. const CODE_LOGIN_ERROR = 0x10040201;
  56. const CODE_UPLOAD_ERROR = 0x10040401;
  57. const CODE_SCRAPING_FORM = 0x10040801;
  58. const CODE_SCRAPING_LOGIN = 0x10040802;
  59. const CODE_CURL_EXTENSION_MISSING = 0x10080101;
  60. protected $email;
  61. protected $password;
  62. protected $caCertSourceType = self::CACERT_SOURCE_SYSTEM;
  63. protected $caCertSource;
  64. protected $loggedIn = FALSE;
  65. protected $cookies = array();
  66.  
  67. /**
  68. * Constructor
  69. *
  70. * @param string $email
  71. * @param string $password
  72. * @throws Exception
  73. */
  74. public function __construct($email, $password) {
  75. // Check requirements
  76. if (!extension_loaded('curl'))
  77. throw new Exception('DropboxUploader requires the cURL extension.', self::CODE_CURL_EXTENSION_MISSING);
  78.  
  79. if (empty($email) || empty($password)) {
  80. throw new Exception((empty($email) ? 'Email' : 'Password') . ' must not be empty.', self::CODE_PARAMETER_TYPE_ERROR);
  81. }
  82.  
  83. $this->email = $email;
  84. $this->password = $password;
  85. }
  86.  
  87. public function setCaCertificateDir($dir) {
  88. $this->caCertSourceType = self::CACERT_SOURCE_DIR;
  89. $this->caCertSource = $dir;
  90. }
  91.  
  92. public function setCaCertificateFile($file) {
  93. $this->caCertSourceType = self::CACERT_SOURCE_FILE;
  94. $this->caCertSource = $file;
  95. }
  96.  
  97. public function upload($source, $remoteDir = '/', $remoteName = NULL) {
  98. if (!is_file($source) or !is_readable($source))
  99. throw new Exception("File '$source' does not exist or is not readable.", self::CODE_FILE_READ_ERROR);
  100.  
  101. $filesize = filesize($source);
  102. if ($filesize < 0 or $filesize > self::DROPBOX_UPLOAD_LIMIT_IN_BYTES) {
  103. throw new Exception("File '$source' too large ($filesize bytes).", self::CODE_FILESIZE_TOO_LARGE);
  104. }
  105.  
  106. if (!is_string($remoteDir))
  107. throw new Exception("Remote directory must be a string, is " . gettype($remoteDir) . " instead.", self::CODE_PARAMETER_TYPE_ERROR);
  108.  
  109. if (is_null($remoteName)) {
  110. # intentionally left blank
  111. } else if (!is_string($remoteName)) {
  112. throw new Exception("Remote filename must be a string, is " . gettype($remoteDir) . " instead.", self::CODE_PARAMETER_TYPE_ERROR);
  113. } else {
  114. $source .= ';filename=' . $remoteName;
  115. }
  116.  
  117. if (!$this->loggedIn)
  118. $this->login();
  119.  
  120. $data = $this->request(self::HTTPS_DROPBOX_COM_HOME);
  121. $token = $this->extractToken($data, self::HTTPS_DROPBOX_COM_UPLOAD);
  122.  
  123. $postData = array(
  124. 'plain' => 'yes',
  125. 'file' => '@' . $source,
  126. 'dest' => $remoteDir,
  127. 't' => $token
  128. );
  129. $data = $this->request(self::HTTPS_DROPBOX_COM_UPLOAD, $postData);
  130. if (strpos($data, 'HTTP/1.1 302 FOUND') === FALSE)
  131. throw new Exception('Upload failed!', self::CODE_UPLOAD_ERROR);
  132. }
  133.  
  134. public function uploadString($string, $remoteName, $remoteDir = '/') {
  135. $exception = NULL;
  136.  
  137. $file = tempnam(sys_get_temp_dir(), 'DBUploadString');
  138. if (!is_file($file))
  139. throw new Exception("Can not create temporary file.", self::CODE_TEMP_FILE_CREATE_ERROR);
  140.  
  141. $bytes = file_put_contents($file, $string);
  142. if ($bytes === FALSE) {
  143. unlink($file);
  144. throw new Exception("Can not write to temporary file '$file'.", self::CODE_TEMP_FILE_WRITE_ERROR);
  145. }
  146.  
  147. try {
  148. $this->upload($file, $remoteDir, $remoteName);
  149. } catch (Exception $exception) {
  150. # intentionally left blank
  151. }
  152.  
  153. unlink($file);
  154.  
  155. if ($exception)
  156. throw $exception;
  157. }
  158.  
  159. protected function login() {
  160. $data = $this->request(self::HTTPS_DROPBOX_COM_LOGIN);
  161. $token = $this->extractTokenFromLoginForm($data);
  162.  
  163. $postData = array(
  164. 'login_email' => (string) $this->email,
  165. 'login_password' => (string) $this->password,
  166. 't' => $token
  167. );
  168. $data = $this->request(self::HTTPS_DROPBOX_COM_LOGIN, http_build_query($postData));
  169.  
  170. if (stripos($data, 'location: /home') === FALSE)
  171. throw new Exception('Login unsuccessful.', self::CODE_LOGIN_ERROR);
  172.  
  173. $this->loggedIn = TRUE;
  174. }
  175.  
  176. protected function request($url, $postData = NULL) {
  177. $ch = curl_init();
  178. curl_setopt($ch, CURLOPT_URL, (string) $url);
  179. curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
  180. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, TRUE);
  181. switch ($this->caCertSourceType) {
  182. case self::CACERT_SOURCE_FILE:
  183. curl_setopt($ch, CURLOPT_CAINFO, (string) $this->caCertSource);
  184. break;
  185. case self::CACERT_SOURCE_DIR:
  186. curl_setopt($ch, CURLOPT_CAPATH, (string) $this->caCertSource);
  187. break;
  188. }
  189. curl_setopt($ch, CURLOPT_HEADER, TRUE);
  190. curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
  191. if (NULL !== $postData) {
  192. curl_setopt($ch, CURLOPT_POST, TRUE);
  193. curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
  194. }
  195.  
  196. // Send cookies
  197. $rawCookies = array();
  198. foreach ($this->cookies as $k => $v)
  199. $rawCookies[] = "$k=$v";
  200. $rawCookies = implode(';', $rawCookies);
  201. curl_setopt($ch, CURLOPT_COOKIE, $rawCookies);
  202.  
  203. $data = curl_exec($ch);
  204. $error = sprintf('Curl error: (#%d) %s', curl_errno($ch), curl_error($ch));
  205. curl_close($ch);
  206.  
  207. if ($data === FALSE) {
  208. throw new Exception($error, self::CODE_CURL_ERROR);
  209. }
  210.  
  211. // Store received cookies
  212. preg_match_all('/Set-Cookie: ([^=]+)=(.*?);/i', $data, $matches, PREG_SET_ORDER);
  213. foreach ($matches as $match)
  214. $this->cookies[$match[1]] = $match[2];
  215.  
  216. return $data;
  217. }
  218.  
  219. protected function extractToken($html, $formAction) {
  220. $quot = preg_quote($formAction, '/');
  221. $pattern = '/<form [^>]*' . $quot . '[^>]*>.*?(?:<input [^>]*name="t" [^>]*value="(.*?)"[^>]*>).*?<\/form>/is';
  222. if (!preg_match($pattern, $html, $matches))
  223. throw new Exception("Cannot extract token! (form action is '$formAction')", self::CODE_SCRAPING_FORM);
  224. return $matches[1];
  225. }
  226.  
  227. protected function extractTokenFromLoginForm($html) {
  228. // <input type="hidden" name="t" value="UJygzfv9DLLCS-is7cLwgG7z" />
  229. if (!preg_match('#<input type="hidden" name="t" value="([A-Za-z0-9_-]+)" />#', $html, $matches))
  230. throw new Exception('Cannot extract login CSRF token.', self::CODE_SCRAPING_LOGIN);
  231. return $matches[1];
  232. }
  233.  
  234. }