Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Contacts API: replace raw image data with url #25081

Merged
merged 6 commits into from
Jun 21, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/dav/appinfo/v1/carddav.php
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
}

$server->addPlugin(new \Sabre\CardDAV\VCFExportPlugin());
$server->addPlugin(new \OCA\DAV\CardDAV\ImageExportPlugin(\OC::$server->getLogger()));
$server->addPlugin(new ExceptionLoggerPlugin('carddav', \OC::$server->getLogger()));

// And off we go!
Expand Down
3 changes: 2 additions & 1 deletion apps/dav/lib/AppInfo/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,8 @@ public function __construct (array $urlParams=array()) {
public function setupContactsProvider(IManager $contactsManager, $userID) {
/** @var ContactsManager $cm */
$cm = $this->getContainer()->query('ContactsManager');
$cm->setupContactsProvider($contactsManager, $userID);
$urlGenerator = $this->getContainer()->getServer()->getURLGenerator();
$cm->setupContactsProvider($contactsManager, $userID, $urlGenerator);
}

public function registerHooks() {
Expand Down
44 changes: 34 additions & 10 deletions apps/dav/lib/CardDAV/AddressBookImpl.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

use OCP\Constants;
use OCP\IAddressBook;
use OCP\IURLGenerator;
use Sabre\VObject\Component\VCard;
use Sabre\VObject\Property\Text;
use Sabre\VObject\Reader;
Expand All @@ -40,21 +41,27 @@ class AddressBookImpl implements IAddressBook {
/** @var AddressBook */
private $addressBook;

/** @var IURLGenerator */
private $urlGenerator;

/**
* AddressBookImpl constructor.
*
* @param AddressBook $addressBook
* @param array $addressBookInfo
* @param CardDavBackend $backend
* @param IUrlGenerator $urlGenerator
*/
public function __construct(
AddressBook $addressBook,
array $addressBookInfo,
CardDavBackend $backend) {
CardDavBackend $backend,
IURLGenerator $urlGenerator) {

$this->addressBook = $addressBook;
$this->addressBookInfo = $addressBookInfo;
$this->backend = $backend;
$this->urlGenerator = $urlGenerator;
}

/**
Expand Down Expand Up @@ -83,11 +90,11 @@ public function getDisplayName() {
* @since 5.0.0
*/
public function search($pattern, $searchProperties, $options) {
$result = $this->backend->search($this->getKey(), $pattern, $searchProperties);
$results = $this->backend->search($this->getKey(), $pattern, $searchProperties);

$vCards = [];
foreach ($result as $cardData) {
$vCards[] = $this->vCard2Array($this->readCard($cardData));
foreach ($results as $result) {
$vCards[] = $this->vCard2Array($result['uri'], $this->readCard($result['carddata']));
}

return $vCards;
Expand All @@ -100,13 +107,12 @@ public function search($pattern, $searchProperties, $options) {
*/
public function createOrUpdate($properties) {
$update = false;
if (!isset($properties['UID'])) { // create a new contact
if (!isset($properties['URI'])) { // create a new contact
$uid = $this->createUid();
$uri = $uid . '.vcf';
$vCard = $this->createEmptyVCard($uid);
} else { // update existing contact
$uid = $properties['UID'];
$uri = $uid . '.vcf';
$uri = $properties['URI'];
$vCardData = $this->backend->getCard($this->getKey(), $uri);
$vCard = $this->readCard($vCardData['carddata']);
$update = true;
Expand All @@ -122,7 +128,7 @@ public function createOrUpdate($properties) {
$this->backend->createCard($this->getKey(), $uri, $vCard->serialize());
}

return $this->vCard2Array($vCard);
return $this->vCard2Array($uri, $vCard);

}

Expand Down Expand Up @@ -207,13 +213,31 @@ protected function createEmptyVCard($uid) {
/**
* create array with all vCard properties
*
* @param string $uri
* @param VCard $vCard
* @return array
*/
protected function vCard2Array(VCard $vCard) {
$result = [];
protected function vCard2Array($uri, VCard $vCard) {
$result = [
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@deepdiver Is there some helper method to generate the full carddav url here?
I have access to the principal uris, but no direct access to addressbooks/users/admin/contacts/

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no helper method at hand - the addressbook name is in addressBookInfo or in the addrebook instance.

No idea if this helps. THX

'URI' => $uri,
];

foreach ($vCard->children as $property) {
$result[$property->name] = $property->getValue();
if ($property->name === 'PHOTO' && $property->getValueType() === 'BINARY') {
$url = $this->urlGenerator->getAbsoluteURL(
$this->urlGenerator->linkTo('', 'remote.php') . '/dav/');
$url .= implode('/', [
'addressbooks',
substr($this->addressBookInfo['principaluri'], 11), //cut off 'principals/'
$this->addressBookInfo['uri'],
$uri
]) . '?photo';

$result['PHOTO'] = 'VALUE=uri:' . $url;
} else {
$result[$property->name] = $property->getValue();
}
}
if ($this->addressBookInfo['principaluri'] === 'principals/system/system' &&
$this->addressBookInfo['uri'] === 'system') {
Expand Down
8 changes: 5 additions & 3 deletions apps/dav/lib/CardDAV/CardDavBackend.php
Original file line number Diff line number Diff line change
Expand Up @@ -780,16 +780,18 @@ public function search($addressBookId, $pattern, $searchProperties) {
}
$query2->andWhere($query2->expr()->eq('cp.addressbookid', $query->createNamedParameter($addressBookId)));

$query->select('c.carddata')->from($this->dbCardsTable, 'c')
$query->select('c.carddata', 'c.uri')->from($this->dbCardsTable, 'c')
->where($query->expr()->in('c.id', $query->createFunction($query2->getSQL())));

$result = $query->execute();
$cards = $result->fetchAll();

$result->closeCursor();

return array_map(function($array) {return $this->readBlob($array['carddata']);}, $cards);

return array_map(function($array) {
$array['carddata'] = $this->readBlob($array['carddata']);
return $array;
}, $cards);
}

/**
Expand Down
14 changes: 9 additions & 5 deletions apps/dav/lib/CardDAV/ContactsManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
namespace OCA\DAV\CardDAV;

use OCP\Contacts\IManager;
use OCP\IURLGenerator;

class ContactsManager {

Expand All @@ -37,26 +38,29 @@ public function __construct(CardDavBackend $backend) {
/**
* @param IManager $cm
* @param string $userId
* @param IURLGenerator $urlGenerator
*/
public function setupContactsProvider(IManager $cm, $userId) {
public function setupContactsProvider(IManager $cm, $userId, IURLGenerator $urlGenerator) {
$addressBooks = $this->backend->getAddressBooksForUser("principals/users/$userId");
$this->register($cm, $addressBooks);
$this->register($cm, $addressBooks, $urlGenerator);
$addressBooks = $this->backend->getAddressBooksForUser("principals/system/system");
$this->register($cm, $addressBooks);
$this->register($cm, $addressBooks, $urlGenerator);
}

/**
* @param IManager $cm
* @param $addressBooks
* @param IURLGenerator $urlGenerator
*/
private function register(IManager $cm, $addressBooks) {
private function register(IManager $cm, $addressBooks, $urlGenerator) {
foreach ($addressBooks as $addressBookInfo) {
$addressBook = new \OCA\DAV\CardDAV\AddressBook($this->backend, $addressBookInfo);
$cm->registerAddressBook(
new AddressBookImpl(
$addressBook,
$addressBookInfo,
$this->backend
$this->backend,
$urlGenerator
)
);
}
Expand Down
146 changes: 146 additions & 0 deletions apps/dav/lib/CardDAV/ImageExportPlugin.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
<?php
/**
* @author Thomas Müller <[email protected]>
*
* @copyright Copyright (c) 2016, ownCloud, Inc.
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/

namespace OCA\DAV\CardDAV;

use OCP\ILogger;
use Sabre\CardDAV\Card;
use Sabre\DAV\Server;
use Sabre\DAV\ServerPlugin;
use Sabre\HTTP\RequestInterface;
use Sabre\HTTP\ResponseInterface;
use Sabre\VObject\Parameter;
use Sabre\VObject\Property\Binary;
use Sabre\VObject\Reader;

class ImageExportPlugin extends ServerPlugin {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this plugin deserves some unit tests as well 🙈


/** @var Server */
protected $server;
/** @var ILogger */
private $logger;

public function __construct(ILogger $logger) {
$this->logger = $logger;
}

/**
* Initializes the plugin and registers event handlers
*
* @param Server $server
* @return void
*/
function initialize(Server $server) {

$this->server = $server;
$this->server->on('method:GET', [$this, 'httpGet'], 90);
}

/**
* Intercepts GET requests on addressbook urls ending with ?photo.
*
* @param RequestInterface $request
* @param ResponseInterface $response
* @return bool|void
*/
function httpGet(RequestInterface $request, ResponseInterface $response) {

$queryParams = $request->getQueryParameters();
// TODO: in addition to photo we should also add logo some point in time
if (!array_key_exists('photo', $queryParams)) {
return true;
}

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

if (!($node instanceof Card)) {
return true;
}

$this->server->transactionType = 'carddav-image-export';

// Checking ACL, if available.
if ($aclPlugin = $this->server->getPlugin('acl')) {
/** @var \Sabre\DAVACL\Plugin $aclPlugin */
$aclPlugin->checkPrivileges($path, '{DAV:}read');
}

if ($result = $this->getPhoto($node)) {
$response->setHeader('Content-Type', $result['Content-Type']);
$response->setStatus(200);

$response->setBody($result['body']);

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

function getPhoto(Card $node) {
// TODO: this is kind of expensive - load carddav data from database and parse it
// we might want to build up a cache one day
try {
$vObject = $this->readCard($node->get());
if (!$vObject->PHOTO) {
return false;
}

$photo = $vObject->PHOTO;
$type = $this->getType($photo);

$valType = $photo->getValueType();
$val = ($valType === 'URI' ? $photo->getRawMimeDirValue() : $photo->getValue());
return [
'Content-Type' => $type,
'body' => $val
];
} catch(\Exception $ex) {
$this->logger->logException($ex);
}
return false;
}

private function readCard($cardData) {
return Reader::read($cardData);
}

/**
* @param Binary $photo
* @return Parameter
*/
private function getType($photo) {
$params = $photo->parameters();
if (isset($params['TYPE']) || isset($params['MEDIATYPE'])) {
/** @var Parameter $typeParam */
$typeParam = isset($params['TYPE']) ? $params['TYPE'] : $params['MEDIATYPE'];
$type = $typeParam->getValue();

if (strpos($type, 'image/') === 0) {
return $type;
} else {
return 'image/' . strtolower($type);
}
}
return '';
}
}
2 changes: 2 additions & 0 deletions apps/dav/lib/Server.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
namespace OCA\DAV;

use OCA\DAV\CalDAV\Schedule\IMipPlugin;
use OCA\DAV\CardDAV\ImageExportPlugin;
use OCA\DAV\Connector\Sabre\Auth;
use OCA\DAV\Connector\Sabre\BlockLegacyClientPlugin;
use OCA\DAV\Connector\Sabre\DavAclPlugin;
Expand Down Expand Up @@ -103,6 +104,7 @@ public function __construct(IRequest $request, $baseUri) {
// addressbook plugins
$this->server->addPlugin(new \OCA\DAV\CardDAV\Plugin());
$this->server->addPlugin(new VCFExportPlugin());
$this->server->addPlugin(new ImageExportPlugin(\OC::$server->getLogger()));

// system tags plugins
$this->server->addPlugin(new \OCA\DAV\SystemTag\SystemTagPlugin(
Expand Down
Loading