Commit | Line | Data |
---|---|---|
e934d809 MS |
1 | <?php |
2 | namespace wcf\action; | |
3 | use wcf\system\exception\IllegalLinkException; | |
4 | use wcf\system\exception\SystemException; | |
c5b74093 | 5 | use wcf\system\WCF; |
1861a13b | 6 | use wcf\util\exception\CryptoException; |
52621aa3 | 7 | use wcf\util\exception\HTTPException; |
1861a13b | 8 | use wcf\util\CryptoUtil; |
e934d809 | 9 | use wcf\util\FileUtil; |
c5b74093 | 10 | use wcf\util\HeaderUtil; |
e934d809 MS |
11 | use wcf\util\HTTPRequest; |
12 | use wcf\util\StringUtil; | |
13 | ||
14 | /** | |
15 | * Proxies requests for embedded images. | |
16 | * | |
17 | * @author Matthias Schmidt | |
c839bd49 | 18 | * @copyright 2001-2018 WoltLab GmbH |
e934d809 | 19 | * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php> |
e71525e4 MW |
20 | * @package WoltLabSuite\Core\Action |
21 | * @since 3.0 | |
e934d809 MS |
22 | */ |
23 | class ImageProxyAction extends AbstractAction { | |
7dc58174 TD |
24 | /** |
25 | * @inheritDoc | |
26 | */ | |
27 | public $neededModules = ['MODULE_IMAGE_PROXY']; | |
28 | ||
e934d809 | 29 | /** |
1861a13b | 30 | * The image key created by CryptoUtil::createSignedString() |
e934d809 MS |
31 | * @var string |
32 | */ | |
1861a13b | 33 | public $key = ''; |
e934d809 MS |
34 | |
35 | /** | |
0fcfe5f6 | 36 | * @inheritDoc |
e934d809 MS |
37 | */ |
38 | public function readParameters() { | |
39 | parent::readParameters(); | |
40 | ||
1861a13b | 41 | if (isset($_REQUEST['key'])) $this->key = StringUtil::trim($_REQUEST['key']); |
e934d809 MS |
42 | } |
43 | ||
44 | /** | |
0fcfe5f6 | 45 | * @inheritDoc |
e934d809 MS |
46 | */ |
47 | public function execute() { | |
48 | parent::execute(); | |
49 | ||
52621aa3 TD |
50 | if (isset($_SERVER['HTTP_VIA']) && strpos($_SERVER['HTTP_VIA'], 'wsc') !== false) throw new IllegalLinkException(); |
51 | ||
e934d809 | 52 | try { |
1861a13b TD |
53 | $url = CryptoUtil::getValueFromSignedString($this->key); |
54 | if ($url === null) throw new IllegalLinkException(); | |
55 | ||
56 | $fileName = sha1($this->key); | |
29d0faf2 | 57 | $dir = WCF_DIR.'images/proxy/'.substr($fileName, 0, 2); |
1861a13b | 58 | |
29d0faf2 TD |
59 | // ensure that the directory exists |
60 | if (!file_exists($dir)) { | |
4879676b | 61 | FileUtil::makePath($dir); |
e934d809 | 62 | } |
e934d809 | 63 | |
29d0faf2 | 64 | // check whether we already downloaded the image |
5fe7469d TD |
65 | $fileLocation = null; |
66 | foreach (['png','jpg','gif'] as $extension) { | |
67 | if (is_file($dir.'/'.$fileName.'.'.$extension)) { | |
68 | $fileLocation = $dir.'/'.$fileName.'.'.$extension; | |
69 | break; | |
70 | } | |
71 | } | |
29d0faf2 | 72 | |
5fe7469d | 73 | if ($fileLocation === null) { |
0b4b5902 | 74 | try { |
1856402e TD |
75 | // download image |
76 | try { | |
d08d11cd TD |
77 | $request = new HTTPRequest($url, [ |
78 | 'maxLength' => 10 * (1 << 20) // download at most 10 MiB | |
79 | ]); | |
52621aa3 | 80 | $request->addHeader('Via', '1.1 wsc'); |
8c7bfa6d | 81 | $request->addHeader('Accept', 'image/*'); |
1856402e TD |
82 | $request->execute(); |
83 | } | |
52621aa3 TD |
84 | catch (\Exception $e) { |
85 | $chain = $e; | |
86 | do { | |
87 | if ($chain instanceof HTTPException) { | |
88 | throw new \DomainException(); | |
89 | } | |
c00717d0 TD |
90 | // TODO: This is the Exception thrown by RemoteFile. |
91 | // Update if RemoteFile switches to a proper subclass in the future. | |
92 | if (strpos($chain->getMessage(), 'Can not connect to') === 0) { | |
93 | throw new \DomainException(); | |
94 | } | |
52621aa3 TD |
95 | } |
96 | while ($chain = $chain->getPrevious()); | |
97 | ||
98 | throw $e; | |
1856402e TD |
99 | } |
100 | $image = $request->getReply()['body']; | |
101 | ||
102 | // check file type | |
34e37928 | 103 | $imageData = @getimagesizefromstring($image); |
1856402e TD |
104 | if (!$imageData) throw new \DomainException(); |
105 | ||
106 | switch ($imageData[2]) { | |
107 | case IMAGETYPE_PNG: | |
108 | $extension = 'png'; | |
109 | break; | |
110 | case IMAGETYPE_GIF: | |
111 | $extension = 'gif'; | |
112 | break; | |
113 | case IMAGETYPE_JPEG: | |
114 | $extension = 'jpg'; | |
115 | break; | |
116 | default: | |
117 | throw new \DomainException(); | |
118 | } | |
119 | } | |
120 | catch (\DomainException $e) { | |
121 | // save a dummy image in case the server sent us junk, otherwise we might try to download the file over and over and over again. | |
122 | // taken from the public domain gif at https://commons.wikimedia.org/wiki/File%3aBlank.gif | |
123 | $image = "\x47\x49\x46\x38\x39\x61\x01\x00\x01\x00\x80\x00\x00\xFF\xFF\xFF\x00\x00\x00\x21\xF9\x04\x00\x00\x00\x00\x00\x2C\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02\x02\x44\x01\x00\x3B"; | |
124 | $extension = 'gif'; | |
e7566924 TD |
125 | } |
126 | ||
29d0faf2 TD |
127 | $fileLocation = $dir.'/'.$fileName.'.'.$extension; |
128 | ||
e7566924 TD |
129 | file_put_contents($fileLocation, $image); |
130 | ||
131 | // update mtime for correct expiration calculation | |
132 | @touch($fileLocation); | |
133 | } | |
5fe7469d | 134 | |
29d0faf2 TD |
135 | $path = FileUtil::getRelativePath(WCF_DIR, dirname($fileLocation)).basename($fileLocation); |
136 | ||
e934d809 MS |
137 | $this->executed(); |
138 | ||
1a01691b | 139 | HeaderUtil::redirect(WCF::getPath().$path, true, false); |
e934d809 MS |
140 | exit; |
141 | } | |
142 | catch (SystemException $e) { | |
0b4b5902 | 143 | \wcf\functions\exception\logThrowable($e); |
e934d809 MS |
144 | throw new IllegalLinkException(); |
145 | } | |
1861a13b | 146 | catch (CryptoException $e) { |
0b4b5902 | 147 | \wcf\functions\exception\logThrowable($e); |
1861a13b TD |
148 | throw new IllegalLinkException(); |
149 | } | |
e934d809 MS |
150 | } |
151 | } |