aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/sabre/dav/lib/CardDAV/VCFExportPlugin.php
blob: 194927c53123c9b583fb4b1edf931ba1c689b1a3 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
<?php

declare(strict_types=1);

namespace Sabre\CardDAV;

use Sabre\DAV;
use Sabre\HTTP\RequestInterface;
use Sabre\HTTP\ResponseInterface;
use Sabre\VObject;

/**
 * VCF Exporter.
 *
 * This plugin adds the ability to export entire address books as .vcf files.
 * This is useful for clients that don't support CardDAV yet. They often do
 * support vcf files.
 *
 * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
 * @author Evert Pot (http://evertpot.com/)
 * @author Thomas Tanghus (http://tanghus.net/)
 * @license http://sabre.io/license/ Modified BSD License
 */
class VCFExportPlugin extends DAV\ServerPlugin
{
    /**
     * Reference to Server class.
     *
     * @var DAV\Server
     */
    protected $server;

    /**
     * Initializes the plugin and registers event handlers.
     *
     * @param DAV\Server $server
     */
    public function initialize(DAV\Server $server)
    {
        $this->server = $server;
        $this->server->on('method:GET', [$this, 'httpGet'], 90);
        $server->on('browserButtonActions', function ($path, $node, &$actions) {
            if ($node instanceof IAddressBook) {
                $actions .= '<a href="'.htmlspecialchars($path, ENT_QUOTES, 'UTF-8').'?export"><span class="oi" data-glyph="book"></span></a>';
            }
        });
    }

    /**
     * Intercepts GET requests on addressbook urls ending with ?export.
     *
     * @param RequestInterface  $request
     * @param ResponseInterface $response
     *
     * @return bool
     */
    public function httpGet(RequestInterface $request, ResponseInterface $response)
    {
        $queryParams = $request->getQueryParameters();
        if (!array_key_exists('export', $queryParams)) {
            return;
        }

        $path = $request->getPath();

        $node = $this->server->tree->getNodeForPath($path);

        if (!($node instanceof IAddressBook)) {
            return;
        }

        $this->server->transactionType = 'get-addressbook-export';

        // Checking ACL, if available.
        if ($aclPlugin = $this->server->getPlugin('acl')) {
            $aclPlugin->checkPrivileges($path, '{DAV:}read');
        }

        $nodes = $this->server->getPropertiesForPath($path, [
            '{'.Plugin::NS_CARDDAV.'}address-data',
        ], 1);

        $format = 'text/directory';

        $output = null;
        $filenameExtension = null;

        switch ($format) {
            case 'text/directory':
                $output = $this->generateVCF($nodes);
                $filenameExtension = '.vcf';
                break;
        }

        $filename = preg_replace(
            '/[^a-zA-Z0-9-_ ]/um',
            '',
            $node->getName()
        );
        $filename .= '-'.date('Y-m-d').$filenameExtension;

        $response->setHeader('Content-Disposition', 'attachment; filename="'.$filename.'"');
        $response->setHeader('Content-Type', $format);

        $response->setStatus(200);
        $response->setBody($output);

        // Returning false to break the event chain
        return false;
    }

    /**
     * Merges all vcard objects, and builds one big vcf export.
     *
     * @param array $nodes
     *
     * @return string
     */
    public function generateVCF(array $nodes)
    {
        $output = '';

        foreach ($nodes as $node) {
            if (!isset($node[200]['{'.Plugin::NS_CARDDAV.'}address-data'])) {
                continue;
            }
            $nodeData = $node[200]['{'.Plugin::NS_CARDDAV.'}address-data'];

            // Parsing this node so VObject can clean up the output.
            $vcard = VObject\Reader::read($nodeData);
            $output .= $vcard->serialize();

            // Destroy circular references to PHP will GC the object.
            $vcard->destroy();
        }

        return $output;
    }

    /**
     * Returns a plugin name.
     *
     * Using this name other plugins will be able to access other plugins
     * using \Sabre\DAV\Server::getPlugin
     *
     * @return string
     */
    public function getPluginName()
    {
        return 'vcf-export';
    }

    /**
     * Returns a bunch of meta-data about the plugin.
     *
     * Providing this information is optional, and is mainly displayed by the
     * Browser plugin.
     *
     * The description key in the returned array may contain html and will not
     * be sanitized.
     *
     * @return array
     */
    public function getPluginInfo()
    {
        return [
            'name' => $this->getPluginName(),
            'description' => 'Adds the ability to export CardDAV addressbooks as a single vCard file.',
            'link' => 'http://sabre.io/dav/vcf-export-plugin/',
        ];
    }
}