1<?php
2
3/**
4 * Inline PDF viewer plugin
5 *
6 * Render PDF files directly in the preview window
7 * by using the JavaScript PDF Reader pdf.js by andreasgal (http://mozilla.github.com/pdf.js)
8 *
9 * @version 0.1.2
10 * @author Thomas Bruederli <bruederli@kolabsys.com>
11 *
12 * Copyright (C) 2013, Kolab Systems AG
13 *
14 * This program is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Affero General Public License as
16 * published by the Free Software Foundation, either version 3 of the
17 * License, or (at your option) any later version.
18 *
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU Affero General Public License for more details.
23 *
24 * You should have received a copy of the GNU Affero General Public License
25 * along with this program. If not, see <http://www.gnu.org/licenses/>.
26 */
27class pdfviewer extends rcube_plugin
28{
29    public $task = 'mail|calendar|tasks|logout';
30
31    private $pdf_mimetypes = array(
32        'application/pdf',
33        'application/x-pdf',
34        'application/acrobat',
35        'applications/vnd.pdf',
36        'text/pdf',
37        'text/x-pdf',
38    );
39
40    /**
41     * Plugin initialization
42     */
43    function init()
44    {
45        // pdf.js only supports IE9 or higher
46        $ua = new rcube_browser;
47        if ($ua->ie && $ua->ver < 9)
48            return;
49
50        // extend list of mimetypes that should open in preview
51        $rcmail = rcube::get_instance();
52        if ($rcmail->action == 'preview' || $rcmail->action == 'show' || $rcmail->task == 'calendar' || $rcmail->task == 'tasks') {
53          $mimetypes = (array)$rcmail->config->get('client_mimetypes');
54          $rcmail->config->set('client_mimetypes', array_merge($mimetypes, $this->pdf_mimetypes));
55        }
56
57        // only use pdf.js if the browser doesn't support inline PDF rendering
58        if (empty($_SESSION['browser_caps']['pdf']) || $ua->opera)
59            $this->add_hook('message_part_get', array($this, 'get_part'));
60
61        $this->add_hook('message_part_structure', array($this, 'part_structure'));
62    }
63
64    /**
65     * Handler for message attachment download
66     */
67    public function get_part($args)
68    {
69        // redirect to viewer/viewer.html
70        if (!$args['download'] && $args['mimetype'] && empty($_GET['_load']) && in_array($args['mimetype'], $this->pdf_mimetypes)) {
71            $rcmail   = rcube::get_instance();
72            $file_url = $_SERVER['REQUEST_URI'] . '&_load=1';
73            $location = $rcmail->output->asset_url($this->urlbase . 'viewer/viewer.html');
74
75            header('Location: ' . $location . '?file=' . urlencode($file_url));
76            exit;
77        }
78
79        return $args;
80    }
81
82    /**
83     * Hook for MIME message parsing.
84     * This allows us to fix mimetypes of PDF attachments
85     */
86    public function part_structure($args)
87    {
88        if (!empty($args['structure']->parts)) {
89            foreach (array_keys($args['structure']->parts) as $i) {
90                $this->fix_mime_part($args['structure']->parts[$i], $args['object']);
91            }
92        }
93        else if ($args['structure']->mimetype != $args['mimetype']) {
94            $args['mimetype'] = $args['structure'];
95        }
96
97        return $args;
98    }
99
100    /**
101     * Helper method to fix potentially invalid mimetypes of PDF attachments
102     */
103    private function fix_mime_part($part, $message)
104    {
105        // Some versions of Outlook create garbage Content-Type:
106        // application/pdf.A520491B_3BF7_494D_8855_7FAC2C6C0608
107        if (preg_match('/^application\/pdf.+/', $part->mimetype)) {
108            $part->mimetype = 'application/pdf';
109        }
110
111        // try to fix invalid application/octet-stream mimetypes for PDF attachments
112        if ($part->mimetype == 'application/octet-stream' && preg_match('/\.pdf$/', strval($part->filename))) {
113            $body = $message->get_part_body($part->mime_id, false, 2048);
114            $real_mimetype = rcube_mime::file_content_type($body, $part->filename, $part->mimetype, true, true);
115            if (in_array($real_mimetype, $this->pdf_mimetypes)) {
116                $part->mimetype = $real_mimetype;
117            }
118        }
119
120        list($part->ctype_primary, $part->ctype_secondary) = explode('/', $part->mimetype);
121    }
122}
123