1<?php
2
3/**
4 * DokuWiki Plugin imgpaste (Action Component)
5 *
6 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
7 * @author  Andreas Gohr <gohr@cosmocode.de>
8 */
9class action_plugin_imgpaste extends DokuWiki_Action_Plugin
10{
11
12    protected $tempdir = '';
13    protected $tempfile = '';
14
15    /**
16     * Clean up on destruction
17     */
18    public function __destruct()
19    {
20        $this->clean();
21    }
22
23    /** @inheritdoc */
24    public function register(Doku_Event_Handler $controller)
25    {
26        $controller->register_hook('AJAX_CALL_UNKNOWN', 'BEFORE', $this, 'handleAjaxUpload');
27    }
28
29    /**
30     * Creates a new file from the given data URL
31     *
32     * @param Doku_Event $event AJAX_CALL_UNKNOWN
33     */
34    public function handleAjaxUpload(Doku_Event $event)
35    {
36        if ($event->data != 'plugin_imgpaste') return;
37        global $lang;
38
39        // get data
40        global $INPUT;
41        $data = $INPUT->post->str('data');
42        list($type, $data) = explode(';', $data);
43        if (!$data) $this->fail(400, $this->getLang('e_nodata'));
44
45        // process data encoding
46        $type = strtolower(substr($type, 5)); // strip 'data:' prefix
47        $data = substr($data, 7); // strip 'base64,' prefix
48        $data = base64_decode($data);
49
50        // check for supported mime type
51        $mimetypes = array_flip(getMimeTypes());
52        if (!isset($mimetypes[$type])) $this->fail(415, $lang['uploadwrong']);
53
54        // prepare file names
55        $tempname = $this->storetemp($data);
56        $filename = $this->createFileName($INPUT->post->str('id'), $mimetypes[$type], $_SERVER['REMOTE_USER']);
57
58        // check ACLs
59        $auth = auth_quickaclcheck($filename);
60        if ($auth < AUTH_UPLOAD) $this->fail(403, $lang['uploadfail']);
61
62        // do the actual saving
63        $result = media_save(
64            [
65                'name' => $tempname,
66                'mime' => $type,
67                'ext' => $mimetypes[$type],
68            ],
69            $filename,
70            false,
71            $auth,
72            'copy'
73        );
74        if (is_array($result)) $this->fail(500, $result[0]);
75
76        //Still here? We had a successful upload
77        $this->clean();
78        header('Content-Type: application/json');
79        echo json_encode([
80            'message' => $lang['uploadsucc'],
81            'id' => $result,
82            'mime' => $type,
83            'ext' => $mimetypes[$type],
84            'url' => ml($result),
85        ]);
86
87        $event->preventDefault();
88        $event->stopPropagation();
89    }
90
91    /**
92     * Create the filename for the new file
93     *
94     * @param string $pageid the original page the paste event happend on
95     * @param string $ext the extension of the file
96     * @param string $user the currently logged in user
97     * @return string
98     */
99    protected function createFileName($pageid, $ext, $user)
100    {
101        $filename = $this->getConf('filename');
102        $filename = str_replace(
103            [
104                '@NS@',
105                '@ID@',
106                '@USER@',
107                '@PAGE@',
108            ],
109            [
110                getNS($pageid),
111                $pageid,
112                $user,
113                noNS($pageid),
114            ],
115            $filename
116        );
117        $filename = strftime($filename);
118        $filename .= '.' . $ext;
119        return cleanID($filename);
120    }
121
122    /**
123     * Create a temporary file from the given data
124     *
125     * exits if an error occurs
126     *
127     * @param $data
128     * @return string
129     */
130    protected function storetemp($data)
131    {
132        // store in temporary file
133        $this->tempdir = io_mktmpdir();
134        if (!$this->tempdir) $this->fail(500);
135        $this->tempfile = $this->tempdir . '/' . md5($data);
136        if (!io_saveFile($this->tempfile, $data)) $this->fail(500);
137        return $this->tempfile;
138    }
139
140    /**
141     * remove temporary file and directory
142     */
143    protected function clean()
144    {
145        if ($this->tempfile && file_exists($this->tempfile)) @unlink($this->tempfile);
146        if ($this->tempdir && is_dir($this->tempdir)) @rmdir($this->tempdir);
147        $this->tempfile = '';
148        $this->tempdir = '';
149    }
150
151    /**
152     * End the execution with a HTTP error code
153     *
154     * Calls clean
155     *
156     * @param int $status HTTP status code
157     * @param string $text
158     */
159    protected function fail($status, $text = '')
160    {
161        $this->clean();
162        http_status($status, $text);
163        exit;
164    }
165
166}
167