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