1<?php 2 3namespace Sabre\CardDAV; 4 5use Sabre\DAV; 6use Sabre\HTTP\RequestInterface; 7use Sabre\HTTP\ResponseInterface; 8use Sabre\VObject; 9 10/** 11 * VCF Exporter 12 * 13 * This plugin adds the ability to export entire address books as .vcf files. 14 * This is useful for clients that don't support CardDAV yet. They often do 15 * support vcf files. 16 * 17 * @copyright Copyright (C) fruux GmbH (https://fruux.com/) 18 * @author Evert Pot (http://evertpot.com/) 19 * @author Thomas Tanghus (http://tanghus.net/) 20 * @license http://sabre.io/license/ Modified BSD License 21 */ 22class VCFExportPlugin extends DAV\ServerPlugin { 23 24 /** 25 * Reference to Server class 26 * 27 * @var DAV\Server 28 */ 29 protected $server; 30 31 /** 32 * Initializes the plugin and registers event handlers 33 * 34 * @param DAV\Server $server 35 * @return void 36 */ 37 function initialize(DAV\Server $server) { 38 39 $this->server = $server; 40 $this->server->on('method:GET', [$this, 'httpGet'], 90); 41 $server->on('browserButtonActions', function($path, $node, &$actions) { 42 if ($node instanceof IAddressBook) { 43 $actions .= '<a href="' . htmlspecialchars($path, ENT_QUOTES, 'UTF-8') . '?export"><span class="oi" data-glyph="book"></span></a>'; 44 } 45 }); 46 } 47 48 /** 49 * Intercepts GET requests on addressbook urls ending with ?export. 50 * 51 * @param RequestInterface $request 52 * @param ResponseInterface $response 53 * @return bool 54 */ 55 function httpGet(RequestInterface $request, ResponseInterface $response) { 56 57 $queryParams = $request->getQueryParameters(); 58 if (!array_key_exists('export', $queryParams)) return; 59 60 $path = $request->getPath(); 61 62 $node = $this->server->tree->getNodeForPath($path); 63 64 if (!($node instanceof IAddressBook)) return; 65 66 $this->server->transactionType = 'get-addressbook-export'; 67 68 // Checking ACL, if available. 69 if ($aclPlugin = $this->server->getPlugin('acl')) { 70 $aclPlugin->checkPrivileges($path, '{DAV:}read'); 71 } 72 73 $nodes = $this->server->getPropertiesForPath($path, [ 74 '{' . Plugin::NS_CARDDAV . '}address-data', 75 ], 1); 76 77 $format = 'text/directory'; 78 79 $output = null; 80 $filenameExtension = null; 81 82 switch ($format) { 83 case 'text/directory': 84 $output = $this->generateVCF($nodes); 85 $filenameExtension = '.vcf'; 86 break; 87 } 88 89 $filename = preg_replace( 90 '/[^a-zA-Z0-9-_ ]/um', 91 '', 92 $node->getName() 93 ); 94 $filename .= '-' . date('Y-m-d') . $filenameExtension; 95 96 $response->setHeader('Content-Disposition', 'attachment; filename="' . $filename . '"'); 97 $response->setHeader('Content-Type', $format); 98 99 $response->setStatus(200); 100 $response->setBody($output); 101 102 // Returning false to break the event chain 103 return false; 104 105 } 106 107 /** 108 * Merges all vcard objects, and builds one big vcf export 109 * 110 * @param array $nodes 111 * @return string 112 */ 113 function generateVCF(array $nodes) { 114 115 $output = ""; 116 117 foreach ($nodes as $node) { 118 119 if (!isset($node[200]['{' . Plugin::NS_CARDDAV . '}address-data'])) { 120 continue; 121 } 122 $nodeData = $node[200]['{' . Plugin::NS_CARDDAV . '}address-data']; 123 124 // Parsing this node so VObject can clean up the output. 125 $vcard = VObject\Reader::read($nodeData); 126 $output .= $vcard->serialize(); 127 128 // Destroy circular references to PHP will GC the object. 129 $vcard->destroy(); 130 131 } 132 133 return $output; 134 135 } 136 137 /** 138 * Returns a plugin name. 139 * 140 * Using this name other plugins will be able to access other plugins 141 * using \Sabre\DAV\Server::getPlugin 142 * 143 * @return string 144 */ 145 function getPluginName() { 146 147 return 'vcf-export'; 148 149 } 150 151 /** 152 * Returns a bunch of meta-data about the plugin. 153 * 154 * Providing this information is optional, and is mainly displayed by the 155 * Browser plugin. 156 * 157 * The description key in the returned array may contain html and will not 158 * be sanitized. 159 * 160 * @return array 161 */ 162 function getPluginInfo() { 163 164 return [ 165 'name' => $this->getPluginName(), 166 'description' => 'Adds the ability to export CardDAV addressbooks as a single vCard file.', 167 'link' => 'http://sabre.io/dav/vcf-export-plugin/', 168 ]; 169 170 } 171 172} 173