/*
* *
* Copyright (C) 2014 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* /
*/
package org.anhonesteffort.flock.webdav.carddav;
import org.anhonesteffort.flock.util.guava.Optional;
import org.anhonesteffort.flock.webdav.WebDavConstants;
import org.anhonesteffort.flock.webdav.AbstractDavComponentStore;
import org.anhonesteffort.flock.webdav.DavClient;
import org.anhonesteffort.flock.webdav.DavComponentStore;
import org.anhonesteffort.flock.webdav.ExtendedMkCol;
import org.anhonesteffort.flock.webdav.PropertyParseException;
import org.apache.jackrabbit.webdav.DavConstants;
import org.apache.jackrabbit.webdav.DavException;
import org.apache.jackrabbit.webdav.MultiStatus;
import org.apache.jackrabbit.webdav.MultiStatusResponse;
import org.apache.jackrabbit.webdav.Status;
import org.apache.jackrabbit.webdav.client.methods.MkColMethod;
import org.apache.jackrabbit.webdav.client.methods.PropFindMethod;
import org.apache.jackrabbit.webdav.property.DavProperty;
import org.apache.jackrabbit.webdav.property.DavPropertyName;
import org.apache.jackrabbit.webdav.property.DavPropertyNameSet;
import org.apache.jackrabbit.webdav.property.DavPropertySet;
import org.apache.jackrabbit.webdav.property.DefaultDavProperty;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
/**
* Programmer: rhodey
*/
public class CardDavStore extends AbstractDavComponentStore<CardDavCollection>
implements DavComponentStore<CardDavCollection>
{
protected Optional<String> addressBookHomeSet = Optional.absent();
public CardDavStore(String hostHREF,
String username,
String password,
Optional<String> currentUserPrincipal,
Optional<String> addressBookHomeSet)
throws DavException, IOException
{
super(hostHREF, username, password, currentUserPrincipal);
this.addressBookHomeSet = addressBookHomeSet;
}
public CardDavStore(DavClient client,
Optional<String> currentUserPrincipal,
Optional<String> addressBookHomeSet)
{
super(client, currentUserPrincipal);
this.addressBookHomeSet = addressBookHomeSet;
}
@Override
protected String getWellKnownUri() {
return "/.well-known/carddav";
}
public Optional<String> getAddressbookHomeSet()
throws PropertyParseException, DavException, IOException
{
if (addressBookHomeSet.isPresent())
return addressBookHomeSet;
if (!getCurrentUserPrincipal().isPresent())
throw new PropertyParseException("DAV:current-user-principal unavailable, server must support rfc5785.",
getHostHREF(), WebDavConstants.PROPERTY_NAME_CURRENT_USER_PRINCIPAL);
DavPropertyNameSet principalsProps = new DavPropertyNameSet();
principalsProps.add(CardDavConstants.PROPERTY_NAME_ADDRESSBOOK_HOME_SET);
principalsProps.add(DavPropertyName.DISPLAYNAME);
String propFindUri = getHostHREF().concat(getCurrentUserPrincipal().get());
PropFindMethod propFindMethod = new PropFindMethod(propFindUri,
principalsProps,
PropFindMethod.DEPTH_0);
try {
getClient().execute(propFindMethod);
MultiStatus multiStatus = propFindMethod.getResponseBodyAsMultiStatus();
MultiStatusResponse[] msResponses = multiStatus.getResponses();
for (MultiStatusResponse msResponse : msResponses) {
DavPropertySet foundProperties = msResponse.getProperties(WebDavConstants.SC_OK);
DavProperty homeSetProperty = foundProperties.get(CardDavConstants.PROPERTY_NAME_ADDRESSBOOK_HOME_SET);
for (Status status : msResponse.getStatus()) {
if (status.getStatusCode() == WebDavConstants.SC_OK) {
if (homeSetProperty != null && homeSetProperty.getValue() instanceof ArrayList) {
for (Object child : (ArrayList<?>) homeSetProperty.getValue()) {
if (child instanceof Element) {
String addressbookHomeSetUri = ((Element) child).getTextContent();
if (!(addressbookHomeSetUri.endsWith("/")))
addressbookHomeSetUri = addressbookHomeSetUri.concat("/");
addressBookHomeSet = Optional.of(addressbookHomeSetUri);
return addressBookHomeSet;
}
}
}
// OwnCloud :(
else if (homeSetProperty != null && homeSetProperty.getValue() instanceof Element) {
String addressbookHomeSetUri = ((Element) homeSetProperty.getValue()).getTextContent();
if (!(addressbookHomeSetUri.endsWith("/")))
addressbookHomeSetUri = addressbookHomeSetUri.concat("/");
addressBookHomeSet = Optional.of(addressbookHomeSetUri);
return addressBookHomeSet;
}
}
}
}
} finally {
propFindMethod.releaseConnection();
}
return Optional.absent();
}
@Override
public void addCollection(String path)
throws DavException, IOException
{
addCollection(path, new DavPropertySet());
}
public void addCollection(String path, DavPropertySet properties)
throws DavException, IOException
{
ArrayList<DavPropertyName> resourceTypes = new ArrayList<DavPropertyName>();
resourceTypes.add(DavPropertyName.create(DavConstants.XML_COLLECTION, DavConstants.NAMESPACE));
resourceTypes.add(DavPropertyName.create("addressbook", CardDavConstants.CARDDAV_NAMESPACE)); // TODO: constant for this...
properties.add(new DefaultDavProperty<ArrayList<DavPropertyName>>(DavPropertyName.RESOURCETYPE, resourceTypes));
CardDavCollection collection = new CardDavCollection(this, getHostHREF().concat(path), properties);
MkColMethod mkColMethod = new MkColMethod(collection.getPath());
ExtendedMkCol extendedMkCol = new ExtendedMkCol(properties);
try {
mkColMethod.setRequestBody(extendedMkCol);
getClient().execute(mkColMethod);
if (!mkColMethod.succeeded())
throw new DavException(mkColMethod.getStatusCode(), mkColMethod.getStatusText());
} finally {
mkColMethod.releaseConnection();
}
}
public void addCollection(String path,
String displayName,
String description)
throws DavException, IOException
{
DavPropertySet properties = new DavPropertySet();
properties.add(new DefaultDavProperty<String>(DavPropertyName.DISPLAYNAME, displayName));
properties.add(new DefaultDavProperty<String>(CardDavConstants.PROPERTY_NAME_ADDRESSBOOK_DESCRIPTION, description));
addCollection(path, properties);
}
// TODO: I don't like this...
public static List<CardDavCollection> getCollectionsFromMultiStatusResponses(CardDavStore store,
MultiStatusResponse[] msResponses)
{
List<CardDavCollection> collections = new LinkedList<CardDavCollection>();
for (MultiStatusResponse msResponse : msResponses) {
DavPropertySet foundProperties = msResponse.getProperties(WebDavConstants.SC_OK);
String collectionUri = msResponse.getHref();
for (Status status : msResponse.getStatus()) {
if (status.getStatusCode() == WebDavConstants.SC_OK) {
boolean isAddressbookCollection = false;
DavPropertySet collectionProperties = new DavPropertySet();
DavProperty resourceTypeProperty = foundProperties.get(DavPropertyName.RESOURCETYPE);
if (resourceTypeProperty != null) {
Object resourceTypeValue = resourceTypeProperty.getValue();
if (resourceTypeValue instanceof ArrayList) {
for (Node child : (ArrayList<Node>) resourceTypeValue) {
if (child instanceof Element) {
String localName = child.getLocalName();
if (localName != null)
isAddressbookCollection = isAddressbookCollection ||
localName.equals(CardDavConstants.RESOURCE_TYPE_ADDRESSBOOK);
}
}
}
}
if (isAddressbookCollection) {
for (DavProperty property : foundProperties) {
if (property != null)
collectionProperties.add(property);
}
collections.add(new CardDavCollection(store, collectionUri, collectionProperties));
}
}
}
}
return collections;
}
@Override
public Optional<CardDavCollection> getCollection(String path) throws DavException, IOException {
CardDavCollection targetCollection = new CardDavCollection(this, path);
DavPropertyNameSet collectionProps = targetCollection.getPropertyNamesForFetch();
PropFindMethod propFindMethod = new PropFindMethod(path, collectionProps, PropFindMethod.DEPTH_0);
try {
getClient().execute(propFindMethod);
MultiStatus multiStatus = propFindMethod.getResponseBodyAsMultiStatus();
MultiStatusResponse[] responses = multiStatus.getResponses();
List<CardDavCollection> returnedCollections = getCollectionsFromMultiStatusResponses(this, responses);
if (returnedCollections.size() == 0)
return Optional.absent();
return Optional.of(returnedCollections.get(0));
} catch (DavException e) {
if (e.getErrorCode() == WebDavConstants.SC_NOT_FOUND)
return Optional.absent();
throw e;
} finally {
propFindMethod.releaseConnection();
}
}
@Override
public List<CardDavCollection> getCollections()
throws PropertyParseException, DavException, IOException
{
Optional<String> addressbookHomeSetUri = getAddressbookHomeSet();
if (!addressbookHomeSetUri.isPresent())
throw new PropertyParseException("No addressbook-home-set property found for user.",
getHostHREF(), CardDavConstants.PROPERTY_NAME_ADDRESSBOOK_HOME_SET);
CardDavCollection hack = new CardDavCollection(this, "");
DavPropertyNameSet addressbookProps = hack.getPropertyNamesForFetch();
PropFindMethod method = new PropFindMethod(getHostHREF().concat(addressbookHomeSetUri.get()),
addressbookProps,
PropFindMethod.DEPTH_1);
try {
getClient().execute(method);
MultiStatus multiStatus = method.getResponseBodyAsMultiStatus();
MultiStatusResponse[] responses = multiStatus.getResponses();
return getCollectionsFromMultiStatusResponses(this, responses);
} finally {
method.releaseConnection();
}
}
}