mirror of
https://gh.llkk.cc/https://github.com/mainwp/mainwp-remote-backups-extension.git
synced 2026-02-23 13:27:28 +08:00
262 lines
No EOL
9.8 KiB
PHP
262 lines
No EOL
9.8 KiB
PHP
<?php
|
|
/**
|
|
* Dropbox Uploader
|
|
*
|
|
* Copyright (c) 2009 Jaka Jancar
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
*
|
|
* @author Jaka Jancar [jaka@kubje.org] [http://jaka.kubje.org/]
|
|
* @version 1.1.12
|
|
*/
|
|
class DropboxUploader {
|
|
/**
|
|
* Certificate Authority Certificate source types
|
|
*/
|
|
const CACERT_SOURCE_SYSTEM = 0;
|
|
const CACERT_SOURCE_FILE = 1;
|
|
const CACERT_SOURCE_DIR = 2;
|
|
/**
|
|
* Dropbox configuration
|
|
*/
|
|
const DROPBOX_UPLOAD_LIMIT_IN_BYTES = 314572800;
|
|
const HTTPS_DROPBOX_COM_HOME = 'https://www.dropbox.com/home';
|
|
const HTTPS_DROPBOX_COM_LOGIN = 'https://www.dropbox.com/login';
|
|
const HTTPS_DROPBOX_COM_UPLOAD = 'https://dl-web.dropbox.com/upload';
|
|
/**
|
|
* DropboxUploader Error Flags and Codes
|
|
*/
|
|
const FLAG_DROPBOX_GENERIC = 0x10000000;
|
|
const FLAG_LOCAL_FILE_IO = 0x10010000;
|
|
const CODE_FILE_READ_ERROR = 0x10010101;
|
|
const CODE_TEMP_FILE_CREATE_ERROR = 0x10010102;
|
|
const CODE_TEMP_FILE_WRITE_ERROR = 0x10010103;
|
|
const FLAG_PARAMETER_INVALID = 0x10020000;
|
|
const CODE_PARAMETER_TYPE_ERROR = 0x10020101;
|
|
const CODE_FILESIZE_TOO_LARGE = 0x10020201;
|
|
const FLAG_REMOTE = 0x10040000;
|
|
const CODE_CURL_ERROR = 0x10040101;
|
|
const CODE_LOGIN_ERROR = 0x10040201;
|
|
const CODE_UPLOAD_ERROR = 0x10040401;
|
|
const CODE_SCRAPING_FORM = 0x10040801;
|
|
const CODE_SCRAPING_LOGIN = 0x10040802;
|
|
const CODE_CURL_EXTENSION_MISSING = 0x10080101;
|
|
protected $email;
|
|
protected $password;
|
|
protected $caCertSourceType = self::CACERT_SOURCE_SYSTEM;
|
|
protected $caCertSource;
|
|
protected $loggedIn = FALSE;
|
|
protected $cookies = array();
|
|
|
|
/**
|
|
* Constructor
|
|
*
|
|
* @param string $email
|
|
* @param string $password
|
|
* @throws Exception
|
|
*/
|
|
public function __construct($email, $password) {
|
|
// Check requirements
|
|
if (!extension_loaded('curl'))
|
|
throw new Exception('DropboxUploader requires the cURL extension.', self::CODE_CURL_EXTENSION_MISSING);
|
|
|
|
if (empty($email) || empty($password)) {
|
|
throw new Exception((empty($email) ? 'Email' : 'Password') . ' must not be empty.', self::CODE_PARAMETER_TYPE_ERROR);
|
|
}
|
|
|
|
$this->email = $email;
|
|
$this->password = $password;
|
|
}
|
|
|
|
public function setCaCertificateDir($dir) {
|
|
$this->caCertSourceType = self::CACERT_SOURCE_DIR;
|
|
$this->caCertSource = $dir;
|
|
}
|
|
|
|
public function setCaCertificateFile($file) {
|
|
$this->caCertSourceType = self::CACERT_SOURCE_FILE;
|
|
$this->caCertSource = $file;
|
|
}
|
|
|
|
public function upload($source, $remoteDir = '/', $remoteName = NULL) {
|
|
if (!is_file($source) or !is_readable($source))
|
|
throw new Exception("File '$source' does not exist or is not readable.", self::CODE_FILE_READ_ERROR);
|
|
|
|
$filesize = filesize($source);
|
|
if ($filesize < 0 or $filesize > self::DROPBOX_UPLOAD_LIMIT_IN_BYTES) {
|
|
throw new Exception("File '$source' too large ($filesize bytes).", self::CODE_FILESIZE_TOO_LARGE);
|
|
}
|
|
|
|
if (!is_string($remoteDir))
|
|
throw new Exception("Remote directory must be a string, is " . gettype($remoteDir) . " instead.", self::CODE_PARAMETER_TYPE_ERROR);
|
|
|
|
if (is_null($remoteName)) {
|
|
# intentionally left blank
|
|
} else if (!is_string($remoteName)) {
|
|
throw new Exception("Remote filename must be a string, is " . gettype($remoteDir) . " instead.", self::CODE_PARAMETER_TYPE_ERROR);
|
|
} else {
|
|
$source .= ';filename=' . $remoteName;
|
|
}
|
|
|
|
if (!$this->loggedIn)
|
|
$this->login();
|
|
|
|
$data = $this->request(self::HTTPS_DROPBOX_COM_HOME);
|
|
$token = $this->extractToken($data, self::HTTPS_DROPBOX_COM_UPLOAD);
|
|
|
|
$postData = array(
|
|
'plain' => 'yes',
|
|
'file' => '@' . $source,
|
|
'dest' => $remoteDir,
|
|
't' => $token
|
|
);
|
|
$data = $this->request(self::HTTPS_DROPBOX_COM_UPLOAD, $postData);
|
|
if (strpos($data, 'HTTP/1.1 302 FOUND') === FALSE)
|
|
throw new Exception('Upload failed!', self::CODE_UPLOAD_ERROR);
|
|
}
|
|
|
|
public function delete($file) {
|
|
|
|
if (!is_string($file))
|
|
throw new Exception("Filename must be a string, is " . gettype($file) . " instead.", self::CODE_PARAMETER_TYPE_ERROR);
|
|
|
|
if (!$this->loggedIn)
|
|
$this->login();
|
|
|
|
$data = $this->request(self::HTTPS_DROPBOX_COM_HOME);
|
|
$token = $this->extractToken($data, self::HTTPS_DROPBOX_COM_UPLOAD);
|
|
|
|
$postData = array(
|
|
'plain' => 'yes',
|
|
'file' => '@' . $source,
|
|
'dest' => $remoteDir,
|
|
't' => $token
|
|
);
|
|
$data = $this->request(self::HTTPS_DROPBOX_COM_UPLOAD, $postData);
|
|
if (strpos($data, 'HTTP/1.1 302 FOUND') === FALSE)
|
|
throw new Exception('Upload failed!', self::CODE_UPLOAD_ERROR);
|
|
}
|
|
|
|
public function uploadString($string, $remoteName, $remoteDir = '/') {
|
|
$exception = NULL;
|
|
|
|
$file = tempnam(sys_get_temp_dir(), 'DBUploadString');
|
|
if (!is_file($file))
|
|
throw new Exception("Can not create temporary file.", self::CODE_TEMP_FILE_CREATE_ERROR);
|
|
|
|
$bytes = file_put_contents($file, $string);
|
|
if ($bytes === FALSE) {
|
|
unlink($file);
|
|
throw new Exception("Can not write to temporary file '$file'.", self::CODE_TEMP_FILE_WRITE_ERROR);
|
|
}
|
|
|
|
try {
|
|
$this->upload($file, $remoteDir, $remoteName);
|
|
} catch (Exception $exception) {
|
|
# intentionally left blank
|
|
}
|
|
|
|
unlink($file);
|
|
|
|
if ($exception)
|
|
throw $exception;
|
|
}
|
|
|
|
protected function login() {
|
|
$data = $this->request(self::HTTPS_DROPBOX_COM_LOGIN);
|
|
$token = $this->extractTokenFromLoginForm($data);
|
|
|
|
$postData = array(
|
|
'login_email' => (string) $this->email,
|
|
'login_password' => (string) $this->password,
|
|
't' => $token
|
|
);
|
|
$data = $this->request(self::HTTPS_DROPBOX_COM_LOGIN, http_build_query($postData));
|
|
|
|
if (stripos($data, 'location: /home') === FALSE)
|
|
throw new Exception('Login unsuccessful.', self::CODE_LOGIN_ERROR);
|
|
|
|
$this->loggedIn = TRUE;
|
|
}
|
|
|
|
public function testConnection()
|
|
{
|
|
$this->login();
|
|
return ($this->loggedIn === TRUE);
|
|
}
|
|
|
|
protected function request($url, $postData = NULL) {
|
|
$ch = curl_init();
|
|
curl_setopt($ch, CURLOPT_URL, (string) $url);
|
|
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
|
|
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, TRUE);
|
|
switch ($this->caCertSourceType) {
|
|
case self::CACERT_SOURCE_FILE:
|
|
curl_setopt($ch, CURLOPT_CAINFO, (string) $this->caCertSource);
|
|
break;
|
|
case self::CACERT_SOURCE_DIR:
|
|
curl_setopt($ch, CURLOPT_CAPATH, (string) $this->caCertSource);
|
|
break;
|
|
}
|
|
curl_setopt($ch, CURLOPT_HEADER, TRUE);
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
|
|
if (NULL !== $postData) {
|
|
curl_setopt($ch, CURLOPT_POST, TRUE);
|
|
curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
|
|
}
|
|
|
|
// Send cookies
|
|
$rawCookies = array();
|
|
foreach ($this->cookies as $k => $v)
|
|
$rawCookies[] = "$k=$v";
|
|
$rawCookies = implode(';', $rawCookies);
|
|
curl_setopt($ch, CURLOPT_COOKIE, $rawCookies);
|
|
|
|
$data = curl_exec($ch);
|
|
$error = sprintf('Curl error: (#%d) %s', curl_errno($ch), curl_error($ch));
|
|
curl_close($ch);
|
|
|
|
if ($data === FALSE) {
|
|
throw new Exception($error, self::CODE_CURL_ERROR);
|
|
}
|
|
|
|
// Store received cookies
|
|
preg_match_all('/Set-Cookie: ([^=]+)=(.*?);/i', $data, $matches, PREG_SET_ORDER);
|
|
foreach ($matches as $match)
|
|
$this->cookies[$match[1]] = $match[2];
|
|
|
|
return $data;
|
|
}
|
|
|
|
protected function extractToken($html, $formAction) {
|
|
$quot = preg_quote($formAction, '/');
|
|
$pattern = '/<form [^>]*' . $quot . '[^>]*>.*?(?:<input [^>]*name="t" [^>]*value="(.*?)"[^>]*>).*?<\/form>/is';
|
|
if (!preg_match($pattern, $html, $matches))
|
|
throw new Exception("Cannot extract token! (form action is '$formAction')", self::CODE_SCRAPING_FORM);
|
|
return $matches[1];
|
|
}
|
|
|
|
protected function extractTokenFromLoginForm($html) {
|
|
// <input type="hidden" name="t" value="UJygzfv9DLLCS-is7cLwgG7z" />
|
|
if (!preg_match('#<input type="hidden" name="t" value="([A-Za-z0-9_-]+)" />#', $html, $matches))
|
|
throw new Exception('Cannot extract login CSRF token.', self::CODE_SCRAPING_LOGIN);
|
|
return $matches[1];
|
|
}
|
|
|
|
} |