diff --git a/.gitignore b/.gitignore index 46aadbc..a90305d 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ DotCMIS/StyleCop.Cach DotCMIS/test-results/ DotCMISUnitTest/test-results/ test-results/ +packages/ diff --git a/DotCMIS/DotCMIS.csproj b/DotCMIS/DotCMIS.csproj index 0be8736..aa37972 100644 --- a/DotCMIS/DotCMIS.csproj +++ b/DotCMIS/DotCMIS.csproj @@ -1,187 +1,187 @@ - - - - Debug - AnyCPU - 8.0.30703 - 2.0 - {529EF90F-A34C-4426-A4F9-65113AC23841} - Library - Properties - DotCMIS - DotCMIS - 512 - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - bin\Debug\DotCMIS.XML - 1591 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - bin\Release\DotCMIS.XML - 1591 - - - true - - - dotcmis.snk - - - - False - ..\Newtonsoft.Json.dll - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - True - True - Reference.svcmap - - - - - - - - - - - - - - - Reference.svcmap - - - Reference.svcmap - - - Reference.svcmap - - - Reference.svcmap - - - Reference.svcmap - - - Reference.svcmap - - - Reference.svcmap - - - Reference.svcmap - - - Reference.svcmap - - - Reference.svcmap - - - Reference.svcmap - - - Reference.svcmap - - - Reference.svcmap - - - Reference.svcmap - - - Reference.svcmap - - - Reference.svcmap - - - Reference.svcmap - - - Reference.svcmap - - - - - - - - - - - - - - WCF Proxy Generator - Reference.cs - - - - - {181FE707-E161-4722-9F38-6AAAB6FAA106} - log4net.vs2010 - - - - + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {529EF90F-A34C-4426-A4F9-65113AC23841} + Library + Properties + DotCMIS + DotCMIS + 512 + v4.5 + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + bin\Debug\DotCMIS.XML + 1591 + false + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + bin\Release\DotCMIS.XML + 1591 + false + + + true + + + dotcmis.snk + + + + ..\..\..\packages\log4net.2.0.5\lib\net40-full\log4net.dll + + + ..\..\..\packages\Newtonsoft.Json.8.0.3\lib\net45\Newtonsoft.Json.dll + True + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + True + True + Reference.svcmap + + + + + + + + + + + + + + Reference.svcmap + + + Reference.svcmap + + + Reference.svcmap + + + Reference.svcmap + + + Reference.svcmap + + + Reference.svcmap + + + Reference.svcmap + + + Reference.svcmap + + + Reference.svcmap + + + Reference.svcmap + + + Reference.svcmap + + + Reference.svcmap + + + Reference.svcmap + + + Reference.svcmap + + + Reference.svcmap + + + Reference.svcmap + + + Reference.svcmap + + + Reference.svcmap + + + + + + + + + + + + + + + WCF Proxy Generator + Reference.cs + + + + \ No newline at end of file diff --git a/DotCMIS/DotCMIS.shfbproj b/DotCMIS/DotCMIS.shfbproj index 62f8875..5ee595a 100644 --- a/DotCMIS/DotCMIS.shfbproj +++ b/DotCMIS/DotCMIS.shfbproj @@ -1,4 +1,5 @@ - + + Documentation @@ -23,7 +24,7 @@ Apache Chemistry DotCMIS HtmlHelp1 MemberName - vs2005 + VS2013 Summary, Parameter, Returns, TypeParameter @@ -51,6 +52,18 @@ True False True + OnlyWarningsAndErrors + False + .NET Framework 3.5 + False + False + 2 + False + Standard + Blank + False + False + AboveNamespaces "); + int end = errorContent.IndexOf(@""); + + if (begin == -1 || end == -1 || begin > end) { + return null; + } + + begin += @"".Length; + return errorContent.Substring(begin, end - begin); + } + protected void ThrowLinkException(String repositoryId, String id, String rel, String type) { int index = GetLinkCache().CheckLink(repositoryId, id, rel, type); @@ -442,17 +494,24 @@ protected HttpUtils.Response Read(UrlBuilder url) // If Internal Server Error, output both request and response to the log for investigation. if (resp.StatusCode == HttpStatusCode.InternalServerError) { - StreamReader reader = new StreamReader(resp.Stream, Encoding.UTF8); - String responseString = reader.ReadToEnd(); - Logger.Warn("----------------------------------------------------------------------"); - Logger.Warn("Your CMIS server has returned: 500 Internal Server Error."); - Logger.Warn("This should never happen. It is a problem with your CMIS server."); - Logger.Warn("Please send this whole message (up to END) to your CMIS server support"); - Logger.Warn("GET request sent by CmisSync:"); - Logger.Warn(url); - Logger.Warn("Response received by CmisSync:"); - Logger.Warn(responseString); - Logger.Warn("--------------------------- END --------------------------------------"); + String responseString; + if (resp.Stream != null) + { + StreamReader reader = new StreamReader(resp.Stream, Encoding.UTF8); + responseString = reader.ReadToEnd(); + } + else { + responseString = "*NULL*"; + } + Logger.Warn("----------------------------------------------------------------------"+"\n"+ + "Your CMIS server has returned: 500 Internal Server Error."+"\n"+ + "This should never happen. It is a problem with your CMIS server."+"\n"+ + "Please send this whole message (up to END) to your CMIS server support"+"\n"+ + "GET request sent by CmisSync:"+"\n"+ + url+"\n"+ + "Response received by CmisSync:"+"\n"+ + responseString+"\n"+ + "--------------------------- END --------------------------------------"); } if (resp.StatusCode != HttpStatusCode.OK) @@ -483,6 +542,33 @@ protected HttpUtils.Response Post(UrlBuilder url, string contentType, HttpUtils. return resp; } + protected void PostAndConsume(UrlBuilder url, string contentType, HttpUtils.Output writer) + { + HttpUtils.Response resp = HttpUtils.InvokePOST(url, contentType, writer, Session); + + if (resp.StatusCode != HttpStatusCode.Created) + { + throw ConvertToCmisException(resp, null); + } + + if (resp.Stream != null) + { + Stream stream = resp.Stream; + try + { + byte[] buffer = new byte[8 * 1024]; + while (stream.Read(buffer, 0, buffer.Length) > 0) + { + } + } + finally + { + try { stream.Close(); } + catch (Exception) { } + } + } + } + protected HttpUtils.Response Put(UrlBuilder url, string contentType, HttpUtils.Output writer) { HttpUtils.Response resp = HttpUtils.InvokePUT(url, contentType, null, writer, Session); @@ -2241,6 +2327,14 @@ public void AppendContentStream(string repositoryId, ref string objectId, bool? } objectId = null; + if (resp.StatusCode == HttpStatusCode.Created) { + try { + objectId = new UrlParser(resp.Headers[HttpResponseHeader.Location.ToString()][0]).GetQueryValue(Parameters.ParamId); + } catch (NullReferenceException) { + } catch (IndexOutOfRangeException) { + } + } + changeToken = null; } @@ -2815,7 +2909,7 @@ public void AddObjectToFolder(string repositoryId, string objectId, string folde AtomEntryWriter entryWriter = new AtomEntryWriter(CreateIdObject(objectId)); // post addObjectToFolder request - Post(url, AtomPubConstants.MediatypeEntry, new HttpUtils.Output(entryWriter.Write)); + PostAndConsume(url, AtomPubConstants.MediatypeEntry, new HttpUtils.Output(entryWriter.Write)); } public void RemoveObjectFromFolder(string repositoryId, string objectId, string folderId, IExtensionsData extension) @@ -2840,7 +2934,7 @@ public void RemoveObjectFromFolder(string repositoryId, string objectId, string AtomEntryWriter entryWriter = new AtomEntryWriter(CreateIdObject(objectId)); // post removeObjectFromFolder request - Post(url, AtomPubConstants.MediatypeEntry, new HttpUtils.Output(entryWriter.Write)); + PostAndConsume(url, AtomPubConstants.MediatypeEntry, new HttpUtils.Output(entryWriter.Write)); } } @@ -2904,7 +2998,7 @@ public void ApplyPolicy(string repositoryId, string policyId, string objectId, I AtomEntryWriter entryWriter = new AtomEntryWriter(CreateIdObject(objectId)); // post applyPolicy request - Post(url, AtomPubConstants.MediatypeEntry, new HttpUtils.Output(entryWriter.Write)); + PostAndConsume(url, AtomPubConstants.MediatypeEntry, new HttpUtils.Output(entryWriter.Write)); } public void RemovePolicy(string repositoryId, string policyId, string objectId, IExtensionsData extension) diff --git a/DotCMIS/binding/binding-impl.cs b/DotCMIS/binding/binding-impl.cs index 549d330..440a9c7 100644 --- a/DotCMIS/binding/binding-impl.cs +++ b/DotCMIS/binding/binding-impl.cs @@ -548,9 +548,9 @@ public ITypeDefinition GetTypeDefinition(string repositoryId, string typeId, IEx /// /// SPI interface. /// - internal interface ICmisSpi : IDisposable + public interface ICmisSpi : IDisposable { - void initialize(BindingSession session); + void initialize(IBindingSession session); IRepositoryService GetRepositoryService(); INavigationService GetNavigationService(); IObjectService GetObjectService(); diff --git a/DotCMIS/binding/binding-intf.cs b/DotCMIS/binding/binding-intf.cs index 56526cd..eb1b856 100644 --- a/DotCMIS/binding/binding-intf.cs +++ b/DotCMIS/binding/binding-intf.cs @@ -111,7 +111,7 @@ protected virtual void HttpAuthenticate(HttpWebRequest request) string user = GetUser(); string password = GetPassword(); - request.AllowWriteStreamBuffering = false; + request.AllowWriteStreamBuffering = true; request.CookieContainer = Cookies; if (user != null || password != null) { @@ -391,7 +391,7 @@ private void CheckSessionParameters(IDictionary sessionParameter { string spiClass; - if (sessionParameters.TryGetValue(SessionParameter.BindingSpiClass, out spiClass)) + if (!sessionParameters.TryGetValue(SessionParameter.BindingSpiClass, out spiClass)) { throw new ArgumentException("SPI class entry (" + SessionParameter.BindingSpiClass + ") is missing!"); } diff --git a/DotCMIS/binding/browser/browser-converter.cs b/DotCMIS/binding/browser/browser-converter.cs index faa21a9..74ae9b3 100644 --- a/DotCMIS/binding/browser/browser-converter.cs +++ b/DotCMIS/binding/browser/browser-converter.cs @@ -1,3 +1,26 @@ +//----------------------------------------------------------------------- +// +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// +//----------------------------------------------------------------------- + using System; using System.Collections.Generic; using System.Linq; @@ -606,9 +629,13 @@ internal static IObjectList ConvertObjectList(JToken json, ClientTypeCache typeC IList objects = new List(); if (jsonChildren != null) { - foreach (JToken jsonChild in jsonChildren.Children()) + foreach (JToken obj in jsonChildren.Children()) { - objects.Add(ConvertObjectData(jsonChild, typeCache)); + if (obj[BrowserConstants.ObjectListObject] != null) { + objects.Add(ConvertObjectData(obj[BrowserConstants.ObjectListObject], typeCache)); + } else { + objects.Add(ConvertObjectData(obj, typeCache)); + } } } result.Objects = objects; diff --git a/DotCMIS/binding/browser/browser-writer.cs b/DotCMIS/binding/browser/browser-writer.cs index 957c5b5..0412de9 100644 --- a/DotCMIS/binding/browser/browser-writer.cs +++ b/DotCMIS/binding/browser/browser-writer.cs @@ -1,3 +1,26 @@ +//----------------------------------------------------------------------- +// +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// +//----------------------------------------------------------------------- + using System; using System.Collections.Generic; using System.Linq; diff --git a/DotCMIS/binding/browser/browser.cs b/DotCMIS/binding/browser/browser.cs index 5da12ed..10cbde2 100644 --- a/DotCMIS/binding/browser/browser.cs +++ b/DotCMIS/binding/browser/browser.cs @@ -1,3 +1,26 @@ +//----------------------------------------------------------------------- +// +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// +// +//----------------------------------------------------------------------- + using System; using System.Collections.Generic; using System.Linq; @@ -196,19 +219,23 @@ internal class CmisBrowserSpi : ICmisSpi private PolicyService PolicyService; private AclService AclService; - public void initialize(BindingSession session) + public void initialize(IBindingSession session) { - Session = session; + this.Session = session as BindingSession; + if (this.Session == null) + { + throw new ArgumentException("Invalid binding session!"); + } - RepositoryService = new RepositoryService(session); - NavigationService = new NavigationService(session); - ObjectService = new ObjectService(session); - VersioningService = new VersioningService(session); - DiscoveryService = new DiscoveryService(session); - MultiFilingService = new MultiFilingService(session); - RelationshipService = new RelationshipService(session); - PolicyService = new PolicyService(session); - AclService = new AclService(session); + RepositoryService = new RepositoryService(this.Session); + NavigationService = new NavigationService(this.Session); + ObjectService = new ObjectService(this.Session); + VersioningService = new VersioningService(this.Session); + DiscoveryService = new DiscoveryService(this.Session); + MultiFilingService = new MultiFilingService(this.Session); + RelationshipService = new RelationshipService(this.Session); + PolicyService = new PolicyService(this.Session); + AclService = new AclService(this.Session); } public IRepositoryService GetRepositoryService() @@ -304,43 +331,96 @@ protected string GetServiceUrl() return Session.GetValue(SessionParameter.BrowserUrl) as string; } - private RepositoryUrlCache GetRepositoryUrlCache() - { + private RepositoryUrlCache GetRepositoryUrlCache() { return BrowserBindingSessionUtility.GetRepositoryUrlCache(Session); } - protected CmisBaseException ConvertStatusCode(HttpStatusCode code, string message, string errorContent, Exception e) - { - switch (code) - { - case HttpStatusCode.Moved: - case HttpStatusCode.Found: - case HttpStatusCode.SeeOther: - case HttpStatusCode.TemporaryRedirect: - return new CmisConnectionException("Redirects are not supported (HTTP status code " + code + "): " - + message, errorContent, e); - case HttpStatusCode.BadRequest: - return new CmisInvalidArgumentException(message, errorContent, e); - case HttpStatusCode.Forbidden: - return new CmisPermissionDeniedException(message, errorContent, e); - case HttpStatusCode.NotFound: - return new CmisObjectNotFoundException(message, errorContent, e); - case HttpStatusCode.MethodNotAllowed: - return new CmisNotSupportedException(message, errorContent,e); - case HttpStatusCode.Conflict: - return new CmisConstraintException(message, errorContent, e); - default: - return new CmisRuntimeException(message, errorContent, e); - } - } - - protected HttpUtils.Response Read(UrlBuilder url) - { + protected CmisBaseException ConvertToCmisException(HttpUtils.Response resp, Exception e) { + string jsonError = null; + string message = resp.Message; + string errorContent = resp.ErrorContent; + try { + var jsonObject = (JObject.Parse(resp.ErrorContent) as JObject); + var jsonExceptionObject = jsonObject.GetValue(@"exception"); + var jsonMessageObject = jsonObject.GetValue(@"message"); + jsonError = jsonExceptionObject.ToString(); + message = jsonMessageObject.ToString(); + } catch(Exception) { + } + + CmisBaseException exception = null; + switch (resp.StatusCode) { + case HttpStatusCode.Moved: + case HttpStatusCode.Found: + case HttpStatusCode.SeeOther: + case HttpStatusCode.TemporaryRedirect: + exception = new CmisConnectionException("Redirects are not supported (HTTP status code " + resp.StatusCode + "): " + + message, errorContent, e); + break; + case HttpStatusCode.BadRequest: + if (string.Equals(jsonError, @"filterNotValid", StringComparison.OrdinalIgnoreCase)) { + exception = new CmisFilterNotValidException(message, errorContent, e); + } else { + exception = new CmisInvalidArgumentException(message, errorContent, e); + } + + break; + case HttpStatusCode.Forbidden: + if (string.Equals(jsonError, @"streamNotSupported", StringComparison.OrdinalIgnoreCase)) { + exception = new CmisStreamNotSupportedException(message, errorContent, e); + } else { + exception = new CmisPermissionDeniedException(message, errorContent, e); + } + + break; + case HttpStatusCode.NotFound: + exception = new CmisObjectNotFoundException(message, errorContent, e); + break; + case HttpStatusCode.MethodNotAllowed: + exception = new CmisNotSupportedException(message, errorContent,e); + break; + case HttpStatusCode.Conflict: + if (string.Equals(jsonError, @"nameConstraintViolation", StringComparison.OrdinalIgnoreCase)) { + exception = new CmisNameConstraintViolationException(message, errorContent, e); + } else if (string.Equals(jsonError, @"versioning", StringComparison.OrdinalIgnoreCase)) { + exception = new CmisVersioningException(message, errorContent, e); + } else if (string.Equals(jsonError, @"contentAlreadyExists", StringComparison.OrdinalIgnoreCase)) { + exception = new CmisContentAlreadyExistsException(message, errorContent, e); + } else if (string.Equals(jsonError, @"updateConflict", StringComparison.OrdinalIgnoreCase)) { + exception = new CmisUpdateConflictException(message, errorContent, e); + } else { + exception = new CmisConstraintException(message, errorContent, e); + } + + break; + case HttpStatusCode.InternalServerError: + if (string.Equals(jsonError, @"storage", StringComparison.OrdinalIgnoreCase)) { + exception = new CmisStorageException(message, errorContent, e); + } else { + exception = new CmisRuntimeException(message, errorContent, e); + } + + break; + case null: + exception = new CmisConnectionException(message, errorContent, resp.Exception); + break; + default: + exception = new CmisRuntimeException(message, errorContent, e); + break; + } + + foreach (var header in resp.Headers) { + exception.Data.Add(header.Key, header.Value); + } + + return exception; + } + + protected HttpUtils.Response Read(UrlBuilder url) { HttpUtils.Response resp = HttpUtils.InvokeGET(url, Session); - if (resp.StatusCode != HttpStatusCode.OK) - { - throw ConvertStatusCode(resp.StatusCode, resp.Message, resp.ErrorContent, null); + if (resp.StatusCode != HttpStatusCode.OK) { + throw ConvertToCmisException(resp, null); } return resp; @@ -352,7 +432,7 @@ protected HttpUtils.Response Post(UrlBuilder url, string contentType, HttpUtils. if (resp.StatusCode != HttpStatusCode.OK && resp.StatusCode != HttpStatusCode.Created) { - throw ConvertStatusCode(resp.StatusCode, resp.Message, resp.ErrorContent, null); + throw ConvertToCmisException(resp, null); } return resp; @@ -388,7 +468,11 @@ protected JObject ParseObject(Stream stream) { using (JsonTextReader reader = new JsonTextReader(new StreamReader(stream))) { - json = JToken.ReadFrom(reader); + try { + json = JToken.ReadFrom(reader); + } catch (WebException e) { + throw new CmisConnectionException(e.Message, e); + } } } finally @@ -410,7 +494,11 @@ protected JArray ParseArray(Stream stream) { using (JsonTextReader reader = new JsonTextReader(new StreamReader(stream))) { - json = JToken.ReadFrom(reader); + try { + json = JToken.ReadFrom(reader); + } catch (WebException e) { + throw new CmisConnectionException(e.Message, e); + } } } finally @@ -830,7 +918,7 @@ public string CreatePolicy(string repositoryId, IProperties properties, string f { UrlBuilder url = string.IsNullOrEmpty(folderId) ? GetRepositoryUrl(repositoryId) : GetObjectUrl(repositoryId, folderId); - FormDataWriter formData = new FormDataWriter(repositoryId); + FormDataWriter formData = new FormDataWriter(BrowserConstants.ActionCreatePolicy); formData.AddPropertiesParameters(properties); formData.AddPoliciesParameters(policies); formData.AddAddAcesParameters(addAces); @@ -855,7 +943,7 @@ public string CreateItem(string repositoryId, IProperties properties, string fol { UrlBuilder url = string.IsNullOrEmpty(folderId) ? GetRepositoryUrl(repositoryId) : GetObjectUrl(repositoryId, folderId); - FormDataWriter formData = new FormDataWriter(repositoryId); + FormDataWriter formData = new FormDataWriter(BrowserConstants.ActionCreateItem); formData.AddPropertiesParameters(properties); formData.AddPoliciesParameters(policies); formData.AddAddAcesParameters(addAces); @@ -963,7 +1051,7 @@ public IContentStream GetContentStream(string repositoryId, string objectId, str HttpUtils.Response resp = HttpUtils.InvokeGET(url, Session, offset, length); if (resp.StatusCode != HttpStatusCode.OK && resp.StatusCode != HttpStatusCode.PartialContent) { - throw ConvertStatusCode(resp.StatusCode, resp.Message, resp.ErrorContent, null); + throw ConvertToCmisException(resp, null); } string filename = null; diff --git a/DotCMIS/binding/converter.cs b/DotCMIS/binding/converter.cs index 8f69d93..5e6f694 100644 --- a/DotCMIS/binding/converter.cs +++ b/DotCMIS/binding/converter.cs @@ -1185,7 +1185,7 @@ public static cmisProperty Convert(IPropertyData property) cmisProperty result = null; IList values = property.Values; - int count = values.Count; + int count = values == null ? 0 : values.Count; switch (property.PropertyType) { diff --git a/DotCMIS/binding/http.cs b/DotCMIS/binding/http.cs index c7a0e18..83ced28 100644 --- a/DotCMIS/binding/http.cs +++ b/DotCMIS/binding/http.cs @@ -1,999 +1,1012 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Net; -using System.Security.Cryptography; -using System.Text; -using System.Threading; -using System.Reflection; -using System.Runtime.Serialization; - -using DotCMIS.Enums; -using DotCMIS.Exceptions; -using DotCMIS.Util; - -namespace DotCMIS.Binding -{ - public class HttpWebRequestResource : IDisposable - { - private static object ResourceLock = new object(); - private static HashSet ResourceSet = new HashSet(); - - private HttpWebRequest Request; - - public static void AbortAll() - { - lock (ResourceLock) { - foreach (HttpWebRequest request in ResourceSet) { - request.Abort (); - } - } - } - - public HttpWebRequestResource() - { - Request = null; - } - - public void StartResource(HttpWebRequest request) - { - lock (ResourceLock) { - if (Request != null) { - ResourceSet.Remove (Request); - } - Request = request; - ResourceSet.Add (Request); - } - } - - public void Dispose() - { - if (Request != null) { - lock (ResourceLock) { - ResourceSet.Remove (Request); - } - } - } - } - -} - -namespace DotCMIS.Binding.Impl -{ - internal static class HttpUtils - { - public delegate void Output(Stream stream); - - public static Response InvokeGET(UrlBuilder url, BindingSession session) - { - return Invoke(url, "GET", null, null, session, null, null, null); - } - - public static Response InvokeGET(UrlBuilder url, BindingSession session, long? offset, long? length) - { - return Invoke(url, "GET", null, null, session, offset, length, null); - } - - public static Response InvokePOST(UrlBuilder url, String contentType, Output writer, BindingSession session) - { - return Invoke(url, "POST", contentType, writer, session, null, null, null); - } - - public static Response InvokePUT(UrlBuilder url, String contentType, IDictionary headers, Output writer, BindingSession session) - { - return Invoke(url, "PUT", contentType, writer, session, null, null, headers); - } - - public static Response InvokeDELETE(UrlBuilder url, BindingSession session) - { - return Invoke(url, "DELETE", null, null, session, null, null, null); - } - - private static Response Invoke(UrlBuilder url, String method, String contentType, Output writer, BindingSession session, - long? offset, long? length, IDictionary headers) - { - Guid tag = Guid.NewGuid(); - string request = method + " " + url; - Stopwatch watch = new Stopwatch(); - watch.Start(); - using (HttpWebRequestResource resource = new HttpWebRequestResource ()) - { - try - { - // log before connect - Trace.WriteLineIf(DotCMISDebug.DotCMISSwitch.TraceInfo, string.Format("[{0}] starting {1}", tag.ToString(), request)); - - //Handles infrequent networking conditions - int retry = 0; - for(;;){ - - // create connection - HttpWebRequest conn = (HttpWebRequest)WebRequest.Create(url.Url); - conn.Method = method; - resource.StartResource(conn); - - // device management - string deviceIdentifier = session.GetValue(SessionParameter.DeviceIdentifier) as String; - if(deviceIdentifier != null) - { - conn.Headers.Add("Device-ID", deviceIdentifier); - } - - // user agent - string userAgent = session.GetValue(SessionParameter.UserAgent) as String; - conn.UserAgent = userAgent == null ? "Apache Chemistry DotCMIS" : userAgent; - - // timeouts - int connectTimeout = session.GetValue(SessionParameter.ConnectTimeout, -2); - if (connectTimeout >= -1) - { - conn.Timeout = connectTimeout; - } - - int readTimeout = session.GetValue(SessionParameter.ReadTimeout, -2); - if (readTimeout >= -1) - { - conn.ReadWriteTimeout = readTimeout; - } - - // set content type - if (contentType != null) - { - conn.ContentType = contentType; - } - - // set additional headers - if (headers != null) - { - foreach (KeyValuePair header in headers) - { - conn.Headers.Add(header.Key, header.Value); - } - } - - // authenticate - IAuthenticationProvider authProvider = session.GetAuthenticationProvider(); - if (authProvider != null) - { - conn.PreAuthenticate = true; - authProvider.Authenticate(conn); - } - - // range - if (offset != null && length != null) - { - if (offset < Int32.MaxValue && offset + length - 1 < Int32.MaxValue) - { - conn.AddRange((int)offset, (int)offset + (int)length - 1); - } - else - { - try - { - MethodInfo mi = conn.GetType().GetMethod("AddRange", new Type[] { typeof(Int64), typeof(Int64) }); - mi.Invoke(conn, new object[] { offset, offset + length - 1 }); - } - catch (Exception e) - { - throw new CmisInvalidArgumentException("Offset or length too big!", e); - } - } - } - else if (offset != null) - { - if (offset < Int32.MaxValue) - { - conn.AddRange((int)offset); - } - else - { - try - { - MethodInfo mi = conn.GetType().GetMethod("AddRange", new Type[] { typeof(Int64) }); - mi.Invoke(conn, new object[] { offset }); - } - catch (Exception e) - { - throw new CmisInvalidArgumentException("Offset too big!", e); - } - } - } - - // compression - string compressionFlag = session.GetValue(SessionParameter.Compression) as string; - if (compressionFlag != null && compressionFlag.ToLower().Equals("true")) - { - conn.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; - } - - // send data - if (writer != null) - { -#if __MonoCS__ // Unchunked upload for Mono, because of Mono bug 21135 - - // First write request to temporary file, inefficient but easier than rewriting large parts of DotCMIS - string tempFile = System.IO.Path.GetTempFileName(); - // Make the temporary file readable only by the user, since data might be confidential. - Mono.Unix.Native.Syscall.chmod(tempFile, - Mono.Unix.Native.FilePermissions.S_IWUSR | Mono.Unix.Native.FilePermissions.S_IRUSR); - using (var tempStream = new StreamWriter(tempFile)) - { - writer(tempStream.BaseStream); - } - - // Send the request to the server - FileStream tempFileStream = new FileStream(tempFile, FileMode.Open, FileAccess.Read); - conn.ContentLength = tempFileStream.Length; - Stream requestStream = conn.GetRequestStream(); - tempFileStream.CopyTo(requestStream); - requestStream.Close(); - - // Remove temporary file - tempFileStream.Close(); - File.Delete(tempFile); -#else - conn.SendChunked = true; - Stream requestStream = conn.GetRequestStream(); - writer(requestStream); - requestStream.Close(); -#endif - } - else - { -#if __MonoCS__ - //around for MONO HTTP DELETE issue - //http://stackoverflow.com/questions/11785597/monotouch-iphone-call-to-httpwebrequest-getrequeststream-connects-to-server - if (method == "DELETE") - { - conn.ContentLength = 0; - Stream requestStream = conn.GetRequestStream(); - requestStream.Close(); - } -#endif - } - - // connect - try - { - HttpWebResponse response = (HttpWebResponse)conn.GetResponse(); - - if (authProvider != null) - { - authProvider.HandleResponse(response); - } - watch.Stop(); - Trace.WriteLineIf(DotCMISDebug.DotCMISSwitch.TraceInfo, string.Format("[{0}] received response after {1} ms", tag.ToString(), watch.ElapsedMilliseconds.ToString())); - - return new Response(response); - } - catch (WebException we) - { - if (method != "GET" || !ExceptionFixabilityDecider.CanExceptionBeFixedByRetry(we) || retry == 5) { - watch.Stop(); - Trace.WriteLineIf(DotCMISDebug.DotCMISSwitch.TraceInfo, string.Format("[{0}] received response after {1} ms", tag.ToString(), watch.ElapsedMilliseconds.ToString())); - return new Response(we); - } - - retry++; - watch.Stop(); - Thread.Sleep(50); - watch.Start(); - Trace.WriteLineIf(DotCMISDebug.DotCMISSwitch.TraceInfo, string.Format("[{0}] {1} retry No {2}", tag.ToString(), we.Message, retry.ToString())); - } - } - } - catch (Exception e) - { - watch.Stop(); - Trace.WriteLineIf(DotCMISDebug.DotCMISSwitch.TraceInfo, string.Format("[{0}] Cannot access {1}: {2} after {3} ms", tag.ToString(), request, e.Message, watch.ElapsedMilliseconds)); - throw new CmisConnectionException("Cannot access " + url + ": " + e.Message, e); - } - } - } - - internal class Response - { - private readonly WebResponse response; - public HttpStatusCode StatusCode { get; private set; } - public string Message { get; private set; } - public Stream Stream { get; private set; } - public string ErrorContent { get; private set; } - public string ContentType { get; private set; } - public long? ContentLength { get; private set; } - public Dictionary Headers { get; private set; } - - public Response(HttpWebResponse httpResponse) - { - this.response = httpResponse; - this.ExtractHeader(); - StatusCode = httpResponse.StatusCode; - Message = httpResponse.StatusDescription; - ContentType = httpResponse.ContentType; - ContentLength = httpResponse.ContentLength == -1 ? null : (long?)httpResponse.ContentLength; - string contentTransferEncoding = httpResponse.Headers["Content-Transfer-Encoding"]; - bool isBase64 = contentTransferEncoding != null && contentTransferEncoding.Equals("base64", StringComparison.CurrentCultureIgnoreCase); - Headers = new Dictionary(); - foreach (string key in httpResponse.Headers.AllKeys) - { - string[] values = httpResponse.Headers.GetValues(key); - Headers.Add(key, values); - } - - if (httpResponse.StatusCode == HttpStatusCode.OK || - httpResponse.StatusCode == HttpStatusCode.Created || - httpResponse.StatusCode == HttpStatusCode.NonAuthoritativeInformation || - httpResponse.StatusCode == HttpStatusCode.PartialContent) - { - if (isBase64) - { - Stream = new BufferedStream(new CryptoStream(httpResponse.GetResponseStream(), new FromBase64Transform(), CryptoStreamMode.Read), 64 * 1024); - } - else - { - Stream = new BufferedStream(httpResponse.GetResponseStream(), 64 * 1024); - } - } - else - { - try { httpResponse.Close(); } - catch (Exception) { } - } - } - - public Response(WebException exception) - { - response = exception.Response; - this.ExtractHeader(); - HttpWebResponse httpResponse = response as HttpWebResponse; - if (httpResponse != null) - { - StatusCode = httpResponse.StatusCode; - Message = httpResponse.StatusDescription; - ContentType = httpResponse.ContentType; - - if (ContentType != null && ContentType.ToLower().StartsWith("text/")) - { - StringBuilder sb = new StringBuilder(); - - using (StreamReader sr = new StreamReader(httpResponse.GetResponseStream())) - { - string s; - while ((s = sr.ReadLine()) != null) - { - sb.Append(s); - sb.Append('\n'); - } - } - - ErrorContent = sb.ToString(); - } - } - else - { - StatusCode = HttpStatusCode.InternalServerError; - Message = exception.Status.ToString(); - } - - try { response.Close(); } - catch (Exception) { } - } - - public void CloseStream() - { - if (Stream != null) - { - Stream.Close(); - } - } - - private void ExtractHeader() { - this.Headers = new Dictionary(); - for (int i = 0; i < this.response.Headers.Count; ++i) { - this.Headers.Add(this.response.Headers.GetKey(i), this.response.Headers.GetValues(i)); - } - } - } - } - - public static class ExceptionFixabilityDecider { - public static bool CanExceptionBeFixedByRetry(WebException we) - { - if(!(we.Response is HttpWebResponse)){ - return true; - } - return CanExceptionStatusCodeBeFixedByRetry((we.Response as HttpWebResponse).StatusCode); - } - - public static bool CanExceptionStatusCodeBeFixedByRetry(HttpStatusCode code) - { - if(code == HttpStatusCode.NotFound || code == HttpStatusCode.Forbidden) { - return false; - } - return true; - } - } - - internal class UrlBuilder - { - private UriBuilder uri; - - public Uri Url - { - get { return uri.Uri; } - } - - public UrlBuilder(string url) - { - if (url == null) - { - throw new ArgumentNullException("url"); - } - - uri = new UriBuilder(url); - } - - public UrlBuilder AddPath(string path) - { - if (path == null || path.Length == 0) - { - return this; - } - uri.Path = uri.Path.TrimEnd('/', '\\'); - path = path.TrimStart('/', '\\'); - uri.Path = uri.Path + "/" + path; - return this; - } - - public UrlBuilder AddParameter(string name, object value) - { - if ((name == null) || (value == null)) - { - return this; - } - - string valueStr = Uri.EscapeDataString(UrlBuilder.NormalizeParameter(value)); - - if (uri.Query != null && uri.Query.Length > 1) - { - uri.Query = uri.Query.Substring(1) + "&" + name + "=" + valueStr; - } - else - { - uri.Query = name + "=" + valueStr; - } - - return this; - } - - public static string NormalizeParameter(object value) - { - if (value == null) - { - return null; - } - else if (value is Enum) - { - return ((Enum)value).GetCmisValue(); - } - else if (value is bool) - { - return (bool)value ? "true" : "false"; - } - - return value.ToString(); - } - - public override string ToString() - { - return Url.ToString(); - } - } - - internal class UrlParser - { - private Uri uri; - - public UrlParser(string url) - { - if (url == null) - { - throw new ArgumentNullException("url"); - } - - uri = new Uri(url); - } - - public string GetQueryValue(string name) - { - string query = uri.Query; - if (!query.Contains("?")) - { - return null; - } - query = query.Substring(query.IndexOf('?') + 1); - - foreach (string segment in query.Split('&')) - { - string[] parts = segment.Split('='); - if(parts[0] != name) - { - continue; - } - if (parts.Length == 1) - { - return string.Empty; - } - if (parts.Length == 2) - { - return Uri.UnescapeDataString(parts[1]); - } - return null; - } - return null; - } - } - - internal class MimeHelper - { - public const string ContentDisposition = "Content-Disposition"; - public const string DispositionAttachment = "attachment"; - public const string DispositionFilename = "filename"; - public const string DispositionName = "name"; - public const string DispositionFormDataContent = "form-data; " + DispositionName + "=\"content\""; - - private const string MIMESpecials = "()<>@,;:\\\"/[]?=" + "\t "; - private const string RFC2231Specials = "*'%" + MIMESpecials; - private static char[] HexDigits = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; - private static byte[] HexDecode = new byte[0x80]; - - - public static string EncodeContentDisposition(string disposition, string filename) - { - if (disposition == null) - { - disposition = DispositionAttachment; - } - return disposition + EncodeRFC2231(DispositionFilename, filename); - } - - protected static string EncodeRFC2231(string key, string value) - { - StringBuilder buf = new StringBuilder(); - bool encoded = EncodeRFC2231value(value, buf); - if (encoded) - { - return "; " + key + "*=" + buf.ToString(); - } - else - { - return "; " + key + "=" + value; - } - } - - protected static bool EncodeRFC2231value(string value, StringBuilder buf) - { - buf.Append("UTF-8"); - buf.Append("''"); // no language - byte[] bytes; - try - { - bytes = UTF8Encoding.UTF8.GetBytes(value); - } - catch (Exception) - { - return true; - } - - bool encoded = false; - for (int i = 0; i < bytes.Length; i++) - { - int ch = bytes[i] & 0xff; - if (ch <= 32 || ch >= 127 || RFC2231Specials.IndexOf((char)ch) != -1) - { - buf.Append('%'); - buf.Append(HexDigits[ch >> 4]); - buf.Append(HexDigits[ch & 0xf]); - encoded = true; - } - else - { - buf.Append((char)ch); - } - } - return encoded; - } - - protected static string DecodeRFC2231value(string value) - { - int q1 = value.IndexOf('\''); - if (q1 == -1) - { - return value; - } - string mimeCharset = value.Substring(0, q1); - int q2 = value.IndexOf('\'', q1 + 1); - if (q2 == -1) - { - return value; - } - byte[] bytes = FromHex(value.Substring(q2 + 1)); - try - { - return UTF8Encoding.UTF8.GetString(bytes); - } - catch (Exception) - { - return value; - } - } - - protected static byte[] FromHex(string data) - { - using (MemoryStream stream = new MemoryStream()) - { - for (int i = 0; i < HexDigits.Length; i++) - { - HexDecode[(int)HexDigits[i]] = (byte)i; - HexDecode[(int)Char.ToLower(HexDigits[i])] = (byte)i; - } - - for (int i = 0; i < data.Length; ) - { - char c = data[i++]; - if (c == '%') - { - if (i > data.Length - 2) - { - break; - } - byte b1 = HexDecode[data[i++] & 0x7f]; - byte b2 = HexDecode[data[i++] & 0x7f]; - stream.WriteByte((byte)((b1 << 4) | b2)); - } - else - { - stream.WriteByte((byte)c); - } - - } - return stream.ToArray(); - } - } - - public static string DecodeContentDisposition(string value, Dictionary parameters) - { - try - { - HeaderTokenizer tokenizer = new HeaderTokenizer(value); - Token token = tokenizer.Next(); - if (token.Type != Token.Atomic) - { - return null; - } - string disposition = token.Value; - - string remainder = tokenizer.Remainder; - if (remainder != null) - { - GetParameters(remainder, parameters); - } - - return disposition; - } - catch (ParseException) - { - return null; - } - } - - private static void GetParameters(string list, Dictionary parameters) - { - HeaderTokenizer tokenizer = new HeaderTokenizer(list); - while (true) - { - Token token = tokenizer.Next(); - switch (token.Type) - { - case Token.EOF: - return; - case ';': - token = tokenizer.Next(); - if (token.Type == Token.EOF) - { - return; - } - if (token.Type != Token.Atomic) - { - throw new ParseException("Invalid parameter name: " + token.Value); - } - string name = token.Value.ToLower(new System.Globalization.CultureInfo("en")); - token = tokenizer.Next(); - if (token.Type != '=') - { - throw new ParseException("Missing '='"); - } - token = tokenizer.Next(); - if (token.Type != Token.Atomic && token.Type != Token.QuotedString) - { - throw new ParseException("Invalid parameter value: " + token.Value); - } - string value = token.Value; - if (name.EndsWith("*")) - { - name = name.Substring(0, name.Length - 1); - value = DecodeRFC2231value(value); - } - parameters[name] = value; - break; - default: - throw new ParseException("Missing ';'"); - } - } - } - - [Serializable] - public class ParseException : FormatException - { - public ParseException(string message) - : base(message) - { - } - - public ParseException() - { - } - - public ParseException(string message, Exception inner) - : base(message, inner) - { - } - - protected ParseException(SerializationInfo info, StreamingContext context) - : base(info, context) - { - } - } - - protected class Token - { - public const int Atomic = -1; - public const int QuotedString = -2; - public const int Comment = -3; - public const int EOF = -4; - - public int Type { get; private set; } - public string Value { get; private set; } - - public Token(int type, string value) - { - Type = type; - Value = value; - } - } - - protected class HeaderTokenizer - { - private static readonly Token EOF = new Token(Token.EOF, null); - - private readonly string Header; - private readonly string Delimiters; - private readonly bool SkipComments; - private int pos; - - public HeaderTokenizer(string header) - : this(header, MIMESpecials, true) - { - } - - protected HeaderTokenizer(string header, string delimiters, bool skipComments) - { - Header = header; - Delimiters = delimiters; - SkipComments = skipComments; - } - - public string Remainder - { - get - { - if (pos >= Header.Length) - { - return null; - } - else - { - return Header.Substring(pos); - } - } - } - - public Token Next() - { - return ReadToken(); - } - - private Token ReadToken() - { - while (pos < Header.Length && char.IsWhiteSpace(Header[pos])) - { - ++pos; - } - - if (pos >= Header.Length) - { - return EOF; - } - - char c = Header[pos]; - if (c == '(') - { - Token comment = ReadComment(); - if (SkipComments) - { - return ReadToken(); - } - else - { - return comment; - } - } - else if (c == '\"') - { - return ReadQuotedString(); - } - else if (c < 32 || c >= 127 || Delimiters.Contains(new string(c, 1))) - { - pos++; - return new Token((int)c, new string(c, 1)); - } - else - { - return ReadAtomicToken(); - } - } - - private Token ReadAtomicToken() - { - int start = pos; - while (++pos < Header.Length) - { - char c = Header[pos]; - if (Delimiters.Contains(new string(c, 1)) || c < 32 || c >= 127) - { - break; - } - } - return new Token(Token.Atomic, Header.Substring(start, pos - start)); - } - - private Token ReadQuotedString() - { - int start = pos + 1; - bool requireEscape = false; - while (++pos < Header.Length) - { - char c = Header[pos]; - if (c == '"') - { - string value; - if (requireEscape) - { - value = GetEscapedValue(start, pos - start); - } - else - { - value = Header.Substring(start, pos - start); - } - return new Token(Token.Comment, value); - } - else if (c == '\\') - { - pos++; - requireEscape = true; - } - else if (c == '\r') - { - requireEscape = true; - } - } - throw new ParseException("Missing '\"'"); - } - - private Token ReadComment() - { - int start = pos + 1; - int nesting = 1; - bool requireEscape = false; - while (++pos < Header.Length) - { - char c = Header[pos]; - if (c == ')') - { - nesting--; - if (nesting == 0) - { - break; - } - } - else if (c == '(') - { - nesting++; - } - else if (c == '\\') - { - pos++; - requireEscape = true; - } - else if (c == '\r') - { - requireEscape = true; - } - } - if (nesting != 0) - { - throw new ParseException("Unbalanced comments"); - } - string value; - if (requireEscape) - { - value = GetEscapedValue(start, pos); - } - else - { - value = Header.Substring(start, pos - start); - } - pos++; - return new Token(Token.Comment, value); - } - - private string GetEscapedValue(int start, int end) - { - StringBuilder value = new StringBuilder(); - for (int i = start; i < end; i++) - { - char c = Header[i]; - if (c == '\\') - { - i++; - if (i == end) - { - throw new ParseException("Invalid escape character"); - } - value.Append(Header[i]); - } - else if (c == '\r') - { - if (i < end - 1 && Header[i + 1] == '\n') - { - i++; - } - } - else - { - value.Append(Header[i]); - } - } - return value.ToString(); - } - } - } -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Net; +using System.Security.Cryptography; +using System.Text; +using System.Threading; +using System.Reflection; +using System.Runtime.Serialization; + +using DotCMIS.Enums; +using DotCMIS.Exceptions; +using DotCMIS.Util; + +namespace DotCMIS.Binding +{ + public class HttpWebRequestResource : IDisposable + { + private static object ResourceLock = new object(); + private static HashSet ResourceSet = new HashSet(); + + private HttpWebRequest Request; + + public static void AbortAll() + { + lock (ResourceLock) { + foreach (HttpWebRequest request in ResourceSet) { + request.Abort (); + } + } + } + + public HttpWebRequestResource() + { + Request = null; + } + + public void StartResource(HttpWebRequest request) + { + lock (ResourceLock) { + if (Request != null) { + ResourceSet.Remove (Request); + } + Request = request; + ResourceSet.Add (Request); + } + } + + public void Dispose() + { + if (Request != null) { + lock (ResourceLock) { + ResourceSet.Remove (Request); + } + } + } + } + +} + +namespace DotCMIS.Binding.Impl +{ + internal static class HttpUtils + { + public delegate void Output(Stream stream); + + public static Response InvokeGET(UrlBuilder url, BindingSession session) + { + return Invoke(url, "GET", null, null, session, null, null, null); + } + + public static Response InvokeGET(UrlBuilder url, BindingSession session, long? offset, long? length) + { + return Invoke(url, "GET", null, null, session, offset, length, null); + } + + public static Response InvokePOST(UrlBuilder url, String contentType, Output writer, BindingSession session) + { + return Invoke(url, "POST", contentType, writer, session, null, null, null); + } + + public static Response InvokePUT(UrlBuilder url, String contentType, IDictionary headers, Output writer, BindingSession session) + { + return Invoke(url, "PUT", contentType, writer, session, null, null, headers); + } + + public static Response InvokeDELETE(UrlBuilder url, BindingSession session) + { + return Invoke(url, "DELETE", null, null, session, null, null, null); + } + + private static Response Invoke(UrlBuilder url, String method, String contentType, Output writer, BindingSession session, + long? offset, long? length, IDictionary headers) + { + Guid tag = Guid.NewGuid(); + string request = method + " " + url; + Stopwatch watch = new Stopwatch(); + watch.Start(); + int maxRetries = 5; + int.TryParse(session.GetValue(SessionParameter.MaximumRequestRetries, "5") as String, out maxRetries); + using (HttpWebRequestResource resource = new HttpWebRequestResource ()) + { + try + { + // log before connect + Trace.WriteLineIf(DotCMISDebug.DotCMISSwitch.TraceInfo, string.Format("[{0}] starting {1}", tag.ToString(), request)); + + //Handles infrequent networking conditions + int retry = 0; + for(;;){ + + // create connection + HttpWebRequest conn = (HttpWebRequest)WebRequest.Create(url.Url); + conn.Method = method; + resource.StartResource(conn); + + // device management + string deviceIdentifier = session.GetValue(SessionParameter.DeviceIdentifier) as String; + if(deviceIdentifier != null) + { + conn.Headers.Add("Device-ID", deviceIdentifier); + } + + // user agent + string userAgent = session.GetValue(SessionParameter.UserAgent) as String; + conn.UserAgent = userAgent == null ? "Apache Chemistry DotCMIS" : userAgent; + + // timeouts + int connectTimeout = session.GetValue(SessionParameter.ConnectTimeout, -2); + if (connectTimeout >= -1) + { + conn.Timeout = connectTimeout; + } + + int readTimeout = session.GetValue(SessionParameter.ReadTimeout, -2); + if (readTimeout >= -1) + { + conn.ReadWriteTimeout = readTimeout; + } + + // set content type + if (contentType != null) + { + conn.ContentType = contentType; + } + + // set additional headers + if (headers != null) + { + foreach (KeyValuePair header in headers) + { + conn.Headers.Add(header.Key, header.Value); + } + } + + // authenticate + IAuthenticationProvider authProvider = session.GetAuthenticationProvider(); + if (authProvider != null) + { + conn.PreAuthenticate = true; + authProvider.Authenticate(conn); + } + + // range + if (offset != null && length != null) + { + if (offset < Int32.MaxValue && offset + length - 1 < Int32.MaxValue) + { + conn.AddRange((int)offset, (int)offset + (int)length - 1); + } + else + { + try + { + MethodInfo mi = conn.GetType().GetMethod("AddRange", new Type[] { typeof(Int64), typeof(Int64) }); + mi.Invoke(conn, new object[] { offset, offset + length - 1 }); + } + catch (Exception e) + { + throw new CmisInvalidArgumentException("Offset or length too big!", e); + } + } + } + else if (offset != null) + { + if (offset < Int32.MaxValue) + { + conn.AddRange((int)offset); + } + else + { + try + { + MethodInfo mi = conn.GetType().GetMethod("AddRange", new Type[] { typeof(Int64) }); + mi.Invoke(conn, new object[] { offset }); + } + catch (Exception e) + { + throw new CmisInvalidArgumentException("Offset too big!", e); + } + } + } + + // compression + string compressionFlag = session.GetValue(SessionParameter.Compression) as string; + if (compressionFlag != null && compressionFlag.ToLower().Equals("true")) + { + conn.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; + } + + // send data + if (writer != null) + { +#if __MonoCS__ // Unchunked upload for Mono, because of Mono bug 21135 + + // First write request to temporary file, inefficient but easier than rewriting large parts of DotCMIS + string tempFile = System.IO.Path.GetTempFileName(); + // Make the temporary file readable only by the user, since data might be confidential. + Mono.Unix.Native.Syscall.chmod(tempFile, + Mono.Unix.Native.FilePermissions.S_IWUSR | Mono.Unix.Native.FilePermissions.S_IRUSR); + using (var tempStream = new StreamWriter(tempFile)) + { + writer(tempStream.BaseStream); + } + + // Send the request to the server + FileStream tempFileStream = new FileStream(tempFile, FileMode.Open, FileAccess.Read); + conn.ContentLength = tempFileStream.Length; + Stream requestStream = conn.GetRequestStream(); + tempFileStream.CopyTo(requestStream); + requestStream.Close(); + + // Remove temporary file + tempFileStream.Close(); + File.Delete(tempFile); +#else + conn.SendChunked = true; + Stream requestStream = conn.GetRequestStream(); + writer(requestStream); + requestStream.Close(); +#endif + } + else + { +#if __MonoCS__ + //around for MONO HTTP DELETE issue + //http://stackoverflow.com/questions/11785597/monotouch-iphone-call-to-httpwebrequest-getrequeststream-connects-to-server + if (method == "DELETE") + { + conn.ContentLength = 0; + Stream requestStream = conn.GetRequestStream(); + requestStream.Close(); + } +#endif + } + + // connect + try + { + HttpWebResponse response = (HttpWebResponse)conn.GetResponse(); + + if (authProvider != null) + { + authProvider.HandleResponse(response); + } + watch.Stop(); + Trace.WriteLineIf(DotCMISDebug.DotCMISSwitch.TraceInfo, string.Format("[{0}] received response after {1} ms", tag.ToString(), watch.ElapsedMilliseconds.ToString())); + + return new Response(response); + } + catch (WebException we) + { + if (method != "GET" || !ExceptionFixabilityDecider.CanExceptionBeFixedByRetry(we) || retry == maxRetries) { + watch.Stop(); + Trace.WriteLineIf(DotCMISDebug.DotCMISSwitch.TraceInfo, string.Format("[{0}] received response after {1} ms", tag.ToString(), watch.ElapsedMilliseconds.ToString())); + if (we.Response != null) + { + return new Response(we); + } + else + { + throw; + } + } + + retry++; + watch.Stop(); + Thread.Sleep(50); + watch.Start(); + Trace.WriteLineIf(DotCMISDebug.DotCMISSwitch.TraceInfo, string.Format("[{0}] {1} retry No {2}", tag.ToString(), we.Message, retry.ToString())); + } + } + } + catch (Exception e) + { + watch.Stop(); + Trace.WriteLineIf(DotCMISDebug.DotCMISSwitch.TraceInfo, string.Format("[{0}] Cannot access {1}: {2} after {3} ms", tag.ToString(), request, e.Message, watch.ElapsedMilliseconds)); + throw new CmisConnectionException("Cannot access " + url + ": " + e.Message, e); + } + } + } + + internal class Response + { + private readonly WebResponse response; + public WebException Exception { get; private set; } + public HttpStatusCode? StatusCode { get; private set; } + public string Message { get; private set; } + public Stream Stream { get; private set; } + public string ErrorContent { get; private set; } + public string ContentType { get; private set; } + public long? ContentLength { get; private set; } + public Dictionary Headers { get; private set; } + + public Response(HttpWebResponse httpResponse) + { + this.response = httpResponse; + this.ExtractHeader(); + StatusCode = httpResponse.StatusCode; + Message = httpResponse.StatusDescription; + ContentType = httpResponse.ContentType; + ContentLength = httpResponse.ContentLength == -1 ? null : (long?)httpResponse.ContentLength; + string contentTransferEncoding = httpResponse.Headers["Content-Transfer-Encoding"]; + bool isBase64 = contentTransferEncoding != null && contentTransferEncoding.Equals("base64", StringComparison.CurrentCultureIgnoreCase); + Headers = new Dictionary(); + foreach (string key in httpResponse.Headers.AllKeys) + { + string[] values = httpResponse.Headers.GetValues(key); + Headers.Add(key, values); + } + + if (httpResponse.StatusCode == HttpStatusCode.OK || + httpResponse.StatusCode == HttpStatusCode.Created || + httpResponse.StatusCode == HttpStatusCode.NonAuthoritativeInformation || + httpResponse.StatusCode == HttpStatusCode.PartialContent) + { + if (isBase64) + { + Stream = new BufferedStream(new CryptoStream(httpResponse.GetResponseStream(), new FromBase64Transform(), CryptoStreamMode.Read), 64 * 1024); + } + else + { + Stream = new BufferedStream(httpResponse.GetResponseStream(), 64 * 1024); + } + } + else + { + try { httpResponse.Close(); } + catch (Exception) { } + } + } + + public Response(WebException exception) + { + this.response = exception.Response; + this.Exception = exception; + this.ExtractHeader(); + HttpWebResponse httpResponse = response as HttpWebResponse; + if (httpResponse != null) + { + StatusCode = httpResponse.StatusCode; + Message = httpResponse.StatusDescription; + ContentType = httpResponse.ContentType; + + if (ContentType != null && (ContentType.ToLower().StartsWith("text/") || ContentType.ToLower().StartsWith("application/json;"))) + { + StringBuilder sb = new StringBuilder(); + + using (StreamReader sr = new StreamReader(httpResponse.GetResponseStream())) + { + string s; + while ((s = sr.ReadLine()) != null) + { + sb.Append(s); + sb.Append('\n'); + } + } + + ErrorContent = sb.ToString(); + } + } + else + { + StatusCode = null; + Message = exception.Status.ToString(); + } + + try { response.Close(); } + catch (Exception) { } + } + + public void CloseStream() + { + if (Stream != null) + { + Stream.Close(); + } + } + + private void ExtractHeader() { + this.Headers = new Dictionary(); + if (this.response != null && this.response.Headers != null) { + for (int i = 0; i < this.response.Headers.Count; ++i) { + this.Headers.Add(this.response.Headers.GetKey(i), this.response.Headers.GetValues(i)); + } + } + } + } + } + + public static class ExceptionFixabilityDecider { + public static bool CanExceptionBeFixedByRetry(WebException we) + { + if(!(we.Response is HttpWebResponse)){ + return true; + } + return CanExceptionStatusCodeBeFixedByRetry((we.Response as HttpWebResponse).StatusCode); + } + + public static bool CanExceptionStatusCodeBeFixedByRetry(HttpStatusCode code) + { + if(code == HttpStatusCode.NotFound || code == HttpStatusCode.Forbidden) { + return false; + } + return true; + } + } + + internal class UrlBuilder + { + private UriBuilder uri; + + public Uri Url + { + get { return uri.Uri; } + } + + public UrlBuilder(string url) + { + if (url == null) + { + throw new ArgumentNullException("url"); + } + + uri = new UriBuilder(url); + } + + public UrlBuilder AddPath(string path) + { + if (path == null || path.Length == 0) + { + return this; + } + uri.Path = uri.Path.TrimEnd('/', '\\'); + path = path.TrimStart('/', '\\'); + uri.Path = uri.Path + "/" + path; + return this; + } + + public UrlBuilder AddParameter(string name, object value) + { + if ((name == null) || (value == null)) + { + return this; + } + + string valueStr = Uri.EscapeDataString(UrlBuilder.NormalizeParameter(value)); + + if (uri.Query != null && uri.Query.Length > 1) + { + uri.Query = uri.Query.Substring(1) + "&" + name + "=" + valueStr; + } + else + { + uri.Query = name + "=" + valueStr; + } + + return this; + } + + public static string NormalizeParameter(object value) + { + if (value == null) + { + return null; + } + else if (value is Enum) + { + return ((Enum)value).GetCmisValue(); + } + else if (value is bool) + { + return (bool)value ? "true" : "false"; + } + + return value.ToString(); + } + + public override string ToString() + { + return Url.ToString(); + } + } + + internal class UrlParser + { + private Uri uri; + + public UrlParser(string url) + { + if (url == null) + { + throw new ArgumentNullException("url"); + } + + uri = new Uri(url); + } + + public string GetQueryValue(string name) + { + string query = uri.Query; + if (!query.Contains("?")) + { + return null; + } + query = query.Substring(query.IndexOf('?') + 1); + + foreach (string segment in query.Split('&')) + { + string[] parts = segment.Split('='); + if(parts[0] != name) + { + continue; + } + if (parts.Length == 1) + { + return string.Empty; + } + if (parts.Length == 2) + { + return Uri.UnescapeDataString(parts[1]); + } + return null; + } + return null; + } + } + + internal class MimeHelper + { + public const string ContentDisposition = "Content-Disposition"; + public const string DispositionAttachment = "attachment"; + public const string DispositionFilename = "filename"; + public const string DispositionName = "name"; + public const string DispositionFormDataContent = "form-data; " + DispositionName + "=\"content\""; + + private const string MIMESpecials = "()<>@,;:\\\"/[]?=" + "\t "; + private const string RFC2231Specials = "*'%" + MIMESpecials; + private static char[] HexDigits = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + private static byte[] HexDecode = new byte[0x80]; + + + public static string EncodeContentDisposition(string disposition, string filename) + { + if (disposition == null) + { + disposition = DispositionAttachment; + } + return disposition + EncodeRFC2231(DispositionFilename, filename); + } + + protected static string EncodeRFC2231(string key, string value) + { + StringBuilder buf = new StringBuilder(); + bool encoded = EncodeRFC2231value(value, buf); + if (encoded) + { + return "; " + key + "*=" + buf.ToString(); + } + else + { + return "; " + key + "=" + value; + } + } + + protected static bool EncodeRFC2231value(string value, StringBuilder buf) + { + buf.Append("UTF-8"); + buf.Append("''"); // no language + byte[] bytes; + try + { + bytes = UTF8Encoding.UTF8.GetBytes(value); + } + catch (Exception) + { + return true; + } + + bool encoded = false; + for (int i = 0; i < bytes.Length; i++) + { + int ch = bytes[i] & 0xff; + if (ch <= 32 || ch >= 127 || RFC2231Specials.IndexOf((char)ch) != -1) + { + buf.Append('%'); + buf.Append(HexDigits[ch >> 4]); + buf.Append(HexDigits[ch & 0xf]); + encoded = true; + } + else + { + buf.Append((char)ch); + } + } + return encoded; + } + + protected static string DecodeRFC2231value(string value) + { + int q1 = value.IndexOf('\''); + if (q1 == -1) + { + return value; + } + string mimeCharset = value.Substring(0, q1); + int q2 = value.IndexOf('\'', q1 + 1); + if (q2 == -1) + { + return value; + } + byte[] bytes = FromHex(value.Substring(q2 + 1)); + try + { + return UTF8Encoding.UTF8.GetString(bytes); + } + catch (Exception) + { + return value; + } + } + + protected static byte[] FromHex(string data) + { + using (MemoryStream stream = new MemoryStream()) + { + for (int i = 0; i < HexDigits.Length; i++) + { + HexDecode[(int)HexDigits[i]] = (byte)i; + HexDecode[(int)Char.ToLower(HexDigits[i])] = (byte)i; + } + + for (int i = 0; i < data.Length; ) + { + char c = data[i++]; + if (c == '%') + { + if (i > data.Length - 2) + { + break; + } + byte b1 = HexDecode[data[i++] & 0x7f]; + byte b2 = HexDecode[data[i++] & 0x7f]; + stream.WriteByte((byte)((b1 << 4) | b2)); + } + else + { + stream.WriteByte((byte)c); + } + + } + return stream.ToArray(); + } + } + + public static string DecodeContentDisposition(string value, Dictionary parameters) + { + try + { + HeaderTokenizer tokenizer = new HeaderTokenizer(value); + Token token = tokenizer.Next(); + if (token.Type != Token.Atomic) + { + return null; + } + string disposition = token.Value; + + string remainder = tokenizer.Remainder; + if (remainder != null) + { + GetParameters(remainder, parameters); + } + + return disposition; + } + catch (ParseException) + { + return null; + } + } + + private static void GetParameters(string list, Dictionary parameters) + { + HeaderTokenizer tokenizer = new HeaderTokenizer(list); + while (true) + { + Token token = tokenizer.Next(); + switch (token.Type) + { + case Token.EOF: + return; + case ';': + token = tokenizer.Next(); + if (token.Type == Token.EOF) + { + return; + } + if (token.Type != Token.Atomic) + { + throw new ParseException("Invalid parameter name: " + token.Value); + } + string name = token.Value.ToLower(new System.Globalization.CultureInfo("en")); + token = tokenizer.Next(); + if (token.Type != '=') + { + throw new ParseException("Missing '='"); + } + token = tokenizer.Next(); + if (token.Type != Token.Atomic && token.Type != Token.QuotedString) + { + throw new ParseException("Invalid parameter value: " + token.Value); + } + string value = token.Value; + if (name.EndsWith("*")) + { + name = name.Substring(0, name.Length - 1); + value = DecodeRFC2231value(value); + } + parameters[name] = value; + break; + default: + throw new ParseException("Missing ';'"); + } + } + } + + [Serializable] + public class ParseException : FormatException + { + public ParseException(string message) + : base(message) + { + } + + public ParseException() + { + } + + public ParseException(string message, Exception inner) + : base(message, inner) + { + } + + protected ParseException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } + + protected class Token + { + public const int Atomic = -1; + public const int QuotedString = -2; + public const int Comment = -3; + public const int EOF = -4; + + public int Type { get; private set; } + public string Value { get; private set; } + + public Token(int type, string value) + { + Type = type; + Value = value; + } + } + + protected class HeaderTokenizer + { + private static readonly Token EOF = new Token(Token.EOF, null); + + private readonly string Header; + private readonly string Delimiters; + private readonly bool SkipComments; + private int pos; + + public HeaderTokenizer(string header) + : this(header, MIMESpecials, true) + { + } + + protected HeaderTokenizer(string header, string delimiters, bool skipComments) + { + Header = header; + Delimiters = delimiters; + SkipComments = skipComments; + } + + public string Remainder + { + get + { + if (pos >= Header.Length) + { + return null; + } + else + { + return Header.Substring(pos); + } + } + } + + public Token Next() + { + return ReadToken(); + } + + private Token ReadToken() + { + while (pos < Header.Length && char.IsWhiteSpace(Header[pos])) + { + ++pos; + } + + if (pos >= Header.Length) + { + return EOF; + } + + char c = Header[pos]; + if (c == '(') + { + Token comment = ReadComment(); + if (SkipComments) + { + return ReadToken(); + } + else + { + return comment; + } + } + else if (c == '\"') + { + return ReadQuotedString(); + } + else if (c < 32 || c >= 127 || Delimiters.Contains(new string(c, 1))) + { + pos++; + return new Token((int)c, new string(c, 1)); + } + else + { + return ReadAtomicToken(); + } + } + + private Token ReadAtomicToken() + { + int start = pos; + while (++pos < Header.Length) + { + char c = Header[pos]; + if (Delimiters.Contains(new string(c, 1)) || c < 32 || c >= 127) + { + break; + } + } + return new Token(Token.Atomic, Header.Substring(start, pos - start)); + } + + private Token ReadQuotedString() + { + int start = pos + 1; + bool requireEscape = false; + while (++pos < Header.Length) + { + char c = Header[pos]; + if (c == '"') + { + string value; + if (requireEscape) + { + value = GetEscapedValue(start, pos - start); + } + else + { + value = Header.Substring(start, pos - start); + } + return new Token(Token.Comment, value); + } + else if (c == '\\') + { + pos++; + requireEscape = true; + } + else if (c == '\r') + { + requireEscape = true; + } + } + throw new ParseException("Missing '\"'"); + } + + private Token ReadComment() + { + int start = pos + 1; + int nesting = 1; + bool requireEscape = false; + while (++pos < Header.Length) + { + char c = Header[pos]; + if (c == ')') + { + nesting--; + if (nesting == 0) + { + break; + } + } + else if (c == '(') + { + nesting++; + } + else if (c == '\\') + { + pos++; + requireEscape = true; + } + else if (c == '\r') + { + requireEscape = true; + } + } + if (nesting != 0) + { + throw new ParseException("Unbalanced comments"); + } + string value; + if (requireEscape) + { + value = GetEscapedValue(start, pos); + } + else + { + value = Header.Substring(start, pos - start); + } + pos++; + return new Token(Token.Comment, value); + } + + private string GetEscapedValue(int start, int end) + { + StringBuilder value = new StringBuilder(); + for (int i = start; i < end; i++) + { + char c = Header[i]; + if (c == '\\') + { + i++; + if (i == end) + { + throw new ParseException("Invalid escape character"); + } + value.Append(Header[i]); + } + else if (c == '\r') + { + if (i < end - 1 && Header[i + 1] == '\n') + { + i++; + } + } + else + { + value.Append(Header[i]); + } + } + return value.ToString(); + } + } + } +} diff --git a/DotCMIS/binding/webservices/webservices.cs b/DotCMIS/binding/webservices/webservices.cs index 90617b4..7ff9e05 100644 --- a/DotCMIS/binding/webservices/webservices.cs +++ b/DotCMIS/binding/webservices/webservices.cs @@ -46,19 +46,25 @@ internal class CmisWebServicesSpi : ICmisSpi private PolicyService policyService; private AclService aclService; - public void initialize(BindingSession session) - { - PortProvider provider = new PortProvider(session); - - repositoryService = new RepositoryService(session, provider); - navigationService = new NavigationService(session, provider); - objectService = new ObjectService(session, provider); - versioningService = new VersioningService(session, provider); - discoveryService = new DiscoveryService(session, provider); - multiFilingService = new MultiFilingService(session, provider); - relationshipService = new RelationshipService(session, provider); - policyService = new PolicyService(session, provider); - aclService = new AclService(session, provider); + public void initialize(IBindingSession session) + { + BindingSession bindingSession = session as BindingSession; + if (bindingSession == null) + { + throw new ArgumentException("Invalid binding session!"); + } + + PortProvider provider = new PortProvider(bindingSession); + + repositoryService = new RepositoryService(bindingSession, provider); + navigationService = new NavigationService(bindingSession, provider); + objectService = new ObjectService(bindingSession, provider); + versioningService = new VersioningService(bindingSession, provider); + discoveryService = new DiscoveryService(bindingSession, provider); + multiFilingService = new MultiFilingService(bindingSession, provider); + relationshipService = new RelationshipService(bindingSession, provider); + policyService = new PolicyService(bindingSession, provider); + aclService = new AclService(bindingSession, provider); } public IRepositoryService GetRepositoryService() diff --git a/DotCMIS/build.bat b/DotCMIS/build.bat index f43b01b..b7c311c 100644 --- a/DotCMIS/build.bat +++ b/DotCMIS/build.bat @@ -33,4 +33,4 @@ echo Building Release DLL... msbuild DotCMIS.csproj /ToolsVersion:3.5 /p:Configuration=Release echo Building documentation... -msbuild DotCMIS.shfbproj /ToolsVersion:3.5 /p:Configuration=Release \ No newline at end of file +msbuild DotCMIS.shfbproj /p:Configuration=Release \ No newline at end of file diff --git a/DotCMIS/client/client-intf.cs b/DotCMIS/client/client-intf.cs index 59e1245..61055ec 100644 --- a/DotCMIS/client/client-intf.cs +++ b/DotCMIS/client/client-intf.cs @@ -871,6 +871,12 @@ public interface IDocumentProperties /// string VersionSeriesCheckedOutId { get; } + /// + /// Gets a value indicating whether this instance is a private working copy. (CMIS property cmis:isPrivateWorkingCopy). + /// + /// true if this instance is a private working copy; otherwise, false. + bool? IsPrivateWorkingCopy { get; } + /// /// Gets the checkin comment (CMIS property cmis:checkinComment). /// diff --git a/DotCMIS/client/client-objectfactory.cs b/DotCMIS/client/client-objectfactory.cs index a23d776..b5beba4 100644 --- a/DotCMIS/client/client-objectfactory.cs +++ b/DotCMIS/client/client-objectfactory.cs @@ -31,13 +31,13 @@ public class ObjectFactory : IObjectFactory { private ISession session; - public void Initialize(ISession session, IDictionary parameters) + public virtual void Initialize(ISession session, IDictionary parameters) { this.session = session; } // ACL and ACE - public IAcl ConvertAces(IList aces) + public virtual IAcl ConvertAces(IList aces) { if (aces == null) { return null; } @@ -52,7 +52,7 @@ public IAcl ConvertAces(IList aces) return result; } - public IAcl CreateAcl(IList aces) + public virtual IAcl CreateAcl(IList aces) { Acl acl = new Acl(); acl.Aces = aces; @@ -60,7 +60,7 @@ public IAcl CreateAcl(IList aces) return acl; } - public IAce CreateAce(string principal, IList permissions) + public virtual IAce CreateAce(string principal, IList permissions) { Ace ace = new Ace(); ace.IsDirect = true; @@ -73,7 +73,7 @@ public IAce CreateAce(string principal, IList permissions) } // policies - public IList ConvertPolicies(IList policies) + public virtual IList ConvertPolicies(IList policies) { if (policies == null) { return null; } @@ -90,7 +90,7 @@ public IList ConvertPolicies(IList policies) } // renditions - public IRendition ConvertRendition(string objectId, IRenditionData rendition) + public virtual IRendition ConvertRendition(string objectId, IRenditionData rendition) { if (rendition == null) { @@ -102,7 +102,7 @@ public IRendition ConvertRendition(string objectId, IRenditionData rendition) } // content stream - public IContentStream CreateContentStream(string filename, long length, string mimetype, Stream stream) + public virtual IContentStream CreateContentStream(string filename, long length, string mimetype, Stream stream) { ContentStream result = new ContentStream(); result.FileName = filename; @@ -114,7 +114,7 @@ public IContentStream CreateContentStream(string filename, long length, string m } // types - public IObjectType ConvertTypeDefinition(ITypeDefinition typeDefinition) + public virtual IObjectType ConvertTypeDefinition(ITypeDefinition typeDefinition) { switch (typeDefinition.BaseTypeId) { @@ -135,7 +135,7 @@ public IObjectType ConvertTypeDefinition(ITypeDefinition typeDefinition) } } - public IObjectType GetTypeFromObjectData(IObjectData objectData) + public virtual IObjectType GetTypeFromObjectData(IObjectData objectData) { if (objectData == null || objectData.Properties == null) { @@ -158,7 +158,7 @@ public IObjectType GetTypeFromObjectData(IObjectData objectData) } // properties - public IProperty CreateProperty(IPropertyDefinition type, IList values) + public virtual IProperty CreateProperty(IPropertyDefinition type, IList values) { return new Property(type, (IList)values); } @@ -194,7 +194,7 @@ protected IProperty ConvertProperty(IObjectType objectType, IList ConvertProperties(IObjectType objectType, IList secondaryTypes, IProperties properties) + public virtual IDictionary ConvertProperties(IObjectType objectType, IList secondaryTypes, IProperties properties) { if (objectType == null) { @@ -223,7 +223,7 @@ public IDictionary ConvertProperties(IObjectType objectType, return result; } - public IProperties ConvertProperties(IDictionary properties, IObjectType type, IList secondaryTypes, HashSet updatabilityFilter) + public virtual IProperties ConvertProperties(IDictionary properties, IObjectType type, IList secondaryTypes, HashSet updatabilityFilter) { // check input if (properties == null) @@ -374,7 +374,7 @@ public IProperties ConvertProperties(IDictionary properties, IOb return result; } - public IList ConvertQueryProperties(IProperties properties) + public virtual IList ConvertQueryProperties(IProperties properties) { if ((properties == null) || (properties.PropertyList == null)) { @@ -385,7 +385,7 @@ public IList ConvertQueryProperties(IProperties properties) } // objects - public ICmisObject ConvertObject(IObjectData objectData, IOperationContext context) + public virtual ICmisObject ConvertObject(IObjectData objectData, IOperationContext context) { if (objectData == null) { @@ -411,7 +411,7 @@ public ICmisObject ConvertObject(IObjectData objectData, IOperationContext conte } } - public IQueryResult ConvertQueryResult(IObjectData objectData) + public virtual IQueryResult ConvertQueryResult(IObjectData objectData) { if (objectData == null) { @@ -421,7 +421,7 @@ public IQueryResult ConvertQueryResult(IObjectData objectData) return new QueryResult(session, objectData); } - public IChangeEvent ConvertChangeEvent(IObjectData objectData) + public virtual IChangeEvent ConvertChangeEvent(IObjectData objectData) { ChangeEvent result = new ChangeEvent(); @@ -466,7 +466,7 @@ public IChangeEvent ConvertChangeEvent(IObjectData objectData) return result; } - public IChangeEvents ConvertChangeEvents(string changeLogToken, IObjectList objectList) + public virtual IChangeEvents ConvertChangeEvents(string changeLogToken, IObjectList objectList) { if (objectList == null) { diff --git a/DotCMIS/client/client-objects.cs b/DotCMIS/client/client-objects.cs index 0c4e3de..1a0c5d1 100644 --- a/DotCMIS/client/client-objects.cs +++ b/DotCMIS/client/client-objects.cs @@ -40,7 +40,7 @@ public abstract class AbstractCmisObject : ICmisObject protected ICmisBinding Binding { get { return Session.Binding; } } private IObjectType objectType; - public IObjectType ObjectType + public virtual IObjectType ObjectType { get { @@ -50,8 +50,8 @@ public IObjectType ObjectType } } } - - public IList SecondaryTypes + + public virtual IList SecondaryTypes { get { @@ -62,7 +62,7 @@ public IList SecondaryTypes } } - protected string ObjectId + protected virtual string ObjectId { get { @@ -88,7 +88,7 @@ protected string ObjectId private IList secondaryTypes; protected object objectLock = new object(); - protected void Initialize(ISession session, IObjectType objectType, IObjectData objectData, IOperationContext context) + protected virtual void Initialize(ISession session, IObjectType objectType, IObjectData objectData, IOperationContext context) { if (session == null) { @@ -199,7 +199,7 @@ protected void Initialize(ISession session, IObjectType objectType, IObjectData } } - protected string GetPropertyQueryName(string propertyId) + protected virtual string GetPropertyQueryName(string propertyId) { lock (objectLock) { @@ -215,7 +215,7 @@ protected string GetPropertyQueryName(string propertyId) // --- object --- - public void Delete(bool allVersions) + public virtual void Delete(bool allVersions) { lock (objectLock) { @@ -223,7 +223,7 @@ public void Delete(bool allVersions) } } - public ICmisObject UpdateProperties(IDictionary properties) + public virtual ICmisObject UpdateProperties(IDictionary properties) { IObjectId objectId = UpdateProperties(properties, true); if (objectId == null) @@ -239,7 +239,7 @@ public ICmisObject UpdateProperties(IDictionary properties) return this; } - public IObjectId UpdateProperties(IDictionary properties, bool refresh) + public virtual IObjectId UpdateProperties(IDictionary properties, bool refresh) { if (properties == null || properties.Count == 0) { @@ -283,7 +283,7 @@ public IObjectId UpdateProperties(IDictionary properties, bool r return Session.CreateObjectId(newObjectId); } - public ICmisObject Rename(string newName) + public virtual ICmisObject Rename(string newName) { if (newName == null || newName.Length == 0) { @@ -296,7 +296,7 @@ public ICmisObject Rename(string newName) return UpdateProperties(prop); } - public IObjectId Rename(string newName, bool refresh) + public virtual IObjectId Rename(string newName, bool refresh) { IDictionary prop = new Dictionary(); prop[PropertyIds.Name] = newName; @@ -306,9 +306,9 @@ public IObjectId Rename(string newName, bool refresh) // --- properties --- - public IObjectType BaseType { get { return Session.GetTypeDefinition(GetPropertyValue(PropertyIds.BaseTypeId) as string); } } + public virtual IObjectType BaseType { get { return Session.GetTypeDefinition(GetPropertyValue(PropertyIds.BaseTypeId) as string); } } - public BaseTypeId BaseTypeId + public virtual BaseTypeId BaseTypeId { get { @@ -319,21 +319,21 @@ public BaseTypeId BaseTypeId } } - public string Id { get { return GetPropertyValue(PropertyIds.ObjectId) as string; } } + public virtual string Id { get { return GetPropertyValue(PropertyIds.ObjectId) as string; } } - public string Name { get { return GetPropertyValue(PropertyIds.Name) as string; } } + public virtual string Name { get { return GetPropertyValue(PropertyIds.Name) as string; } } - public string CreatedBy { get { return GetPropertyValue(PropertyIds.CreatedBy) as string; } } + public virtual string CreatedBy { get { return GetPropertyValue(PropertyIds.CreatedBy) as string; } } - public DateTime? CreationDate { get { return GetPropertyValue(PropertyIds.CreationDate) as DateTime?; } } + public virtual DateTime? CreationDate { get { return GetPropertyValue(PropertyIds.CreationDate) as DateTime?; } } - public string LastModifiedBy { get { return GetPropertyValue(PropertyIds.LastModifiedBy) as string; } } + public virtual string LastModifiedBy { get { return GetPropertyValue(PropertyIds.LastModifiedBy) as string; } } - public DateTime? LastModificationDate { get { return GetPropertyValue(PropertyIds.LastModificationDate) as DateTime?; } } + public virtual DateTime? LastModificationDate { get { return GetPropertyValue(PropertyIds.LastModificationDate) as DateTime?; } } - public string ChangeToken { get { return GetPropertyValue(PropertyIds.ChangeToken) as string; } } + public virtual string ChangeToken { get { return GetPropertyValue(PropertyIds.ChangeToken) as string; } } - public IList Properties + public virtual IList Properties { get { @@ -344,7 +344,7 @@ public IList Properties } } - public IProperty this[string propertyId] + public virtual IProperty this[string propertyId] { get { @@ -365,7 +365,7 @@ public IProperty this[string propertyId] } } - public object GetPropertyValue(string propertyId) + public virtual object GetPropertyValue(string propertyId) { IProperty property = this[propertyId]; if (property == null) { return null; } @@ -375,7 +375,7 @@ public object GetPropertyValue(string propertyId) // --- allowable actions --- - public IAllowableActions AllowableActions + public virtual IAllowableActions AllowableActions { get { @@ -388,7 +388,7 @@ public IAllowableActions AllowableActions // --- renditions --- - public IList Renditions + public virtual IList Renditions { get { @@ -401,12 +401,12 @@ public IList Renditions // --- ACL --- - public IAcl getAcl(bool onlyBasicPermissions) + public virtual IAcl getAcl(bool onlyBasicPermissions) { return Binding.GetAclService().GetAcl(RepositoryId, ObjectId, onlyBasicPermissions, null); } - public IAcl ApplyAcl(IList addAces, IList removeAces, AclPropagation? aclPropagation) + public virtual IAcl ApplyAcl(IList addAces, IList removeAces, AclPropagation? aclPropagation) { IAcl result = Session.ApplyAcl(this, addAces, removeAces, aclPropagation); @@ -415,17 +415,17 @@ public IAcl ApplyAcl(IList addAces, IList removeAces, AclPropagation return result; } - public IAcl AddAcl(IList addAces, AclPropagation? aclPropagation) + public virtual IAcl AddAcl(IList addAces, AclPropagation? aclPropagation) { return ApplyAcl(addAces, null, aclPropagation); } - public IAcl RemoveAcl(IList removeAces, AclPropagation? aclPropagation) + public virtual IAcl RemoveAcl(IList removeAces, AclPropagation? aclPropagation) { return ApplyAcl(null, removeAces, aclPropagation); } - public IAcl Acl + public virtual IAcl Acl { get { @@ -438,7 +438,7 @@ public IAcl Acl // --- policies --- - public void ApplyPolicy(params IObjectId[] policyId) + public virtual void ApplyPolicy(params IObjectId[] policyId) { lock (objectLock) { @@ -448,7 +448,7 @@ public void ApplyPolicy(params IObjectId[] policyId) Refresh(); } - public void RemovePolicy(params IObjectId[] policyId) + public virtual void RemovePolicy(params IObjectId[] policyId) { lock (objectLock) { @@ -458,7 +458,7 @@ public void RemovePolicy(params IObjectId[] policyId) Refresh(); } - public IList Policies + public virtual IList Policies { get { @@ -471,7 +471,7 @@ public IList Policies // --- relationships --- - public IList Relationships + public virtual IList Relationships { get { @@ -484,7 +484,7 @@ public IList Relationships // --- extensions --- - public IList GetExtensions(ExtensionLevel level) + public virtual IList GetExtensions(ExtensionLevel level) { IList ext; if (extensions.TryGetValue(level, out ext)) @@ -497,7 +497,7 @@ public IList GetExtensions(ExtensionLevel level) // --- other --- - public DateTime RefreshTimestamp { get; private set; } + public virtual DateTime RefreshTimestamp { get; private set; } public void Refresh() { @@ -514,7 +514,7 @@ public void Refresh() } } - public void RefreshIfOld(long durationInMillis) + public virtual void RefreshIfOld(long durationInMillis) { lock (objectLock) { @@ -531,7 +531,7 @@ public void RefreshIfOld(long durationInMillis) /// public abstract class AbstractFileableCmisObject : AbstractCmisObject, IFileableCmisObject { - public IFileableCmisObject Move(IObjectId sourceFolderId, IObjectId targetFolderId) + public virtual IFileableCmisObject Move(IObjectId sourceFolderId, IObjectId targetFolderId) { string objectId = ObjectId; @@ -644,7 +644,7 @@ public virtual IList Paths } } - public void AddToFolder(IObjectId folderId, bool allVersions) + public virtual void AddToFolder(IObjectId folderId, bool allVersions) { if (folderId == null || folderId.Id == null) { @@ -654,7 +654,7 @@ public void AddToFolder(IObjectId folderId, bool allVersions) Binding.GetMultiFilingService().AddObjectToFolder(RepositoryId, ObjectId, folderId.Id, allVersions, null); } - public void RemoveFromFolder(IObjectId folderId) + public virtual void RemoveFromFolder(IObjectId folderId) { Binding.GetMultiFilingService().RemoveObjectFromFolder(RepositoryId, ObjectId, folderId == null ? null : folderId.Id, null); } @@ -672,37 +672,39 @@ public Document(ISession session, IObjectType objectType, IObjectData objectData // properties - public bool? IsImmutable { get { return GetPropertyValue(PropertyIds.IsImmutable) as bool?; } } + public virtual bool? IsImmutable { get { return GetPropertyValue(PropertyIds.IsImmutable) as bool?; } } + + public virtual bool? IsLatestVersion { get { return GetPropertyValue(PropertyIds.IsLatestVersion) as bool?; } } - public bool? IsLatestVersion { get { return GetPropertyValue(PropertyIds.IsLatestVersion) as bool?; } } + public virtual bool? IsMajorVersion { get { return GetPropertyValue(PropertyIds.IsMajorVersion) as bool?; } } - public bool? IsMajorVersion { get { return GetPropertyValue(PropertyIds.IsMajorVersion) as bool?; } } + public virtual bool? IsLatestMajorVersion { get { return GetPropertyValue(PropertyIds.IsLatestMajorVersion) as bool?; } } - public bool? IsLatestMajorVersion { get { return GetPropertyValue(PropertyIds.IsLatestMajorVersion) as bool?; } } + public virtual bool? IsPrivateWorkingCopy { get { return GetPropertyValue(PropertyIds.IsPrivateWorkingCopy) as bool?; } } - public string VersionLabel { get { return GetPropertyValue(PropertyIds.VersionLabel) as string; } } + public virtual string VersionLabel { get { return GetPropertyValue(PropertyIds.VersionLabel) as string; } } - public string VersionSeriesId { get { return GetPropertyValue(PropertyIds.VersionSeriesId) as string; } } + public virtual string VersionSeriesId { get { return GetPropertyValue(PropertyIds.VersionSeriesId) as string; } } - public bool? IsVersionSeriesCheckedOut { get { return GetPropertyValue(PropertyIds.IsVersionSeriesCheckedOut) as bool?; } } + public virtual bool? IsVersionSeriesCheckedOut { get { return GetPropertyValue(PropertyIds.IsVersionSeriesCheckedOut) as bool?; } } - public string VersionSeriesCheckedOutBy { get { return GetPropertyValue(PropertyIds.VersionSeriesCheckedOutBy) as string; } } + public virtual string VersionSeriesCheckedOutBy { get { return GetPropertyValue(PropertyIds.VersionSeriesCheckedOutBy) as string; } } - public string VersionSeriesCheckedOutId { get { return GetPropertyValue(PropertyIds.VersionSeriesCheckedOutId) as string; } } + public virtual string VersionSeriesCheckedOutId { get { return GetPropertyValue(PropertyIds.VersionSeriesCheckedOutId) as string; } } - public string CheckinComment { get { return GetPropertyValue(PropertyIds.CheckinComment) as string; } } + public virtual string CheckinComment { get { return GetPropertyValue(PropertyIds.CheckinComment) as string; } } - public long? ContentStreamLength { get { return GetPropertyValue(PropertyIds.ContentStreamLength) as long?; } } + public virtual long? ContentStreamLength { get { return GetPropertyValue(PropertyIds.ContentStreamLength) as long?; } } - public string ContentStreamMimeType { get { return GetPropertyValue(PropertyIds.ContentStreamMimeType) as string; } } + public virtual string ContentStreamMimeType { get { return GetPropertyValue(PropertyIds.ContentStreamMimeType) as string; } } - public string ContentStreamFileName { get { return GetPropertyValue(PropertyIds.ContentStreamFileName) as string; } } + public virtual string ContentStreamFileName { get { return GetPropertyValue(PropertyIds.ContentStreamFileName) as string; } } - public string ContentStreamId { get { return GetPropertyValue(PropertyIds.ContentStreamId) as string; } } + public virtual string ContentStreamId { get { return GetPropertyValue(PropertyIds.ContentStreamId) as string; } } // operations - public IDocument Copy(IObjectId targetFolderId, IDictionary properties, VersioningState? versioningState, + public virtual IDocument Copy(IObjectId targetFolderId, IDictionary properties, VersioningState? versioningState, IList policies, IList addAces, IList removeAces, IOperationContext context) { @@ -723,19 +725,19 @@ public IDocument Copy(IObjectId targetFolderId, IDictionary prop return newDoc; } - public IDocument Copy(IObjectId targetFolderId) + public virtual IDocument Copy(IObjectId targetFolderId) { return Copy(targetFolderId, null, null, null, null, null, Session.DefaultContext); } - public void DeleteAllVersions() + public virtual void DeleteAllVersions() { Delete(true); } // versioning - public IObjectId CheckOut() + public virtual IObjectId CheckOut() { string newObjectId = null; @@ -756,12 +758,12 @@ public IObjectId CheckOut() return Session.CreateObjectId(newObjectId); } - public void CancelCheckOut() + public virtual void CancelCheckOut() { Binding.GetVersioningService().CancelCheckOut(RepositoryId, ObjectId, null); } - public IObjectId CheckIn(bool major, IDictionary properties, IContentStream contentStream, + public virtual IObjectId CheckIn(bool major, IDictionary properties, IContentStream contentStream, string checkinComment, IList policies, IList addAces, IList removeAces) { String newObjectId = null; @@ -791,12 +793,12 @@ public IObjectId CheckIn(bool major, IDictionary properties, ICo } - public IList GetAllVersions() + public virtual IList GetAllVersions() { return GetAllVersions(Session.DefaultContext); } - public IList GetAllVersions(IOperationContext context) + public virtual IList GetAllVersions(IOperationContext context) { string objectId; string versionSeriesId; @@ -831,29 +833,29 @@ public IList GetAllVersions(IOperationContext context) return result; } - public IDocument GetObjectOfLatestVersion(bool major) + public virtual IDocument GetObjectOfLatestVersion(bool major) { return GetObjectOfLatestVersion(major, Session.DefaultContext); } - public IDocument GetObjectOfLatestVersion(bool major, IOperationContext context) + public virtual IDocument GetObjectOfLatestVersion(bool major, IOperationContext context) { return Session.GetLatestDocumentVersion(this, major, context); } // content operations - public IContentStream GetContentStream() + public virtual IContentStream GetContentStream() { return GetContentStream(null); } - public IContentStream GetContentStream(string streamId) + public virtual IContentStream GetContentStream(string streamId) { return GetContentStream(streamId, null, null); } - public IContentStream GetContentStream(string streamId, long? offset, long? length) + public virtual IContentStream GetContentStream(string streamId, long? offset, long? length) { IContentStream contentStream = Session.GetContentStream(this, streamId, offset, length); if (contentStream == null) @@ -879,7 +881,7 @@ public IContentStream GetContentStream(string streamId, long? offset, long? leng return contentStream; } - public IDocument SetContentStream(IContentStream contentStream, bool overwrite) + public virtual IDocument SetContentStream(IContentStream contentStream, bool overwrite) { IObjectId objectId = SetContentStream(contentStream, overwrite, true); if (objectId == null) @@ -895,7 +897,7 @@ public IDocument SetContentStream(IContentStream contentStream, bool overwrite) return this; } - public IObjectId SetContentStream(IContentStream contentStream, bool overwrite, bool refresh) + public virtual IObjectId SetContentStream(IContentStream contentStream, bool overwrite, bool refresh) { string newObjectId = null; @@ -921,8 +923,8 @@ public IObjectId SetContentStream(IContentStream contentStream, bool overwrite, return Session.CreateObjectId(newObjectId); } - - public IDocument AppendContentStream(IContentStream contentStream, bool isLastTrunk) + + public virtual IDocument AppendContentStream(IContentStream contentStream, bool isLastTrunk) { IObjectId objectId = AppendContentStream(contentStream, isLastTrunk, true); if (objectId == null) @@ -932,7 +934,9 @@ public IDocument AppendContentStream(IContentStream contentStream, bool isLastTr if (ObjectId != objectId.Id) { - return (IDocument)Session.GetObject(objectId, CreationContext); + IDocument newDoc = Session.GetObject(objectId, CreationContext) as IDocument; + newDoc.Refresh(); + return newDoc; } return this; @@ -952,8 +956,7 @@ public IObjectId AppendContentStream(IContentStream contentStream, bool isLastTr newObjectId = objectId; } - if (refresh) - { + if (!(newObjectId != null && this.ObjectId != newObjectId) && refresh) { Refresh(); } @@ -965,7 +968,7 @@ public IObjectId AppendContentStream(IContentStream contentStream, bool isLastTr return Session.CreateObjectId(newObjectId); } - public IDocument DeleteContentStream() + public virtual IDocument DeleteContentStream() { IObjectId objectId = DeleteContentStream(true); if (objectId == null) @@ -981,7 +984,7 @@ public IDocument DeleteContentStream() return this; } - public IObjectId DeleteContentStream(bool refresh) + public virtual IObjectId DeleteContentStream(bool refresh) { string newObjectId = null; @@ -1008,7 +1011,7 @@ public IObjectId DeleteContentStream(bool refresh) return Session.CreateObjectId(newObjectId); } - public IObjectId CheckIn(bool major, IDictionary properties, IContentStream contentStream, string checkinComment) + public virtual IObjectId CheckIn(bool major, IDictionary properties, IContentStream contentStream, string checkinComment) { return this.CheckIn(major, properties, contentStream, checkinComment, null, null, null); } @@ -1024,7 +1027,7 @@ public Folder(ISession session, IObjectType objectType, IObjectData objectData, Initialize(session, objectType, objectData, context); } - public IDocument CreateDocument(IDictionary properties, IContentStream contentStream, VersioningState? versioningState, + public virtual IDocument CreateDocument(IDictionary properties, IContentStream contentStream, VersioningState? versioningState, IList policies, IList addAces, IList removeAces, IOperationContext context) { IObjectId newId = Session.CreateDocument(properties, this, contentStream, versioningState, policies, addAces, removeAces); @@ -1045,7 +1048,7 @@ public IDocument CreateDocument(IDictionary properties, IContent return newDoc; } - public IDocument CreateDocumentFromSource(IObjectId source, IDictionary properties, VersioningState? versioningState, + public virtual IDocument CreateDocumentFromSource(IObjectId source, IDictionary properties, VersioningState? versioningState, IList policies, IList addAces, IList removeAces, IOperationContext context) { IObjectId newId = Session.CreateDocumentFromSource(source, properties, this, versioningState, policies, addAces, removeAces); @@ -1066,7 +1069,7 @@ public IDocument CreateDocumentFromSource(IObjectId source, IDictionary properties, IList policies, IList addAces, IList removeAces, IOperationContext context) + public virtual IFolder CreateFolder(IDictionary properties, IList policies, IList addAces, IList removeAces, IOperationContext context) { IObjectId newId = Session.CreateFolder(properties, this, policies, addAces, removeAces); @@ -1086,7 +1089,7 @@ public IFolder CreateFolder(IDictionary properties, IList properties, IList policies, IList addAces, IList removeAces, IOperationContext context) + public virtual IPolicy CreatePolicy(IDictionary properties, IList policies, IList addAces, IList removeAces, IOperationContext context) { IObjectId newId = Session.CreatePolicy(properties, this, policies, addAces, removeAces); @@ -1106,15 +1109,15 @@ public IPolicy CreatePolicy(IDictionary properties, IList DeleteTree(bool allVersions, UnfileObject? unfile, bool continueOnFailure) + public virtual IList DeleteTree(bool allVersions, UnfileObject? unfile, bool continueOnFailure) { IFailedToDeleteData failed = Binding.GetObjectService().DeleteTree(RepositoryId, ObjectId, allVersions, unfile, continueOnFailure, null); return failed.Ids; } - public string ParentId { get { return GetPropertyValue(PropertyIds.ParentId) as string; } } + public virtual string ParentId { get { return GetPropertyValue(PropertyIds.ParentId) as string; } } - public IList AllowedChildObjectTypes + public virtual IList AllowedChildObjectTypes { get { @@ -1138,12 +1141,12 @@ public IList AllowedChildObjectTypes } } - public IItemEnumerable GetCheckedOutDocs() + public virtual IItemEnumerable GetCheckedOutDocs() { return GetCheckedOutDocs(Session.DefaultContext); } - public IItemEnumerable GetCheckedOutDocs(IOperationContext context) + public virtual IItemEnumerable GetCheckedOutDocs(IOperationContext context) { string objectId = ObjectId; INavigationService service = Binding.GetNavigationService(); @@ -1179,12 +1182,12 @@ public IItemEnumerable GetCheckedOutDocs(IOperationContext context) return new CollectionEnumerable(new PageFetcher(ctxt.MaxItemsPerPage, fetchPageDelegate)); } - public IItemEnumerable GetChildren() + public virtual IItemEnumerable GetChildren() { return GetChildren(Session.DefaultContext); } - public IItemEnumerable GetChildren(IOperationContext context) + public virtual IItemEnumerable GetChildren(IOperationContext context) { string objectId = ObjectId; INavigationService service = Binding.GetNavigationService(); @@ -1216,12 +1219,12 @@ public IItemEnumerable GetChildren(IOperationContext context) return new CollectionEnumerable(new PageFetcher(ctxt.MaxItemsPerPage, fetchPageDelegate)); } - public IList> GetDescendants(int depth) + public virtual IList> GetDescendants(int depth) { return GetDescendants(depth, Session.DefaultContext); } - public IList> GetDescendants(int depth, IOperationContext context) + public virtual IList> GetDescendants(int depth, IOperationContext context) { IList bindingContainerList = Binding.GetNavigationService().GetDescendants(RepositoryId, ObjectId, depth, context.FilterString, context.IncludeAllowableActions, context.IncludeRelationships, context.RenditionFilterString, @@ -1230,12 +1233,12 @@ public IList> GetDescendants(int depth, IOperationCon return ConvertProviderContainer(bindingContainerList, context); } - public IList> GetFolderTree(int depth) + public virtual IList> GetFolderTree(int depth) { return GetFolderTree(depth, Session.DefaultContext); } - public IList> GetFolderTree(int depth, IOperationContext context) + public virtual IList> GetFolderTree(int depth, IOperationContext context) { IList bindingContainerList = Binding.GetNavigationService().GetFolderTree(RepositoryId, ObjectId, depth, context.FilterString, context.IncludeAllowableActions, context.IncludeRelationships, context.RenditionFilterString, @@ -1282,9 +1285,9 @@ private IList> ConvertProviderContainer(IList Paths } } - public IDocument CreateDocument(IDictionary properties, IContentStream contentStream, VersioningState? versioningState) + public virtual IDocument CreateDocument(IDictionary properties, IContentStream contentStream, VersioningState? versioningState) { return CreateDocument(properties, contentStream, versioningState, null, null, null, Session.DefaultContext); } - public IDocument CreateDocumentFromSource(IObjectId source, IDictionary properties, VersioningState? versioningState) + public virtual IDocument CreateDocumentFromSource(IObjectId source, IDictionary properties, VersioningState? versioningState) { return CreateDocumentFromSource(source, properties, versioningState, null, null, null, Session.DefaultContext); } - public IFolder CreateFolder(IDictionary properties) + public virtual IFolder CreateFolder(IDictionary properties) { return CreateFolder(properties, null, null, null, Session.DefaultContext); } - public IPolicy CreatePolicy(IDictionary properties) + public virtual IPolicy CreatePolicy(IDictionary properties) { return CreatePolicy(properties, null, null, null, Session.DefaultContext); } @@ -1384,7 +1387,7 @@ public Policy(ISession session, IObjectType objectType, IObjectData objectData, Initialize(session, objectType, objectData, context); } - public string PolicyText { get { return GetPropertyValue(PropertyIds.PolicyText) as string; } } + public virtual string PolicyText { get { return GetPropertyValue(PropertyIds.PolicyText) as string; } } } /// @@ -1409,12 +1412,12 @@ public Relationship(ISession session, IObjectType objectType, IObjectData object Initialize(session, objectType, objectData, context); } - public ICmisObject GetSource() + public virtual ICmisObject GetSource() { return GetSource(Session.DefaultContext); } - public ICmisObject GetSource(IOperationContext context) + public virtual ICmisObject GetSource(IOperationContext context) { lock (objectLock) { @@ -1428,7 +1431,7 @@ public ICmisObject GetSource(IOperationContext context) } } - public IObjectId SourceId + public virtual IObjectId SourceId { get { @@ -1442,12 +1445,12 @@ public IObjectId SourceId } } - public ICmisObject GetTarget() + public virtual ICmisObject GetTarget() { return GetTarget(Session.DefaultContext); } - public ICmisObject GetTarget(IOperationContext context) + public virtual ICmisObject GetTarget(IOperationContext context) { lock (objectLock) { @@ -1461,7 +1464,7 @@ public ICmisObject GetTarget(IOperationContext context) } } - public IObjectId TargetId + public virtual IObjectId TargetId { get { @@ -1484,21 +1487,21 @@ public Property(IPropertyDefinition propertyDefinition, IList values) Values = values; } - public string Id { get { return PropertyDefinition.Id; } } + public virtual string Id { get { return PropertyDefinition.Id; } } - public string LocalName { get { return PropertyDefinition.LocalName; } } + public virtual string LocalName { get { return PropertyDefinition.LocalName; } } - public string DisplayName { get { return PropertyDefinition.DisplayName; } } + public virtual string DisplayName { get { return PropertyDefinition.DisplayName; } } - public string QueryName { get { return PropertyDefinition.QueryName; } } + public virtual string QueryName { get { return PropertyDefinition.QueryName; } } - public bool IsMultiValued { get { return PropertyDefinition.Cardinality == Cardinality.Multi; } } + public virtual bool IsMultiValued { get { return PropertyDefinition.Cardinality == Cardinality.Multi; } } - public PropertyType? PropertyType { get { return PropertyDefinition.PropertyType; } } + public virtual PropertyType? PropertyType { get { return PropertyDefinition.PropertyType; } } - public IPropertyDefinition PropertyDefinition { get; protected set; } + public virtual IPropertyDefinition PropertyDefinition { get; protected set; } - public object Value + public virtual object Value { get { @@ -1513,13 +1516,13 @@ public object Value } } - public IList Values { get; protected set; } + public virtual IList Values { get; protected set; } - public object FirstValue { get { return Values == null || Values.Count == 0 ? null : Values[0]; } } + public virtual object FirstValue { get { return Values == null || Values.Count == 0 ? null : Values[0]; } } - public string ValueAsString { get { return FormatValue(FirstValue); } } + public virtual string ValueAsString { get { return FormatValue(FirstValue); } } - public string ValuesAsString + public virtual string ValuesAsString { get { @@ -1579,12 +1582,12 @@ public Rendition(ISession session, string objectId, string streamId, string mime RenditionDocumentId = renditionDocumentId; } - public IDocument GetRenditionDocument() + public virtual IDocument GetRenditionDocument() { return GetRenditionDocument(session.DefaultContext); } - public IDocument GetRenditionDocument(IOperationContext context) + public virtual IDocument GetRenditionDocument(IOperationContext context) { if (RenditionDocumentId == null) { @@ -1594,7 +1597,7 @@ public IDocument GetRenditionDocument(IOperationContext context) return session.GetObject(session.CreateObjectId(RenditionDocumentId), context) as IDocument; } - public IContentStream GetContentStream() + public virtual IContentStream GetContentStream() { if (objectId == null || StreamId == null) { @@ -1668,7 +1671,7 @@ public QueryResult(ISession session, IObjectData objectData) } } - public IPropertyData this[string queryName] + public virtual IPropertyData this[string queryName] { get { @@ -1687,9 +1690,9 @@ public IPropertyData this[string queryName] } } - public IList Properties { get; protected set; } + public virtual IList Properties { get; protected set; } - public IPropertyData GetPropertyById(string propertyId) + public virtual IPropertyData GetPropertyById(string propertyId) { if (propertyId == null) { @@ -1705,7 +1708,7 @@ public IPropertyData GetPropertyById(string propertyId) return null; } - public object GetPropertyValueByQueryName(string queryName) + public virtual object GetPropertyValueByQueryName(string queryName) { IPropertyData property = this[queryName]; if (property == null) @@ -1716,7 +1719,7 @@ public object GetPropertyValueByQueryName(string queryName) return property.FirstValue; } - public object GetPropertyValueById(string propertyId) + public virtual object GetPropertyValueById(string propertyId) { IPropertyData property = GetPropertyById(propertyId); if (property == null) @@ -1727,7 +1730,7 @@ public object GetPropertyValueById(string propertyId) return property.FirstValue; } - public IList GetPropertyMultivalueByQueryName(string queryName) + public virtual IList GetPropertyMultivalueByQueryName(string queryName) { IPropertyData property = this[queryName]; if (property == null) @@ -1738,7 +1741,7 @@ public IList GetPropertyMultivalueByQueryName(string queryName) return property.Values; } - public IList GetPropertyMultivalueById(string propertyId) + public virtual IList GetPropertyMultivalueById(string propertyId) { IPropertyData property = GetPropertyById(propertyId); if (property == null) @@ -1749,32 +1752,32 @@ public IList GetPropertyMultivalueById(string propertyId) return property.Values; } - public IAllowableActions AllowableActions { get; protected set; } + public virtual IAllowableActions AllowableActions { get; protected set; } - public IList Relationships { get; protected set; } + public virtual IList Relationships { get; protected set; } - public IList Renditions { get; protected set; } + public virtual IList Renditions { get; protected set; } } public class ChangeEvent : ChangeEventInfo, IChangeEvent { - public string ObjectId { get; set; } + public virtual string ObjectId { get; set; } - public IDictionary> Properties { get; set; } + public virtual IDictionary> Properties { get; set; } - public IList PolicyIds { get; set; } + public virtual IList PolicyIds { get; set; } - public IAcl Acl { get; set; } + public virtual IAcl Acl { get; set; } } public class ChangeEvents : IChangeEvents { - public string LatestChangeLogToken { get; set; } + public virtual string LatestChangeLogToken { get; set; } - public IList ChangeEventList { get; set; } + public virtual IList ChangeEventList { get; set; } - public bool? HasMoreItems { get; set; } + public virtual bool? HasMoreItems { get; set; } - public long? TotalNumItems { get; set; } + public virtual long? TotalNumItems { get; set; } } } diff --git a/DotCMIS/client/client-types.cs b/DotCMIS/client/client-types.cs index 48b41f6..c470ccb 100644 --- a/DotCMIS/client/client-types.cs +++ b/DotCMIS/client/client-types.cs @@ -90,15 +90,15 @@ public DocumentType(ISession session, IDocumentTypeDefinition typeDefinition) helper = new ObjectTypeHelper(session, this); } - public IObjectType GetBaseType() { return helper.GetBaseType(); } + public virtual IObjectType GetBaseType() { return helper.GetBaseType(); } - public IItemEnumerable GetChildren() { return helper.GetChildren(); } + public virtual IItemEnumerable GetChildren() { return helper.GetChildren(); } - public IList> GetDescendants(int depth) { return helper.GetDescendants(depth); } + public virtual IList> GetDescendants(int depth) { return helper.GetDescendants(depth); } - public IObjectType GetParentType() { return helper.GetParentType(); } + public virtual IObjectType GetParentType() { return helper.GetParentType(); } - public bool IsBaseType { get { return helper.IsBaseType; } } + public virtual bool IsBaseType { get { return helper.IsBaseType; } } } /// @@ -114,15 +114,15 @@ public FolderType(ISession session, IFolderTypeDefinition typeDefinition) helper = new ObjectTypeHelper(session, this); } - public IObjectType GetBaseType() { return helper.GetBaseType(); } + public virtual IObjectType GetBaseType() { return helper.GetBaseType(); } - public IItemEnumerable GetChildren() { return helper.GetChildren(); } + public virtual IItemEnumerable GetChildren() { return helper.GetChildren(); } - public IList> GetDescendants(int depth) { return helper.GetDescendants(depth); } + public virtual IList> GetDescendants(int depth) { return helper.GetDescendants(depth); } - public IObjectType GetParentType() { return helper.GetParentType(); } + public virtual IObjectType GetParentType() { return helper.GetParentType(); } - public bool IsBaseType { get { return helper.IsBaseType; } } + public virtual bool IsBaseType { get { return helper.IsBaseType; } } } public class SecondaryType : SecondaryTypeDefinition, ISecondaryType @@ -162,17 +162,17 @@ public RelationshipType(ISession session, IRelationshipTypeDefinition typeDefini helper = new ObjectTypeHelper(session, this); } - public IObjectType GetBaseType() { return helper.GetBaseType(); } + public virtual IObjectType GetBaseType() { return helper.GetBaseType(); } - public IItemEnumerable GetChildren() { return helper.GetChildren(); } + public virtual IItemEnumerable GetChildren() { return helper.GetChildren(); } - public IList> GetDescendants(int depth) { return helper.GetDescendants(depth); } + public virtual IList> GetDescendants(int depth) { return helper.GetDescendants(depth); } - public IObjectType GetParentType() { return helper.GetParentType(); } + public virtual IObjectType GetParentType() { return helper.GetParentType(); } - public bool IsBaseType { get { return helper.IsBaseType; } } + public virtual bool IsBaseType { get { return helper.IsBaseType; } } - public IList GetAllowedSourceTypes + public virtual IList GetAllowedSourceTypes { get { @@ -193,7 +193,7 @@ public IList GetAllowedSourceTypes } } - public IList GetAllowedTargetTypes + public virtual IList GetAllowedTargetTypes { get { @@ -228,15 +228,15 @@ public PolicyType(ISession session, IPolicyTypeDefinition typeDefinition) helper = new ObjectTypeHelper(session, this); } - public IObjectType GetBaseType() { return helper.GetBaseType(); } + public virtual IObjectType GetBaseType() { return helper.GetBaseType(); } - public IItemEnumerable GetChildren() { return helper.GetChildren(); } + public virtual IItemEnumerable GetChildren() { return helper.GetChildren(); } - public IList> GetDescendants(int depth) { return helper.GetDescendants(depth); } + public virtual IList> GetDescendants(int depth) { return helper.GetDescendants(depth); } - public IObjectType GetParentType() { return helper.GetParentType(); } + public virtual IObjectType GetParentType() { return helper.GetParentType(); } - public bool IsBaseType { get { return helper.IsBaseType; } } + public virtual bool IsBaseType { get { return helper.IsBaseType; } } } /// diff --git a/DotCMIS/const.cs b/DotCMIS/const.cs index 29a1775..201db6c 100644 --- a/DotCMIS/const.cs +++ b/DotCMIS/const.cs @@ -87,6 +87,9 @@ public static class SessionParameter // http user agent public const string UserAgent = "org.apache.chemistry.dotcmis.http.useragent"; + // http stability tweek + public const string MaximumRequestRetries = "org.apache.chemistry.dotcmis.http.maximumretries"; + // session parameter public const string ObjectFactoryClass = "org.apache.chemistry.dotcmis.objectfactory.classname"; public const string CacheClass = "org.apache.chemistry.dotcmis.cache.classname"; @@ -126,6 +129,7 @@ public static class PropertyIds public const string IsLatestVersion = "cmis:isLatestVersion"; public const string IsMajorVersion = "cmis:isMajorVersion"; public const string IsLatestMajorVersion = "cmis:isLatestMajorVersion"; + public const string IsPrivateWorkingCopy = "cmis:isPrivateWorkingCopy"; public const string VersionLabel = "cmis:versionLabel"; public const string VersionSeriesId = "cmis:versionSeriesId"; public const string IsVersionSeriesCheckedOut = "cmis:isVersionSeriesCheckedOut"; @@ -624,6 +628,7 @@ internal static class BrowserConstants }; public const string ObjectListObjects = "objects"; + public const string ObjectListObject = "object"; public const string ObjectListHasMoreItems = "hasMoreItems"; public const string ObjectListNumItems = "numItems"; public const string ObjectListChangeLogToken = "changeLogToken"; @@ -780,6 +785,8 @@ internal static class BrowserConstants public const string ActionCancelCheckOut = "cancelCheckOut"; public const string ActionCheckIn = "checkIn"; public const string ActionCreateRelationship = "createRelationship"; + public const string ActionCreateItem = "createItem"; + public const string ActionCreatePolicy = "createPolicy"; public const string ActionQuery = "query"; public const string ActionApplyPolicy = "applyPolicy"; public const string ActionRemovePolicy = "removePolicy"; diff --git a/DotCMIS/exceptions.cs b/DotCMIS/exceptions.cs index 4746a66..1997d7a 100644 --- a/DotCMIS/exceptions.cs +++ b/DotCMIS/exceptions.cs @@ -54,6 +54,20 @@ public CmisBaseException(string message, string errorContent, Exception inner) public long? Code { get; protected set; } public string ErrorContent { get; protected set; } + + public override string StackTrace { + get { + if (string.IsNullOrEmpty(this.ErrorContent)) { + return base.StackTrace; + } else { + return string.Format( + "----- Begin ErrorContent -----{0}{1}{0}----- End ErrorContent -----{0}{2}", + Environment.NewLine, + this.ErrorContent, + base.StackTrace); + } + } + } } [Serializable] @@ -69,7 +83,7 @@ protected CmisConnectionException( public CmisConnectionException(string message, long? code) : base(message) { } public CmisConnectionException(string message, string errorContent) - : base(message) { } + : base(message, errorContent) { } public CmisConnectionException(string message, string errorContent, Exception inner) : base(message, errorContent, inner) { } } @@ -87,7 +101,7 @@ protected CmisConstraintException( public CmisConstraintException(string message, long? code) : base(message) { } public CmisConstraintException(string message, string errorContent) - : base(message) { } + : base(message, errorContent) { } public CmisConstraintException(string message, string errorContent, Exception inner) : base(message, errorContent, inner) { } } @@ -105,7 +119,7 @@ protected CmisContentAlreadyExistsException( public CmisContentAlreadyExistsException(string message, long? code) : base(message) { } public CmisContentAlreadyExistsException(string message, string errorContent) - : base(message) { } + : base(message, errorContent) { } public CmisContentAlreadyExistsException(string message, string errorContent, Exception inner) : base(message, errorContent, inner) { } } @@ -123,7 +137,7 @@ protected CmisFilterNotValidException( public CmisFilterNotValidException(string message, long? code) : base(message) { } public CmisFilterNotValidException(string message, string errorContent) - : base(message) { } + : base(message, errorContent) { } public CmisFilterNotValidException(string message, string errorContent, Exception inner) : base(message, errorContent, inner) { } } @@ -141,7 +155,7 @@ protected CmisInvalidArgumentException( public CmisInvalidArgumentException(string message, long? code) : base(message) { } public CmisInvalidArgumentException(string message, string errorContent) - : base(message) { } + : base(message, errorContent) { } public CmisInvalidArgumentException(string message, string errorContent, Exception inner) : base(message, errorContent, inner) { } } @@ -159,7 +173,7 @@ protected CmisNameConstraintViolationException( public CmisNameConstraintViolationException(string message, long? code) : base(message) { } public CmisNameConstraintViolationException(string message, string errorContent) - : base(message) { } + : base(message, errorContent) { } public CmisNameConstraintViolationException(string message, string errorContent, Exception inner) : base(message, errorContent, inner) { } } @@ -177,7 +191,7 @@ protected CmisNotSupportedException( public CmisNotSupportedException(string message, long? code) : base(message) { } public CmisNotSupportedException(string message, string errorContent) - : base(message) { } + : base(message, errorContent) { } public CmisNotSupportedException(string message, string errorContent, Exception inner) : base(message, errorContent, inner) { } } @@ -195,7 +209,7 @@ protected CmisObjectNotFoundException( public CmisObjectNotFoundException(string message, long? code) : base(message) { } public CmisObjectNotFoundException(string message, string errorContent) - : base(message) { } + : base(message, errorContent) { } public CmisObjectNotFoundException(string message, string errorContent, Exception inner) : base(message, errorContent, inner) { } } @@ -213,7 +227,7 @@ protected CmisPermissionDeniedException( public CmisPermissionDeniedException(string message, long? code) : base(message) { } public CmisPermissionDeniedException(string message, string errorContent) - : base(message) { } + : base(message, errorContent) { } public CmisPermissionDeniedException(string message, string errorContent, Exception inner) : base(message, errorContent, inner) { } } @@ -231,7 +245,7 @@ protected CmisRuntimeException( public CmisRuntimeException(string message, long? code) : base(message) { } public CmisRuntimeException(string message, string errorContent) - : base(message) { } + : base(message, errorContent) { } public CmisRuntimeException(string message, string errorContent, Exception inner) : base(message, errorContent, inner) { } } @@ -249,7 +263,7 @@ protected CmisStorageException( public CmisStorageException(string message, long? code) : base(message) { } public CmisStorageException(string message, string errorContent) - : base(message) { } + : base(message, errorContent) { } public CmisStorageException(string message, string errorContent, Exception inner) : base(message, errorContent, inner) { } } @@ -267,7 +281,7 @@ protected CmisStreamNotSupportedException( public CmisStreamNotSupportedException(string message, long? code) : base(message) { } public CmisStreamNotSupportedException(string message, string errorContent) - : base(message) { } + : base(message, errorContent) { } public CmisStreamNotSupportedException(string message, string errorContent, Exception inner) : base(message, errorContent, inner) { } } @@ -285,7 +299,7 @@ protected CmisUpdateConflictException( public CmisUpdateConflictException(string message, long? code) : base(message) { } public CmisUpdateConflictException(string message, string errorContent) - : base(message) { } + : base(message, errorContent) { } public CmisUpdateConflictException(string message, string errorContent, Exception inner) : base(message, errorContent, inner) { } } @@ -303,8 +317,8 @@ protected CmisVersioningException( public CmisVersioningException(string message, long? code) : base(message) { } public CmisVersioningException(string message, string errorContent) - : base(message) { } + : base(message, errorContent) { } public CmisVersioningException(string message, string errorContent, Exception inner) : base(message, errorContent, inner) { } } -} +} \ No newline at end of file diff --git a/DotCMIS/packages.config b/DotCMIS/packages.config index f6a2459..3ac53f2 100644 --- a/DotCMIS/packages.config +++ b/DotCMIS/packages.config @@ -1,4 +1,5 @@ - - - + + + + \ No newline at end of file diff --git a/DotCMISUnitTest/DotCMISUnitTest.csproj b/DotCMISUnitTest/DotCMISUnitTest.csproj index c8e7c8b..71ea915 100644 --- a/DotCMISUnitTest/DotCMISUnitTest.csproj +++ b/DotCMISUnitTest/DotCMISUnitTest.csproj @@ -10,7 +10,6 @@ Properties DotCMISUnitTest DotCMISUnitTest - v3.5 512 @@ -44,10 +43,6 @@ false - - False - ..\..\..\..\Program Files (x86)\NUnit 2.5.9\bin\net-2.0\framework\nunit.framework.dll - @@ -57,7 +52,7 @@ - ..\..\DataSpaceSync\packages\NUnit.2.6.3\lib\nunit.framework.dll + ..\packages\NUnit.2.6.4\lib\nunit.framework.dll @@ -82,6 +77,7 @@ Always + - \ No newline at end of file + diff --git a/DotCMISUnitTest/app.config b/DotCMISUnitTest/app.config index b1bf321..c423f6a 100644 --- a/DotCMISUnitTest/app.config +++ b/DotCMISUnitTest/app.config @@ -1,14 +1,13 @@  - + -