Skip to content

Commit

Permalink
Migrate to multi-get requests
Browse files Browse the repository at this point in the history
Signed-off-by: John Molakvoæ (skjnldsv) <[email protected]>
  • Loading branch information
skjnldsv committed Oct 25, 2018
1 parent 5d67b11 commit 5387c87
Showing 1 changed file with 23 additions and 93 deletions.
116 changes: 23 additions & 93 deletions apps/dav/lib/CardDAV/VCFMultiExportPlugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,10 @@

namespace OCA\DAV\CardDAV;

use OCA\DAV\CardDAV\AddressBook;
use Sabre\DAV;
use Sabre\DAV\Server;
use Sabre\HTTP\RequestInterface;
use Sabre\HTTP\ResponseInterface;
use Sabre\VObject;

class VCFMultiExportPlugin extends DAV\ServerPlugin {

Expand All @@ -44,11 +42,11 @@ class VCFMultiExportPlugin extends DAV\ServerPlugin {
*/
public function initialize(Server $server) {
$this->server = $server;
$this->server->on('method:GET', [$this, 'httpGet'], 90);
$this->server->on('afterMethod:REPORT', [$this, 'httpGet'], 90);
}

/**
* Intercepts GET requests
* Intercepts REPORT requests
*
* @param RequestInterface $request
* @param ResponseInterface $response
Expand All @@ -57,103 +55,35 @@ public function initialize(Server $server) {
public function httpGet(RequestInterface $request, ResponseInterface $response) {

$queryParams = $request->getQueryParameters();

// check for post data validity
if (!array_key_exists('vcards', $queryParams)
|| !is_array($queryParams['vcards'])) {
return true;
if (!array_key_exists('export', $queryParams)) {
return;
}

// user addressbooks home
$path = $request->getPath();
$paths = $queryParams['vcards'];

// extract unique addressbooks from all the paths
$addressbookIDs = array_unique(array_map(function($path) {
return explode('/', $path)[0];
}, $paths));

// valid addressbooks and the requested vcards
$addressbooks = [];
foreach ($addressbookIDs as $addressbook) {
$node = $this->server->tree->getNodeForPath("$path/$addressbook/");

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

// checking that the path is a valid addressbook
if ($node instanceof AddressBook) {
$addressbooks = array_merge($addressbooks, [$addressbook => $vcards]);
}
// Only handling xml
$contentType = $response->getHeader('Content-Type');
if (strpos($contentType, 'application/xml') === false && strpos($contentType, 'text/xml') === false) {
return;
}

// array of vcard paths
$absolutePaths = array_map(function ($vcard) use ($path) {
return "$path/$vcard";
}, $paths);

$this->server->transactionType = 'vcf-multi-export';

// We do not need to check for other paths
// If one of the path is incorrect, it either
// 1. would have been thrown if the ab doesn't exists
// 2. will be ignored by the getMultipleNodes method
$nodes = $this->server->tree->getMultipleNodes($absolutePaths);

$format = 'text/directory';

$output = null;
$filenameExtension = null;
$this->server->transactionType = 'vcf-multi-get-intercept-and-export';

switch ($format) {
case 'text/directory':
$output = $this->generateVCF($nodes);
$filenameExtension = '.vcf';
break;
}
// Get the xml response
$responseBody = $response->getBodyAsString();
$responseXml = $this->server->xml->parse($responseBody);

$filename = preg_replace(
'/[^a-zA-Z0-9-_ ]/um',
'',
$node->getName()
);
$filename .= '-' . date('Y-m-d') . $filenameExtension;
// Reduce the vcards into one string
$output = array_reduce($responseXml->getResponses(), function ($vcf, $card) {
$vcf .= $card->getResponseProperties()[200]['{urn:ietf:params:xml:ns:carddav}address-data'];
return $vcf;
}, '');

// Build and override the response
$filename = 'vcfexport-' . date('Y-m-d') . $filenameExtension;
$response->setHeader('Content-Disposition', 'attachment; filename="' . $filename . '"');
$response->setHeader('Content-Type', $format);

$response->setStatus(200);
$response->setHeader('Content-Type', 'text/directory');
$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
*/
private function generateVCF(array $nodes) {
$output = '';

foreach ($nodes as $node) {

$nodeData = $node->get();

// 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;
return true;
}

/**
Expand All @@ -165,7 +95,7 @@ private function generateVCF(array $nodes) {
* @return string
*/
public function getPluginName() {
return 'vcf-multi-export';
return 'vcf-multi-get-intercept-and-export';
}

/**
Expand All @@ -182,7 +112,7 @@ public function getPluginName() {
public function getPluginInfo() {
return [
'name' => $this->getPluginName(),
'description' => 'Adds the ability to export multiple vCard as a single vCard file.'
'description' => 'Intercept a multi-get request and return a single vcf file instead.'
];

}
Expand Down

0 comments on commit 5387c87

Please sign in to comment.