├── INFO ├── LICENSE ├── test.php └── webshare.php /INFO: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Webshare.cz", 3 | "hostprefix": "webshare.cz", 4 | "displayname": "Webshare.cz", 5 | "version": "22.04", 6 | "authentication": "yes", 7 | "module": "webshare.php", 8 | "class": "SynoFileHostingWebshare", 9 | "description": "Existing account required" 10 | } 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Radovan Kepák 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /test.php: -------------------------------------------------------------------------------- 1 | 'Failed', 43 | 1 => 'Passed - regular', 44 | 2 => 'Passed - vip' 45 | ]; 46 | $result = $client->Verify(); 47 | echo $resultMsg[$result] . PHP_EOL; 48 | 49 | # Link 50 | echo 'Testing link: '; 51 | $result = $client->GetDownloadInfo(); 52 | if (isset($result[DOWNLOAD_URL])) { 53 | echo 'Passed'; 54 | } else { 55 | echo 'Failed - ' . $result[DOWNLOAD_ERROR]; 56 | } 57 | 58 | echo PHP_EOL . PHP_EOL; 59 | 60 | # All good 61 | echo 'Done' . PHP_EOL; 62 | -------------------------------------------------------------------------------- /webshare.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | class SynoFileHostingWebshare 9 | { 10 | /** 11 | * Webshare API link 12 | */ 13 | const API_URL = 'https://webshare.cz/api'; 14 | 15 | /** 16 | * @var string 17 | */ 18 | protected $url; 19 | 20 | /** 21 | * @var string 22 | */ 23 | protected $username; 24 | 25 | /** 26 | * @var string 27 | */ 28 | protected $password; 29 | 30 | /** 31 | * @var string 32 | */ 33 | protected $salt; 34 | 35 | /** 36 | * @var string 37 | */ 38 | protected $token; 39 | 40 | /** 41 | * SynoFileHostingWebshare constructor. 42 | * 43 | * @param $url string 44 | * @param $username string 45 | * @param $password string 46 | */ 47 | public function __construct($url, $username, $password) 48 | { 49 | $this->url = $url; 50 | $this->username = $username; 51 | $this->password = $password; 52 | } 53 | 54 | /** 55 | * Try to get direct link for file 56 | * 57 | * @return array 58 | */ 59 | public function GetDownloadInfo() 60 | { 61 | try { 62 | if ($this->isDirectLink($this->url)) { 63 | $link = $this->url; 64 | } else { 65 | $ident = $this->getIdent($this->url); 66 | 67 | if (!$ident) { 68 | throw new \Exception('Identifier not found', ERR_NOT_SUPPORT_TYPE); 69 | } 70 | 71 | if (!$link = $this->getDirectLink($ident)) { 72 | throw new \Exception('Link not found', ERR_FILE_NO_EXIST); 73 | } 74 | } 75 | 76 | return [DOWNLOAD_URL => $link]; 77 | } catch (\Exception $e) { 78 | return [DOWNLOAD_ERROR => $e->getCode()]; 79 | } 80 | } 81 | 82 | /** 83 | * Get identifier from link 84 | * 85 | * @param string $url 86 | * @return string|null 87 | */ 88 | protected function getIdent($url) 89 | { 90 | if ( 91 | @preg_match('~^https?://(?:beta\.)?webshare\.cz(?:/|#|/#|#/|/#/)file/(?P\w+)(?:/.*)?$~i', trim($url), $matches) 92 | && isset($matches['ident']) 93 | ) { 94 | return $matches['ident']; 95 | } 96 | return null; 97 | } 98 | 99 | /** 100 | * Is direct link inserted? 101 | * 102 | * @param string $url 103 | * @return string|null 104 | */ 105 | protected function isDirectLink($url) 106 | { 107 | if (@preg_match('~^https?://(vip\.)?\d+\.dl\.webshare\.cz/.*$~i', trim($url))) { 108 | return $url; 109 | } 110 | return null; 111 | } 112 | 113 | /** 114 | * Get link for download 115 | * 116 | * @param string $ident 117 | * @return string|false 118 | * @throws \Exception 119 | */ 120 | protected function getDirectLink($ident) 121 | { 122 | if (!$this->getSalt()) { 123 | throw new \Exception('Salt can`t be loaded', LOGIN_FAIL); 124 | } 125 | 126 | if (!$token = $this->getToken()) { 127 | throw new \Exception('User can`t be logged!', LOGIN_FAIL); 128 | } 129 | 130 | $data = ['wst' => $token, 'ident' => $ident]; 131 | $response = $this->makeRequest('file_link', $data); 132 | return $response ? $this->getXmlParam($response, 'link') : false; 133 | } 134 | 135 | /** 136 | * Get salt 137 | * 138 | * @return string|false 139 | */ 140 | protected function getSalt() 141 | { 142 | if ($this->salt === null) { 143 | $this->salt = false; 144 | $response = $this->makeRequest('salt', [ 145 | 'username_or_email' => $this->username, 146 | ]); 147 | 148 | if ($response) { 149 | $this->salt = $this->getXmlParam($response, 'salt'); 150 | } 151 | } 152 | 153 | return $this->salt; 154 | } 155 | 156 | /** 157 | * @param string $action 158 | * @param array $data 159 | * @return bool|string 160 | */ 161 | protected function makeRequest($action, array $data) 162 | { 163 | $headers = ['Accept' => 'application/json']; 164 | $url = self::API_URL . "/{$action}/"; 165 | $response = $this->request($url, $headers, $data); 166 | $status = $this->getXmlParam($response, 'status'); 167 | return $status === 'OK' ? $response : false; 168 | } 169 | 170 | /** 171 | * @param string $url 172 | * @param array $headers 173 | * @param array $data 174 | * @return string 175 | */ 176 | protected function request($url, array $headers = [], array $data = []) 177 | { 178 | $curl = @curl_init(); 179 | 180 | //@curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); 181 | @curl_setopt($curl, CURLOPT_POST, true); 182 | @curl_setopt($curl, CURLOPT_HTTPHEADER, $headers); 183 | @curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($data)); 184 | @curl_setopt($curl, CURLOPT_USERAGENT, DOWNLOAD_STATION_USER_AGENT); 185 | @curl_setopt($curl, CURLOPT_COOKIEFILE, '/tmp/webshare.cookies.l'); 186 | @curl_setopt($curl, CURLOPT_TIMEOUT, 15); 187 | @curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); 188 | @curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); 189 | @curl_setopt($curl, CURLOPT_URL, $url); 190 | 191 | $response = @curl_exec($curl); 192 | @curl_close($curl); 193 | 194 | return $response; 195 | } 196 | 197 | /** 198 | * Get param from XML response 199 | * 200 | * @param $xml string 201 | * @param $key string 202 | * @return bool|string 203 | */ 204 | protected function getXmlParamFallback($xml, $key) 205 | { 206 | $element = @preg_quote($key, '~'); 207 | if (@preg_match('~<(' . $element . ')>(?P.*?)~i', $xml, $matches)) { 208 | return (string)$matches['value']; 209 | } 210 | return false; 211 | } 212 | 213 | /** 214 | * Get param from XML response 215 | * 216 | * @param $xml string 217 | * @param $key string 218 | * @return bool|string 219 | */ 220 | protected function getXmlParam($xml, $key) 221 | { 222 | if (!class_exists('SimpleXMLElement')) { 223 | return $this->getXmlParamFallback($xml, $key); 224 | } 225 | try { 226 | $data = new \SimpleXMLElement($xml); 227 | return isset($data->{$key}) ? (string) $data->{$key} : false; 228 | } catch (\Exception $e) { 229 | return false; 230 | } 231 | } 232 | 233 | /** 234 | * Get token 235 | * 236 | * @param string salt 237 | * @return null|string 238 | */ 239 | protected function getToken() 240 | { 241 | if ($this->token === null) { 242 | $this->token = false; 243 | if (!$salt = $this->getSalt()) { 244 | return false; 245 | } 246 | 247 | $response = $this->makeRequest('login', [ 248 | 'username_or_email' => $this->username, 249 | 'password' => sha1(crypt($this->password, '$1$' . $salt . '$')), 250 | 'digest' => md5($this->username . ':Webshare:' . $this->password), 251 | ]); 252 | 253 | if ($response) { 254 | $this->token = $this->getXmlParam($response, 'token'); 255 | } 256 | } 257 | 258 | return $this->token; 259 | } 260 | 261 | /** 262 | * Verify account 263 | * 264 | * @return int 265 | */ 266 | public function Verify() 267 | { 268 | if (!$this->getSalt()) { 269 | return LOGIN_FAIL; 270 | } 271 | 272 | if (!$token = $this->getToken()) { 273 | return LOGIN_FAIL; 274 | } 275 | 276 | $response = $this->makeRequest('user_data', ['wst' => $token]); 277 | if ((int)$this->getXmlParam($response, 'vip') === 1) { 278 | return USER_IS_PREMIUM; 279 | } 280 | return USER_IS_FREE; 281 | } 282 | } 283 | --------------------------------------------------------------------------------