@Override protected void listRemote() throws IOException, HttpException, DavException { // fetch list of remote VCards and build hash table to index file name try { davAddressBook().addressbookQuery(); } catch (HttpException e) { /* non-successful responses to CARDDAV:addressbook-query with empty filter, tested on 2015/10/21 * fastmail.com 403 Forbidden (DAV:error CARDDAV:supported-filter) * mailbox.org (OpenXchange) 400 Bad Request * SOGo 207 Multi-status, but without entries http://www.sogo.nu/bugs/view.php?id=3370 * Zimbra ZCS 500 Server Error https://bugzilla.zimbra.com/show_bug.cgi?id=101902 */ if (e.status == 400 || e.status == 403 || e.status == 500 || e.status == 501) { App.log.log( Level.WARNING, "Server error on REPORT addressbook-query, falling back to PROPFIND", e); davAddressBook().propfind(1, GetETag.NAME); } else // no defined fallback, pass through exception throw e; } remoteResources = new HashMap<>(davCollection.members.size()); for (DavResource vCard : davCollection.members) { String fileName = vCard.fileName(); App.log.fine("Found remote VCard: " + fileName); remoteResources.put(fileName, vCard); } }
@Override protected void downloadRemote() throws IOException, HttpException, DavException, ContactsStorageException { App.log.info("Downloading " + toDownload.size() + " contacts (" + MAX_MULTIGET + " at once)"); // prepare downloader which may be used to download external resource like contact photos Contact.Downloader downloader = new ResourceDownloader(collectionURL); // download new/updated VCards from server for (DavResource[] bunch : ArrayUtils.partition( toDownload.toArray(new DavResource[toDownload.size()]), MAX_MULTIGET)) { if (Thread.interrupted()) return; App.log.info("Downloading " + StringUtils.join(bunch, ", ")); if (bunch.length == 1) { // only one contact, use GET DavResource remote = bunch[0]; ResponseBody body = remote.get("text/vcard;version=4.0, text/vcard;charset=utf-8;q=0.8, text/vcard;q=0.5"); String eTag = ((GetETag) remote.properties.get(GetETag.NAME)).eTag; Charset charset = Charsets.UTF_8; MediaType contentType = body.contentType(); if (contentType != null) charset = contentType.charset(Charsets.UTF_8); @Cleanup InputStream stream = body.byteStream(); processVCard(remote.fileName(), eTag, stream, charset, downloader); } else { // multiple contacts, use multi-get List<HttpUrl> urls = new LinkedList<>(); for (DavResource remote : bunch) urls.add(remote.location); davAddressBook().multiget(urls.toArray(new HttpUrl[urls.size()]), hasVCard4); // process multiget results for (DavResource remote : davCollection.members) { String eTag; GetETag getETag = (GetETag) remote.properties.get(GetETag.NAME); if (getETag != null) eTag = getETag.eTag; else throw new DavException("Received multi-get response without ETag"); Charset charset = Charsets.UTF_8; GetContentType getContentType = (GetContentType) remote.properties.get(GetContentType.NAME); if (getContentType != null && getContentType.type != null) { MediaType type = MediaType.parse(getContentType.type); if (type != null) charset = type.charset(Charsets.UTF_8); } AddressData addressData = (AddressData) remote.properties.get(AddressData.NAME); if (addressData == null || addressData.vCard == null) throw new DavException("Received multi-get response without address data"); @Cleanup InputStream stream = new ByteArrayInputStream(addressData.vCard.getBytes()); processVCard(remote.fileName(), eTag, stream, charset, downloader); } } } }