Skip to content

Commit

Permalink
Mark read-only collections as read-only
Browse files Browse the repository at this point in the history
The UI layer will enforce that the user cannot edit or delete contacts
in read-only collections, and thus we won't attempt to upsync changes
to such collections (which would fail).
  • Loading branch information
Chris Adams committed Sep 24, 2020
1 parent 6030db1 commit 9184b28
Show file tree
Hide file tree
Showing 7 changed files with 72 additions and 1 deletion.
1 change: 1 addition & 0 deletions rpm/buteo-sync-plugin-carddav.spec
Expand Up @@ -55,6 +55,7 @@ This package contains unit tests for the CardDAV Buteo sync plugin.
/opt/tests/buteo/plugins/carddav/data/replyparser_addressbookhome_single-well-formed.xml
/opt/tests/buteo/plugins/carddav/data/replyparser_addressbookinformation_empty.xml
/opt/tests/buteo/plugins/carddav/data/replyparser_addressbookinformation_single-well-formed.xml
/opt/tests/buteo/plugins/carddav/data/replyparser_addressbookinformation_two-with-privileges.xml
/opt/tests/buteo/plugins/carddav/data/replyparser_addressbookinformation_addressbook-plus-contact.xml
/opt/tests/buteo/plugins/carddav/data/replyparser_addressbookinformation_addressbook-calendar-principal.xml
/opt/tests/buteo/plugins/carddav/data/replyparser_addressbookinformation_addressbook-principal-proxy.xml
Expand Down
15 changes: 15 additions & 0 deletions src/replyparser.cpp
Expand Up @@ -241,6 +241,10 @@ QList<ReplyParser::AddressBookInformation> ReplyParser::parseAddressbookInformat
<card:addressbook />
</d:resourcetype>
<d:displayname>My Address Book</d:displayname>
<d:current-user-privilege-set>
<d:privilege><d:read /></d:privilege>
<d:privilege><d:write /></d:privilege>
</d:current-user-privilege-set>
<cs:getctag>3145</cs:getctag>
<d:sync-token>http://sabredav.org/ns/sync-token/3145</d:sync-token>
</d:prop>
Expand Down Expand Up @@ -312,6 +316,17 @@ QList<ReplyParser::AddressBookInformation> ReplyParser::parseAddressbookInformat
if (prop.contains("displayname")) {
currInfo.displayName = prop.value("displayname").toMap().value("@text").toString();
}
if (prop.contains("current-user-privilege-set")) {
bool foundWrite = false;
const QVariantList privileges = prop.value("current-user-privilege-set").toMap().value("privilege").toList();
for (const QVariant &pv : privileges) {
const QVariantMap pvm = pv.toMap();
if (pvm.contains("write")) {
foundWrite = true;
}
}
currInfo.readOnly = !foundWrite;
}
bool thisPropstatIsForResourceType = false;
if (prop.contains("resourcetype")) {
thisPropstatIsForResourceType = true;
Expand Down
1 change: 1 addition & 0 deletions src/replyparser_p.h
Expand Up @@ -48,6 +48,7 @@ class ReplyParser
QString displayName;
QString ctag;
QString syncToken;
bool readOnly = false;
};

class ContactInformation {
Expand Down
1 change: 1 addition & 0 deletions src/requestgenerator.cpp
Expand Up @@ -213,6 +213,7 @@ QNetworkReply *RequestGenerator::addressbooksInformation(const QString &serverUr
"<d:prop>"
"<d:resourcetype />"
"<d:displayname />"
"<d:current-user-privilege-set />"
"<d:sync-token />"
"<cs:getctag />"
"</d:prop>"
Expand Down
2 changes: 2 additions & 0 deletions src/syncer.cpp
Expand Up @@ -142,6 +142,7 @@ bool Syncer::determineRemoteCollections()
addressbook.setExtendedMetaData(COLLECTION_EXTENDEDMETADATA_KEY_APPLICATIONNAME, CARDDAV_CONTACTS_APPLICATION);
addressbook.setExtendedMetaData(COLLECTION_EXTENDEDMETADATA_KEY_ACCOUNTID, m_accountId);
addressbook.setExtendedMetaData(COLLECTION_EXTENDEDMETADATA_KEY_REMOTEPATH, it->url);
addressbook.setExtendedMetaData(COLLECTION_EXTENDEDMETADATA_KEY_READONLY, it->readOnly);
addressbook.setExtendedMetaData(KEY_CTAG, it->ctag);
addressbook.setExtendedMetaData(KEY_SYNCTOKEN, it->syncToken);
addressbooks.append(addressbook);
Expand Down Expand Up @@ -175,6 +176,7 @@ bool Syncer::determineRemoteCollectionChanges(
addressbook.setExtendedMetaData(COLLECTION_EXTENDEDMETADATA_KEY_APPLICATIONNAME, CARDDAV_CONTACTS_APPLICATION);
addressbook.setExtendedMetaData(COLLECTION_EXTENDEDMETADATA_KEY_ACCOUNTID, m_accountId);
addressbook.setExtendedMetaData(COLLECTION_EXTENDEDMETADATA_KEY_REMOTEPATH, path);
addressbook.setExtendedMetaData(COLLECTION_EXTENDEDMETADATA_KEY_READONLY, it->readOnly);
addressbook.setExtendedMetaData(KEY_CTAG, it->ctag);
addressbook.setExtendedMetaData(KEY_SYNCTOKEN, it->syncToken);
remoteCollections.insert(path, addressbook);
Expand Down
@@ -0,0 +1,31 @@
<d:multistatus xmlns:d="DAV:" xmlns:cs="http://calendarserver.org/ns/">
<d:response>
<d:href>/addressbooks/johndoe/contacts/</d:href>
<d:propstat>
<d:prop>
<d:displayname>My Address Book</d:displayname>
<d:current-user-privilege-set>
<d:privilege><d:read /></d:privilege>
<d:privilege><d:write /></d:privilege>
</d:current-user-privilege-set>
<cs:getctag>3145</cs:getctag>
<d:sync-token>http://sabredav.org/ns/sync-token/3145</d:sync-token>
</d:prop>
<d:status>HTTP/1.1 200 OK</d:status>
</d:propstat>
</d:response>
<d:response>
<d:href>/addressbooks/johndoe/readonly-contacts/</d:href>
<d:propstat>
<d:prop>
<d:displayname>ReadOnly Address Book</d:displayname>
<d:current-user-privilege-set>
<d:privilege><d:read /></d:privilege>
</d:current-user-privilege-set>
<cs:getctag>3149</cs:getctag>
<d:sync-token>http://sabredav.org/ns/sync-token/3149</d:sync-token>
</d:prop>
<d:status>HTTP/1.1 200 OK</d:status>
</d:propstat>
</d:response>
</d:multistatus>
22 changes: 21 additions & 1 deletion tests/replyparser/tst_replyparser.cpp
Expand Up @@ -252,14 +252,34 @@ void tst_replyparser::parseAddressbookInformation_data()
<< QStringLiteral("data/replyparser_addressbookinformation_addressbook-plus-collection-resource.xml")
<< QStringLiteral("/carddav/accountname%40server.tld/addressbook/")
<< infos;

infos.clear();
ReplyParser::AddressBookInformation a7;
a7.url = QStringLiteral("/addressbooks/johndoe/contacts/");
a7.displayName = QStringLiteral("My Address Book");
a7.ctag = QStringLiteral("3145");
a7.syncToken = QStringLiteral("http://sabredav.org/ns/sync-token/3145");
a7.readOnly = false;
ReplyParser::AddressBookInformation a8;
a8.url = QStringLiteral("/addressbooks/johndoe/readonly-contacts/");
a8.displayName = QStringLiteral("ReadOnly Address Book");
a8.ctag = QStringLiteral("3149");
a8.syncToken = QStringLiteral("http://sabredav.org/ns/sync-token/3149");
a8.readOnly = true;
infos << a7 << a8;
QTest::newRow("two addressbooks information in response with privileges specified")
<< QStringLiteral("data/replyparser_addressbookinformation_two-with-privileges.xml")
<< QStringLiteral("/addressbooks/johndoe/")
<< infos;
}

bool operator==(const ReplyParser::AddressBookInformation& first, const ReplyParser::AddressBookInformation& second)
{
return first.url == second.url
&& first.displayName == second.displayName
&& first.ctag == second.ctag
&& first.syncToken == second.syncToken;
&& first.syncToken == second.syncToken
&& first.readOnly == second.readOnly;
}

void tst_replyparser::parseAddressbookInformation()
Expand Down

0 comments on commit 9184b28

Please sign in to comment.