Merge branch 'master' of https://code.google.com/p/plexi.sharepoint
diff --git a/SiteData.wsdl b/SiteData.wsdl
index 75bc1ce..bc5cede 100644
--- a/SiteData.wsdl
+++ b/SiteData.wsdl
@@ -943,7 +943,7 @@
<s:complexType name='permissionsForACL'>
<s:sequence >
- <s:element name='permission' type='tns:permission' maxOccurs='unbounded' />
+ <s:element name='permission' type='tns:permission' minOccurs='0' maxOccurs='unbounded' />
</s:sequence>
</s:complexType>
diff --git a/lib/plexi b/lib/plexi
index a90492e..32f1a1c 160000
--- a/lib/plexi
+++ b/lib/plexi
@@ -1 +1 @@
-Subproject commit a90492e5cc03a58f97aad3f5c5fbc5b6e35eb489
+Subproject commit 32f1a1c24a61b3e97f7a9b97dc9a62b5e39c0b81
diff --git a/src/com/google/enterprise/adaptor/sharepoint/SharePointAdaptor.java b/src/com/google/enterprise/adaptor/sharepoint/SharePointAdaptor.java
index cc6f506..add21ef 100644
--- a/src/com/google/enterprise/adaptor/sharepoint/SharePointAdaptor.java
+++ b/src/com/google/enterprise/adaptor/sharepoint/SharePointAdaptor.java
@@ -187,6 +187,7 @@
= new ConcurrentSkipListMap<String, String>();
private final SiteDataFactory siteDataFactory;
private final HttpClient httpClient;
+ private boolean xmlValidation;
private NtlmAuthenticator ntlmAuthenticator;
public SharePointAdaptor() {
@@ -215,6 +216,7 @@
config.addKey("sharepoint.server", null);
config.addKey("sharepoint.username", null);
config.addKey("sharepoint.password", null);
+ config.addKey("sharepoint.xmlValidation", "true");
}
@Override
@@ -225,6 +227,8 @@
String username = config.getValue("sharepoint.username");
String password = context.getSensitiveValueDecoder().decodeValue(
config.getValue("sharepoint.password"));
+ xmlValidation = Boolean.parseBoolean(
+ config.getValue("sharepoint.xmlValidation"));
log.log(Level.CONFIG, "VirtualServer: {0}", virtualServer);
log.log(Level.CONFIG, "Username: {0}", username);
@@ -506,24 +510,28 @@
VirtualServer vs = getContentVirtualServer();
final long necessaryPermissionMask = LIST_ITEM_MASK;
- List<String> permitUsers = new ArrayList<String>();
- List<String> denyUsers = new ArrayList<String>();
+ // A PolicyUser is either a user or group, but we aren't provided with
+ // which. Thus, we treat PolicyUsers as both a user and a group in ACLs
+ // and understand that only one of the two entries will have an effect.
+ List<String> permitIds = new ArrayList<String>();
+ List<String> denyIds = new ArrayList<String>();
for (PolicyUser policyUser : vs.getPolicies().getPolicyUser()) {
// TODO(ejona): special case NT AUTHORITY\LOCAL SERVICE.
String loginName = policyUser.getLoginName();
long grant = policyUser.getGrantMask().longValue();
if ((necessaryPermissionMask & grant) == necessaryPermissionMask) {
- permitUsers.add(loginName);
+ permitIds.add(loginName);
}
long deny = policyUser.getDenyMask().longValue();
// If at least one necessary bit is masked, then deny user.
if ((necessaryPermissionMask & deny) != 0) {
- denyUsers.add(loginName);
+ denyIds.add(loginName);
}
}
response.setAcl(new Acl.Builder()
.setInheritanceType(Acl.InheritanceType.PARENT_OVERRIDES)
- .setPermitUsers(permitUsers).setDenyUsers(denyUsers).build());
+ .setPermitUsers(permitIds).setPermitGroups(permitIds)
+ .setDenyUsers(denyIds).setDenyGroups(denyIds).build());
response.setContentType("text/html");
HtmlResponseWriter writer = createHtmlResponseWriter(response);
@@ -1280,18 +1288,22 @@
log.entering("SiteDataClient", "getChangesContentDatabase",
new Object[] {contentDatabaseGuid, startChangeId});
final Holder<String> lastChangeId = new Holder<String>(startChangeId);
+ final Holder<String> lastLastChangeId = new Holder<String>();
final Holder<String> currentChangeId = new Holder<String>();
final Holder<Boolean> moreChanges = new Holder<Boolean>(true);
log.exiting("SiteDataClient", "getChangesContentDatabase");
return new CursorPaginator<SPContentDatabase, String>() {
@Override
public SPContentDatabase next() throws IOException {
- // SharePoint 2010 (at least sometimes) does not set
- // lastChangeId = currentChangeId when paging is complete, even
- // though this is a "MUST" requirement in the documentation.
- if (!moreChanges.value) {
+ // SharePoint 2010 sometimes does not set lastChangeId=currentChangeId
+ // nor moreChanges=false when paging is complete, even though both of
+ // these conditions are a "MUST" requirement in the documentation.
+ // Thus, we make sure that each call changes the lastChangeId.
+ if (!moreChanges.value
+ || lastChangeId.value.equals(lastLastChangeId.value)) {
return null;
}
+ lastLastChangeId.value = lastChangeId.value;
Holder<String> result = new Holder<String>();
siteData.getChanges(ObjectType.CONTENT_DATABASE, contentDatabaseGuid,
lastChangeId, currentChangeId, 15, result, moreChanges);
@@ -1315,7 +1327,9 @@
Source source = new StreamSource(new StringReader(xml));
try {
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
- unmarshaller.setSchema(schema);
+ if (xmlValidation) {
+ unmarshaller.setSchema(schema);
+ }
return unmarshaller.unmarshal(source, klass).getValue();
} catch (JAXBException ex) {
throw new XmlProcessingException(ex, xml);
@@ -1375,7 +1389,7 @@
static class FileInfo {
/** Non-null contents. */
private final InputStream contents;
- /** Non-null headers. */
+ /** Non-null headers. Alternates between header name and header value. */
private final List<String> headers;
private FileInfo(InputStream contents, List<String> headers) {
@@ -1387,6 +1401,10 @@
return contents;
}
+ public List<String> getHeaders() {
+ return headers;
+ }
+
public int getHeaderCount() {
return headers.size() / 2;
}
@@ -1430,6 +1448,10 @@
return this;
}
+ /**
+ * Sets the headers recieved as a response. List must alternate between
+ * header name and header value.
+ */
public Builder setHeaders(List<String> headers) {
if (headers == null) {
throw new NullPointerException();
@@ -1459,7 +1481,7 @@
public FileInfo issueGetRequest(URL url) throws IOException;
}
- private static class HttpClientImpl implements HttpClient {
+ static class HttpClientImpl implements HttpClient {
@Override
public FileInfo issueGetRequest(URL url) throws IOException {
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
@@ -1493,7 +1515,7 @@
public SiteDataSoap newSiteData(String endpoint);
}
- private static class SiteDataFactoryImpl implements SiteDataFactory {
+ static class SiteDataFactoryImpl implements SiteDataFactory {
private final Service siteDataService;
public SiteDataFactoryImpl() {
diff --git a/test/com/google/enterprise/adaptor/sharepoint/GetContentsResponse.java b/test/com/google/enterprise/adaptor/sharepoint/GetContentsResponse.java
index 7f820cf..d43c183 100644
--- a/test/com/google/enterprise/adaptor/sharepoint/GetContentsResponse.java
+++ b/test/com/google/enterprise/adaptor/sharepoint/GetContentsResponse.java
@@ -27,6 +27,8 @@
private String contentType;
private Metadata metadata = new Metadata();
private Acl acl;
+ private boolean secure;
+ private Date lastModified;
private List<URI> anchorUris = new ArrayList<URI>();
private List<String> anchorTexts = new ArrayList<String>();
private boolean notFound;
@@ -69,6 +71,16 @@
}
@Override
+ public void setSecure(boolean secure) {
+ this.secure = secure;
+ }
+
+ @Override
+ public void setLastModified(Date lastModified) {
+ this.lastModified = lastModified;
+ }
+
+ @Override
public void addAnchor(URI uri, String text) {
anchorUris.add(uri);
anchorTexts.add(text);
diff --git a/test/com/google/enterprise/adaptor/sharepoint/SharePointAdaptorTest.java b/test/com/google/enterprise/adaptor/sharepoint/SharePointAdaptorTest.java
index 9bf39f2..fdb76a8 100644
--- a/test/com/google/enterprise/adaptor/sharepoint/SharePointAdaptorTest.java
+++ b/test/com/google/enterprise/adaptor/sharepoint/SharePointAdaptorTest.java
@@ -256,10 +256,11 @@
+ "SiteCollection</a></li>"
+ "</ul></body></html>";
assertEquals(golden, responseString);
+ List<String> permit = Arrays.asList("GDC-PSL\\Administrator",
+ "GDC-PSL\\spuser1", "NT AUTHORITY\\LOCAL SERVICE");
assertEquals(new Acl.Builder()
.setInheritanceType(Acl.InheritanceType.PARENT_OVERRIDES)
- .setPermitUsers(Arrays.asList("GDC-PSL\\Administrator",
- "GDC-PSL\\spuser1", "NT AUTHORITY\\LOCAL SERVICE")).build(),
+ .setPermitUsers(permit).setPermitGroups(permit).build(),
response.getAcl());
}
@@ -3100,7 +3101,9 @@
setValue(currentChangeId, "1;0;4fb7dea1-2912-4927-9eda-1ea2f0977cf9;634"
+ "727056595000000;604");
setValue(getChangesResult, getChangesContentDatabase4fb);
- setValue(moreChanges, false);
+ // Purposefully make moreChanges=true even though there are no more
+ // pages, because SP 2010 has been known to do this.
+ setValue(moreChanges, true);
}
};
SiteDataFactory siteDataFactory = new SingleSiteDataFactory(siteData,
@@ -3331,6 +3334,22 @@
}
@Test
+ public void testDisabledValidation() throws Exception {
+ adaptor = new SharePointAdaptor(new UnsupportedSiteDataFactory(),
+ new UnsupportedHttpClient());
+ config.overrideKey("sharepoint.xmlValidation", "false");
+ adaptor.init(new MockAdaptorContext(config, null));
+ SharePointAdaptor.SiteDataClient client = adaptor.new SiteDataClient(
+ "http://localhost:1", "http://localhost:1",
+ new UnsupportedSiteData(), new UnsupportedCallable<MemberIdMapping>());
+ // Lacks required child element.
+ String xml = "<SPContentDatabase"
+ + " xmlns='http://schemas.microsoft.com/sharepoint/soap/'/>";
+ assertNotNull(client.jaxbParse(xml, SPContentDatabase.class));
+ }
+
+
+ @Test
public void testParseUnknownXml() throws Exception {
adaptor = new SharePointAdaptor(new UnsupportedSiteDataFactory(),
new UnsupportedHttpClient());