1<?php 2 3use dokuwiki\Extension\ActionPlugin; 4use dokuwiki\Extension\Event; 5use dokuwiki\Extension\EventHandler; 6use dokuwiki\plugin\imgpaste\Exception as PasteException; 7 8/** 9 * DokuWiki Plugin imgpaste (Action Component) 10 * 11 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 12 * @author Andreas Gohr <gohr@cosmocode.de> 13 */ 14class action_plugin_imgpaste extends ActionPlugin 15{ 16 17 protected $tempdir = ''; 18 protected $tempfile = ''; 19 20 /** 21 * Clean up on destruction 22 */ 23 public function __destruct() 24 { 25 $this->clean(); 26 } 27 28 /** @inheritdoc */ 29 public function register(EventHandler $controller) 30 { 31 $controller->register_hook('AJAX_CALL_UNKNOWN', 'BEFORE', $this, 'handleAjaxUpload'); 32 } 33 34 35 /** 36 * Creates a new file from the given data URL 37 * 38 * @param Event $event AJAX_CALL_UNKNOWN 39 */ 40 public function handleAjaxUpload(Event $event) 41 { 42 if ($event->data != 'plugin_imgpaste') return; 43 $event->preventDefault(); 44 $event->stopPropagation(); 45 46 global $INPUT; 47 try { 48 if ($INPUT->has('url')) { 49 [$data, $type] = $this->externalUrlToData($INPUT->post->str('url')); 50 } else { 51 [$data, $type] = $this->dataUrlToData($INPUT->post->str('data')); 52 } 53 $result = $this->storeImage($data, $type); 54 } catch (PasteException $e) { 55 $this->clean(); 56 http_status($e->getCode(), $e->getMessage()); 57 exit; 58 } 59 60 header('Content-Type: application/json'); 61 echo json_encode($result); 62 } 63 64 /** 65 * Get the binary data and mime type from a data URL 66 * 67 * @param string $dataUrl 68 * @return array [data, type] 69 * @throws PasteException 70 */ 71 protected function dataUrlToData($dataUrl) 72 { 73 list($type, $data) = explode(';', $dataUrl); 74 if (!$data) throw new PasteException($this->getLang('e_nodata'), 400); 75 76 // process data encoding 77 $type = strtolower(substr($type, 5)); // strip 'data:' prefix 78 $data = substr($data, 7); // strip 'base64,' prefix 79 $data = base64_decode($data); 80 return [$data, $type]; 81 } 82 83 /** 84 * Download the file from an external URL 85 * 86 * @param string $externalUrl 87 * @return array [data, type] 88 * @throws PasteException 89 */ 90 protected function externalUrlToData($externalUrl) 91 { 92 global $lang; 93 94 // download the file 95 $http = new \dokuwiki\HTTP\DokuHTTPClient(); 96 $data = $http->get($externalUrl); 97 if (!$data) throw new PasteException($lang['uploadfail'], 500); 98 [$type] = explode(';', $http->resp_headers['content-type']); 99 return [$data, $type]; 100 } 101 102 /** 103 * @throws PasteException 104 */ 105 protected function storeImage($data, $type) 106 { 107 global $lang; 108 global $INPUT; 109 110 // check for supported mime type 111 $mimetypes = array_flip(getMimeTypes()); 112 if (!isset($mimetypes[$type])) throw new PasteException($lang['uploadwrong'], 415); 113 114 // prepare file names 115 $tempname = $this->storetemp($data); 116 $filename = $this->createFileName($INPUT->post->str('id'), $mimetypes[$type], $_SERVER['REMOTE_USER']); 117 118 // check ACLs 119 $auth = auth_quickaclcheck($filename); 120 if ($auth < AUTH_UPLOAD) throw new PasteException($lang['uploadfail'], 403); 121 122 // do the actual saving 123 $result = media_save( 124 [ 125 'name' => $tempname, 126 'mime' => $type, 127 'ext' => $mimetypes[$type], 128 ], 129 $filename, 130 false, 131 $auth, 132 'copy' 133 ); 134 if (is_array($result)) throw new PasteException($result[0], 500); 135 136 //Still here? We had a successful upload 137 $this->clean(); 138 return [ 139 'message' => $lang['uploadsucc'], 140 'id' => $result, 141 'mime' => $type, 142 'ext' => $mimetypes[$type], 143 'url' => ml($result), 144 ]; 145 } 146 147 /** 148 * Create the filename for the new file 149 * 150 * @param string $pageid the original page the paste event happend on 151 * @param string $ext the extension of the file 152 * @param string $user the currently logged in user 153 * @return string 154 */ 155 protected function createFileName($pageid, $ext, $user) 156 { 157 $unique = ''; 158 $filename = $this->getConf('filename'); 159 $filename = str_replace( 160 [ 161 '@NS@', 162 '@ID@', 163 '@USER@', 164 '@PAGE@', 165 ], 166 [ 167 getNS($pageid), 168 $pageid, 169 $user, 170 noNS($pageid), 171 ], 172 $filename 173 ); 174 $filename = strftime($filename); 175 $filename = cleanID($filename); 176 while (media_exists($filename . $unique . '.' . $ext)) { 177 $unique = (int)$unique + 1; 178 } 179 return $filename . $unique . '.' . $ext; 180 } 181 182 /** 183 * Create a temporary file from the given data 184 * 185 * exits if an error occurs 186 * 187 * @param $data 188 * @return string 189 */ 190 protected function storetemp($data) 191 { 192 // store in temporary file 193 $this->tempdir = io_mktmpdir(); 194 if (!$this->tempdir) throw new PasteException('', 500); 195 $this->tempfile = $this->tempdir . '/' . md5($data); 196 if (!io_saveFile($this->tempfile, $data)) throw new PasteException('', 500); 197 return $this->tempfile; 198 } 199 200 /** 201 * remove temporary file and directory 202 */ 203 protected function clean() 204 { 205 if ($this->tempfile && file_exists($this->tempfile)) @unlink($this->tempfile); 206 if ($this->tempdir && is_dir($this->tempdir)) @rmdir($this->tempdir); 207 $this->tempfile = ''; 208 $this->tempdir = ''; 209 } 210 211} 212