You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@chemistry.apache.org by fm...@apache.org on 2015/07/20 10:48:59 UTC

svn commit: r1691890 [5/14] - in /chemistry/portcmis: ./ PortCMIS/ PortCMIS/Properties/ PortCMIS/binding/ PortCMIS/binding/atompub/ PortCMIS/binding/browser/ PortCMIS/binding/browser/json/ PortCMIS/client/ PortCMIS/const/ PortCMIS/data/ PortCMIS/enum/ ...

Added: chemistry/portcmis/PortCMIS/binding/browser/BrowserBinding.cs
URL: http://svn.apache.org/viewvc/chemistry/portcmis/PortCMIS/binding/browser/BrowserBinding.cs?rev=1691890&view=auto
==============================================================================
--- chemistry/portcmis/PortCMIS/binding/browser/BrowserBinding.cs (added)
+++ chemistry/portcmis/PortCMIS/binding/browser/BrowserBinding.cs Mon Jul 20 08:48:57 2015
@@ -0,0 +1,2216 @@
+/*
+* 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 PortCMIS.binding;
+using PortCMIS.Binding.Browser.Json;
+using PortCMIS.Binding.Http;
+using PortCMIS.Binding.Impl;
+using PortCMIS.Binding.Services;
+using PortCMIS.Client;
+using PortCMIS.Data;
+using PortCMIS.Data.Extensions;
+using PortCMIS.Enums;
+using PortCMIS.Exceptions;
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Net;
+using System.Net.Http;
+using System.Net.Http.Headers;
+using System.Numerics;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace PortCMIS.Binding.Browser
+{
+    public class RepositoryInfoBrowserBinding : RepositoryInfo
+    {
+        public string RepositoryUrl { get; set; }
+        public string RootUrl { get; set; }
+    }
+
+    /// <summary>
+    /// Browser binding SPI.
+    /// </summary>
+    internal class CmisBrowserSpi : ICmisSpi
+    {
+        public const string RepositoryUrlCache = "org.apache.chemistry.portcmis.binding.browser.repositoryurls";
+
+        private BindingSession session;
+        private RepositoryService repositoryService;
+        private NavigationService navigationService;
+        private ObjectService objectService;
+        private VersioningService versioningService;
+        private DiscoveryService discoveryService;
+        private MultiFilingService multiFilingService;
+        private RelationshipService relationshipService;
+        private PolicyService policyService;
+        private AclService aclService;
+
+        public void Initialize(IBindingSession session)
+        {
+            this.session = session as BindingSession;
+            if (this.session == null)
+            {
+                throw new ArgumentException("Inavlid binding 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()
+        {
+            return repositoryService;
+        }
+
+        public INavigationService GetNavigationService()
+        {
+            return navigationService;
+        }
+
+        public IObjectService GetObjectService()
+        {
+            return objectService;
+        }
+
+        public IVersioningService GetVersioningService()
+        {
+            return versioningService;
+        }
+
+        public IRelationshipService GetRelationshipService()
+        {
+            return relationshipService;
+        }
+
+        public IDiscoveryService GetDiscoveryService()
+        {
+            return discoveryService;
+        }
+
+        public IMultiFilingService GetMultiFilingService()
+        {
+            return multiFilingService;
+        }
+
+        public IAclService GetAclService()
+        {
+            return aclService;
+        }
+
+        public IPolicyService GetPolicyService()
+        {
+            return policyService;
+        }
+
+        public void ClearAllCaches()
+        {
+            session.RemoveValue(RepositoryUrlCache);
+        }
+
+        public void ClearRepositoryCache(string repositoryId)
+        {
+            RepositoryUrlCache repUrlCache = session.GetValue(RepositoryUrlCache) as RepositoryUrlCache;
+            if (repUrlCache != null)
+            {
+                repUrlCache.RemoveRepository(repositoryId);
+            }
+        }
+
+        public void Dispose()
+        {
+            // nothing to do
+        }
+    }
+
+    internal class ClientTypeCache : ITypeCache
+    {
+        private string RepositoryId { get; set; }
+        private AbstractBrowserBindingService Service { get; set; }
+
+        public ClientTypeCache(string repositoryId, AbstractBrowserBindingService service)
+        {
+            this.RepositoryId = repositoryId;
+            this.Service = service;
+        }
+
+        public ITypeDefinition GetTypeDefinition(string typeId)
+        {
+            TypeDefinitionCache cache = Service.Session.GetTypeDefinitionCache();
+
+            ITypeDefinition type = cache.Get(RepositoryId, typeId);
+            if (type == null)
+            {
+                type = Service.GetTypeDefinitionInternal(RepositoryId, typeId);
+                if (type != null)
+                {
+                    cache.Put(RepositoryId, type);
+                }
+            }
+
+            return type;
+        }
+
+        public ITypeDefinition ReloadTypeDefinition(string typeId)
+        {
+            TypeDefinitionCache cache = Service.Session.GetTypeDefinitionCache();
+
+            ITypeDefinition type = Service.GetTypeDefinitionInternal(RepositoryId, typeId);
+            if (type != null)
+            {
+                cache.Put(RepositoryId, type);
+            }
+
+            return type;
+        }
+
+        public ITypeDefinition GetTypeDefinitionForObject(string objectId)
+        {
+            return null;
+        }
+
+        public IPropertyDefinition GetPropertyDefinition(string propId)
+        {
+            return null;
+        }
+    }
+
+    internal class FormDataComposer
+    {
+        public string CmisAction { get; private set; }
+
+        public IContentStream Stream { get; set; }
+
+        public IProperties Properties { get; set; }
+        public bool Succinct { get; set; }
+        public DateTimeFormat DateTimeFormat { get; set; }
+
+        public IAcl AddAces { get; set; }
+        public IAcl RemoveAces { get; set; }
+
+        public IList<string> Policies { get; set; }
+        public string PolicyId { get; set; }
+
+        public IList<string> AddSecondaryTypeIds { get; set; }
+        public IList<string> RemoveSecondaryTypeIds { get; set; }
+
+        public IList<IBulkUpdateObjectIdAndChangeToken> ObjectIdsAndChangeTokens { get; set; }
+
+        private IDictionary<string, object> parameters;
+
+        public IDictionary<string, object> Parameters
+        {
+            get
+            {
+                if (parameters == null)
+                {
+                    parameters = new Dictionary<string, object>();
+                }
+                return parameters;
+            }
+            set { parameters = value; }
+        }
+
+        public FormDataComposer(string cmisAction)
+        {
+            CmisAction = cmisAction;
+        }
+
+        public HttpContent CreateHttpContent()
+        {
+            if (Stream == null)
+            {
+                return CreateFormUrlEncodedContent();
+            }
+            else
+            {
+                return CreateMultipartFormDataContent();
+            }
+        }
+
+        protected FormUrlEncodedContent CreateFormUrlEncodedContent()
+        {
+            return new FormUrlEncodedContent(CreateContent());
+        }
+
+        protected MultipartFormDataContent CreateMultipartFormDataContent()
+        {
+            MultipartFormDataContent content = new MultipartFormDataContent();
+
+            IList<KeyValuePair<string, string>> parameters = CreateContent();
+            foreach (KeyValuePair<string, string> p in parameters)
+            {
+                content.Add(new StringContent(p.Value, Encoding.UTF8), p.Key);
+            }
+
+            if (Stream.Stream != null)
+            {
+                StreamContent streamContent = new StreamContent(Stream.Stream);
+                streamContent.Headers.ContentType = new MediaTypeHeaderValue(Stream.MimeType ?? "applictaion/octet-stream");
+
+                content.Add(streamContent, "content", Stream.FileName ?? "content");
+            }
+
+            return content;
+        }
+
+        protected IList<KeyValuePair<string, string>> CreateContent()
+        {
+            IList<KeyValuePair<string, string>> result = new List<KeyValuePair<string, string>>();
+
+            // set cmisaction
+            result.Add(new KeyValuePair<string, string>(BindingConstants.ControlCmisAction, CmisAction));
+
+            // set parameters
+            if (parameters != null)
+            {
+                foreach (KeyValuePair<string, object> p in parameters)
+                {
+                    if (p.Value != null)
+                    {
+                        result.Add(new KeyValuePair<string, string>(p.Key, UrlBuilder.NormalizeParameter(p.Value)));
+                    }
+                }
+            }
+
+            // set succinct
+            if (Succinct)
+            {
+                result.Add(new KeyValuePair<string, string>(BindingConstants.ControlSuccinct, "true"));
+            }
+
+            // set properties
+            AddPropertiesParameters(Properties, result);
+
+            // set Aces
+            AddAcesParameters(AddAces, BindingConstants.ControlAddAcePrincipal, BindingConstants.ControlRemoveAcePermission, result);
+            AddAcesParameters(RemoveAces, BindingConstants.ControlRemoveAcePrincipal, BindingConstants.ControlRemoveAcePermission, result);
+
+            // set policies
+            AddPoliciesParameters(Policies, result);
+            AddPolicyIdParameter(PolicyId, result);
+
+            // set secondary type IDs
+            AddSecondaryTypeIdParameters(AddSecondaryTypeIds, BindingConstants.ControlAddSecondaryType, result);
+            AddSecondaryTypeIdParameters(RemoveSecondaryTypeIds, BindingConstants.ControlRemoveSecondaryType, result);
+
+            // set bulk update values
+            AddObjectIdsAndChangeTokens(ObjectIdsAndChangeTokens, result);
+
+            return result;
+        }
+
+        private void AddPropertiesParameters(IProperties properties, IList<KeyValuePair<string, string>> result)
+        {
+            if (properties == null || properties.PropertyList == null)
+            {
+                return;
+            }
+
+            int idx = 0;
+            foreach (PropertyData prop in properties.PropertyList)
+            {
+                if (prop == null)
+                {
+                    continue;
+                }
+
+                string idxStr = "[" + idx + "]";
+                result.Add(new KeyValuePair<string, string>(BindingConstants.ControlPropId + idxStr, prop.Id));
+
+                if (prop.Values != null && prop.Values.Count > 0)
+                {
+                    if (prop.Values.Count == 1)
+                    {
+                        result.Add(new KeyValuePair<string, string>(
+                            BindingConstants.ControlPropValue + idxStr, ConvertPropertyValue(prop.FirstValue, DateTimeFormat)));
+                    }
+                    else
+                    {
+                        int vidx = 0;
+                        foreach (object obj in prop.Values)
+                        {
+                            string vidxStr = "[" + vidx + "]";
+                            result.Add(new KeyValuePair<string, string>(
+                                BindingConstants.ControlPropValue + idxStr + vidxStr, ConvertPropertyValue(obj, DateTimeFormat)));
+                            vidx++;
+                        }
+                    }
+                }
+                idx++;
+            }
+        }
+
+        private void AddAcesParameters(IAcl acl, string principalControl, string permissionControl, IList<KeyValuePair<string, string>> result)
+        {
+            if (acl == null || acl.Aces == null)
+            {
+                return;
+            }
+
+            int idx = 0;
+            foreach (IAce ace in acl.Aces)
+            {
+                if (ace.PrincipalId != null && ace.Permissions != null && ace.Permissions.Count > 0)
+                {
+                    string idxStr = "[" + idx + "]";
+                    result.Add(new KeyValuePair<string, string>(principalControl + idxStr, ace.PrincipalId));
+
+                    int permIdx = 0;
+                    foreach (string perm in ace.Permissions)
+                    {
+                        if (perm != null)
+                        {
+                            string permIdxStr = "[" + permIdx + "]";
+                            result.Add(new KeyValuePair<string, string>(permissionControl + idxStr + permIdxStr, perm));
+                            permIdx++;
+                        }
+                    }
+                    idx++;
+                }
+            }
+        }
+
+        private void AddPoliciesParameters(IList<string> policies, IList<KeyValuePair<string, string>> result)
+        {
+            if (policies == null)
+            {
+                return;
+            }
+
+            int idx = 0;
+            foreach (string policy in policies)
+            {
+                if (policy != null)
+                {
+                    string idxStr = "[" + idx + "]";
+                    result.Add(new KeyValuePair<string, string>(BindingConstants.ControlPolicy + idxStr, policy));
+                    idx++;
+                }
+            }
+        }
+
+        private void AddPolicyIdParameter(string policyId, IList<KeyValuePair<string, string>> result)
+        {
+            if (policyId == null)
+            {
+                return;
+            }
+
+            result.Add(new KeyValuePair<string, string>(BindingConstants.ControlPolicyId, policyId));
+        }
+
+        private void AddSecondaryTypeIdParameters(IList<string> secondaryTypeIds, string secondaryTypeIdControl, IList<KeyValuePair<string, string>> result)
+        {
+            if (secondaryTypeIds == null || secondaryTypeIds.Count == 0)
+            {
+                return;
+            }
+
+            int idx = 0;
+            foreach (string typeId in secondaryTypeIds)
+            {
+                if (typeId == null || typeId.Length == 0)
+                {
+                    continue;
+                }
+
+                string idxStr = "[" + idx + "]";
+                result.Add(new KeyValuePair<string, string>(secondaryTypeIdControl + idxStr, typeId));
+
+                idx++;
+            }
+        }
+
+        public void AddObjectIdsAndChangeTokens(IList<IBulkUpdateObjectIdAndChangeToken> objectIdsAndChangeTokens, IList<KeyValuePair<string, string>> result)
+        {
+            if (objectIdsAndChangeTokens == null || objectIdsAndChangeTokens.Count == 0)
+            {
+                return;
+            }
+
+            int idx = 0;
+            foreach (IBulkUpdateObjectIdAndChangeToken oc in objectIdsAndChangeTokens)
+            {
+                if (oc == null || oc.Id == null || oc.Id.Length == 0)
+                {
+                    continue;
+                }
+
+                string idxStr = "[" + idx + "]";
+                result.Add(new KeyValuePair<string, string>(BindingConstants.ControlObjectId + idxStr, oc.Id));
+                result.Add(new KeyValuePair<string, string>(BindingConstants.ControlChangeToken + idxStr, oc.ChangeToken ?? ""));
+
+                idx++;
+            }
+        }
+
+        private string ConvertPropertyValue(object value, DateTimeFormat dateTimeFormat)
+        {
+            if (value == null)
+            {
+                return null;
+            }
+            else if (value is DateTime)
+            {
+                if (DateTimeFormat == DateTimeFormat.Extended)
+                {
+                    return DateTimeHelper.FormatISO8601((DateTime)value);
+                }
+                else
+                {
+                    return DateTimeHelper.ConvertDateTimeToMillis((DateTime)value).ToString();
+                }
+            }
+            else if (value is decimal)
+            {
+                return ((decimal)value).ToString("#", CultureInfo.InvariantCulture);
+            }
+            else if (value is BigInteger)
+            {
+                return ((BigInteger)value).ToString("#", CultureInfo.InvariantCulture);
+            }
+            else
+            {
+                return value.ToString();
+            }
+        }
+    }
+
+
+    /// <summary>
+    /// Common service data and operations.
+    /// </summary>
+    internal abstract class AbstractBrowserBindingService
+    {
+        private BindingSession session;
+
+        public BindingSession Session
+        {
+            get
+            {
+                return session;
+            }
+            protected set
+            {
+                session = value;
+
+                Succinct = true;
+                object succintObj = session.GetValue(SessionParameter.BrowserSuccinct);
+                if (succintObj is string)
+                {
+                    Succinct = Convert.ToBoolean((string)succintObj);
+                }
+
+                DateTimeFormat = DateTimeFormat.Simple;
+                object dateTimeFormatObj = session.GetValue(SessionParameter.BrowserDateTimeFormat);
+                if (dateTimeFormatObj is string)
+                {
+                    DateTimeFormat? dtf = ((string)dateTimeFormatObj).GetCmisEnum<DateTimeFormat>();
+                    DateTimeFormat = dtf ?? DateTimeFormat.Simple;
+                }
+            }
+        }
+        protected bool Succinct { get; private set; }
+        protected string SuccinctParameter { get { return Succinct ? "true" : null; } }
+
+        protected DateTimeFormat DateTimeFormat { get; private set; }
+        protected string DateTimeFormatParameter { get { return DateTimeFormat == DateTimeFormat.Simple ? null : DateTimeFormat.GetCmisValue(); } }
+
+        protected string GetServiceUrl()
+        {
+            return Session.GetValue(SessionParameter.BrowserUrl) as string;
+        }
+
+        protected UrlBuilder GetRepositoryUrl(string repositoryId, string selector)
+        {
+            UrlBuilder result = GetRepositoryUrlCache().GetRepositoryUrl(repositoryId, selector);
+
+            if (result == null)
+            {
+                GetRepositoriesInternal(repositoryId);
+                result = GetRepositoryUrlCache().GetRepositoryUrl(repositoryId, selector);
+            }
+
+            if (result == null)
+            {
+                throw new CmisObjectNotFoundException("Unknown repository!");
+            }
+
+            return result;
+        }
+
+        protected UrlBuilder GetRepositoryUrl(string repositoryId)
+        {
+            UrlBuilder result = GetRepositoryUrlCache().GetRepositoryUrl(repositoryId);
+
+            if (result == null)
+            {
+                GetRepositoriesInternal(repositoryId);
+                result = GetRepositoryUrlCache().GetRepositoryUrl(repositoryId);
+            }
+
+            if (result == null)
+            {
+                throw new CmisObjectNotFoundException("Unknown repository!");
+            }
+
+            return result;
+        }
+
+        protected UrlBuilder GetObjectUrl(string repositoryId, string objectId, string selector)
+        {
+            UrlBuilder result = GetRepositoryUrlCache().GetObjectUrl(repositoryId, objectId, selector);
+
+            if (result == null)
+            {
+                GetRepositoriesInternal(repositoryId);
+                result = GetRepositoryUrlCache().GetObjectUrl(repositoryId, objectId, selector);
+            }
+
+            if (result == null)
+            {
+                throw new CmisObjectNotFoundException("Unknown repository!");
+            }
+
+            return result;
+        }
+
+        protected UrlBuilder GetObjectUrl(string repositoryId, string objectId)
+        {
+            UrlBuilder result = GetRepositoryUrlCache().GetObjectUrl(repositoryId, objectId);
+
+            if (result == null)
+            {
+                GetRepositoriesInternal(repositoryId);
+                result = GetRepositoryUrlCache().GetObjectUrl(repositoryId, objectId);
+            }
+
+            if (result == null)
+            {
+                throw new CmisObjectNotFoundException("Unknown repository!");
+            }
+
+            return result;
+        }
+
+        protected UrlBuilder GetPathUrl(string repositoryId, string path, string selector)
+        {
+            UrlBuilder result = GetRepositoryUrlCache().GetPathUrl(repositoryId, path, selector);
+
+            if (result == null)
+            {
+                GetRepositoriesInternal(repositoryId);
+                result = GetRepositoryUrlCache().GetPathUrl(repositoryId, path, selector);
+            }
+
+            if (result == null)
+            {
+                throw new CmisObjectNotFoundException("Unknown repository!");
+            }
+
+            return result;
+        }
+
+
+        protected RepositoryUrlCache GetRepositoryUrlCache()
+        {
+            RepositoryUrlCache repositoryUrlCache = Session.GetValue(CmisBrowserSpi.RepositoryUrlCache) as RepositoryUrlCache;
+            if (repositoryUrlCache == null)
+            {
+                repositoryUrlCache = new RepositoryUrlCache();
+                Session.PutValue(CmisBrowserSpi.RepositoryUrlCache, repositoryUrlCache);
+            }
+
+            return repositoryUrlCache;
+        }
+
+        protected void SetChangeToken(ref string changeToken, IObjectData obj)
+        {
+            if (changeToken == null)
+            {
+                return;
+            }
+
+            changeToken = null;
+
+            if (obj == null || obj.Properties == null)
+            {
+                return;
+            }
+
+            IPropertyData ct = obj.Properties[PropertyIds.ChangeToken];
+            if (ct != null && ct.PropertyType == PropertyType.String)
+            {
+                changeToken = (string)ct.FirstValue;
+            }
+        }
+
+        // ---- exceptions ----
+
+        /// <summary>
+        /// Converts an error message or a HTTP status code into an Exception.
+        /// </summary>
+        protected CmisBaseException ConvertStatusCode(int code, string message, string errorContent, Exception e)
+        {
+            object obj = null;
+            try
+            {
+                if (errorContent != null)
+                {
+                    JsonParser parser = new JsonParser();
+                    obj = parser.Parse(new StringReader(errorContent));
+                }
+            }
+            catch (JsonParseException)
+            {
+                // error content is not valid JSON -> ignore
+            }
+
+            if (obj is JsonObject)
+            {
+                JsonObject json = (JsonObject)obj;
+                object jsonError = json[BrowserConstants.ErrorException];
+                if (jsonError is string)
+                {
+                    object jsonMessage = json[BrowserConstants.ErrorMessage];
+                    if (jsonMessage != null)
+                    {
+                        message = jsonMessage.ToString();
+                    }
+
+                    if (CmisConstraintException.ExceptionName.Equals((string)jsonError, StringComparison.OrdinalIgnoreCase))
+                    {
+                        return new CmisConstraintException(message, errorContent, e);
+                    }
+                    else if (CmisContentAlreadyExistsException.ExceptionName.Equals((string)jsonError, StringComparison.OrdinalIgnoreCase))
+                    {
+                        return new CmisContentAlreadyExistsException(message, errorContent, e);
+                    }
+                    else if (CmisFilterNotValidException.ExceptionName.Equals((string)jsonError, StringComparison.OrdinalIgnoreCase))
+                    {
+                        return new CmisFilterNotValidException(message, errorContent, e);
+                    }
+                    else if (CmisInvalidArgumentException.ExceptionName.Equals((string)jsonError, StringComparison.OrdinalIgnoreCase))
+                    {
+                        return new CmisInvalidArgumentException(message, errorContent, e);
+                    }
+                    else if (CmisNameConstraintViolationException.ExceptionName.Equals((string)jsonError, StringComparison.OrdinalIgnoreCase))
+                    {
+                        return new CmisNameConstraintViolationException(message, errorContent, e);
+                    }
+                    else if (CmisNotSupportedException.ExceptionName.Equals((string)jsonError, StringComparison.OrdinalIgnoreCase))
+                    {
+                        return new CmisNotSupportedException(message, errorContent, e);
+                    }
+                    else if (CmisObjectNotFoundException.ExceptionName.Equals((string)jsonError, StringComparison.OrdinalIgnoreCase))
+                    {
+                        return new CmisObjectNotFoundException(message, errorContent, e);
+                    }
+                    else if (CmisPermissionDeniedException.ExceptionName.Equals((string)jsonError, StringComparison.OrdinalIgnoreCase))
+                    {
+                        return new CmisPermissionDeniedException(message, errorContent, e);
+                    }
+                    else if (CmisStorageException.ExceptionName.Equals((string)jsonError, StringComparison.OrdinalIgnoreCase))
+                    {
+                        return new CmisStorageException(message, errorContent, e);
+                    }
+                    else if (CmisStreamNotSupportedException.ExceptionName.Equals((string)jsonError, StringComparison.OrdinalIgnoreCase))
+                    {
+                        return new CmisStreamNotSupportedException(message, errorContent, e);
+                    }
+                    else if (CmisUpdateConflictException.ExceptionName.Equals((string)jsonError, StringComparison.OrdinalIgnoreCase))
+                    {
+                        return new CmisUpdateConflictException(message, errorContent, e);
+                    }
+                    else if (CmisVersioningException.ExceptionName.Equals((string)jsonError, StringComparison.OrdinalIgnoreCase))
+                    {
+                        return new CmisVersioningException(message, errorContent, e);
+                    }
+                    else if (code == 503)
+                    {
+                        return new CmisServiceUnavailableException(message, errorContent, e);
+                    }
+                }
+            }
+
+            // fall back to status code
+            switch (code)
+            {
+                case 301:
+                case 302:
+                case 303:
+                case 307:
+                    return new CmisConnectionException("Redirects are not supported (HTTP status code " + code + "): " + message, errorContent, e);
+                case 400:
+                    return new CmisInvalidArgumentException(message, errorContent, e);
+                case 401:
+                    return new CmisUnauthorizedException(message, errorContent, e);
+                case 403:
+                    return new CmisPermissionDeniedException(message, errorContent, e);
+                case 404:
+                    return new CmisObjectNotFoundException(message, errorContent, e);
+                case 405:
+                    return new CmisNotSupportedException(message, errorContent, e);
+                case 407:
+                    return new CmisProxyAuthenticationException(message, errorContent, e);
+                case 409:
+                    return new CmisConstraintException(message, errorContent, e);
+                case 503:
+                    return new CmisServiceUnavailableException(message, errorContent, e);
+                default:
+                    return new CmisRuntimeException(message, errorContent, e);
+            }
+        }
+
+
+        // ---- helpers ----
+
+        /// <summary>
+        /// Parses an object from an input stream.
+        /// </summary>
+        protected JsonObject ParseObject(Stream stream, string charset)
+        {
+            object obj = Parse(stream, charset);
+
+            if (obj is JsonObject)
+            {
+                return (JsonObject)obj;
+            }
+
+            throw new CmisConnectionException("Unexpected object!");
+        }
+
+        /// <summary>
+        /// Parses an array from an input stream.
+        /// </summary>
+        protected JsonArray ParseArray(Stream stream, string charset)
+        {
+            object obj = Parse(stream, charset);
+
+            if (obj is JsonArray)
+            {
+                return (JsonArray)obj;
+            }
+
+            throw new CmisConnectionException("Unexpected object!");
+        }
+
+        /// <summary>
+        /// Parses an input stream.
+        /// </summary>
+        protected object Parse(Stream stream, string charset)
+        {
+            Encoding enc;
+            if (charset == null)
+            {
+                enc = Encoding.UTF8;
+            }
+            else
+            {
+                try
+                {
+                    enc = Encoding.GetEncoding(charset);
+                }
+                catch (Exception e)
+                {
+                    throw new CmisConnectionException("Unsupported charset: " + charset, e);
+                }
+            }
+
+            StreamReader reader = null;
+
+            object obj = null;
+            try
+            {
+                reader = new StreamReader(stream, enc);
+                JsonParser parser = new JsonParser();
+                obj = parser.Parse(reader);
+            }
+            catch (Exception e)
+            {
+                throw new CmisConnectionException("Parsing exception!", e);
+            }
+            finally
+            {
+                IOUtils.ConsumeAndClose(reader);
+                if (reader == null)
+                {
+                    stream.Dispose();
+                }
+            }
+
+            return obj;
+        }
+
+        /// <summary>
+        /// Performs a GET on an URL, checks the response code and returns the
+        /// result.
+        /// </summary>
+        protected IResponse Read(UrlBuilder url)
+        {
+            // make the call
+            IResponse resp = Session.GetHttpInvoker().InvokeGET(url, session);
+
+            // check response code
+            if (resp.StatusCode != 200)
+            {
+                throw ConvertStatusCode(resp.StatusCode, resp.Message, resp.ErrorContent, null);
+            }
+
+            return resp;
+        }
+
+
+        /// <summary>
+        /// Performs a POST on an URL, checks the response code and returns the
+        /// result.
+        /// </summary>
+        protected IResponse Post(UrlBuilder url, HttpContent content)
+        {
+            // make the call
+            IResponse resp = Session.GetHttpInvoker().InvokePOST(url, content, session);
+
+            // check response code
+            if (resp.StatusCode != 200 && resp.StatusCode != 201)
+            {
+                throw ConvertStatusCode(resp.StatusCode, resp.Message, resp.ErrorContent, null);
+            }
+
+            return resp;
+        }
+
+        /// <summary>
+        /// Performs a POST on an URL, checks the response code and returns the
+        /// result.
+        /// </summary>
+        protected void PostAndConsume(UrlBuilder url, HttpContent content)
+        {
+            IResponse resp = Post(url, content);
+            IOUtils.ConsumeAndClose(resp.Stream);
+        }
+
+        // -----
+
+        protected IList<IRepositoryInfo> GetRepositoriesInternal(string repositoryId)
+        {
+            UrlBuilder url = null;
+
+            if (repositoryId == null)
+            {
+                // no repository id provided -> get all
+                url = new UrlBuilder(GetServiceUrl());
+            }
+            else
+            {
+                // use URL of the specified repository
+                url = GetRepositoryUrlCache().GetRepositoryUrl(repositoryId, BindingConstants.SelectorRepositoryInfo);
+                if (url == null)
+                {
+                    // repository infos haven't been fetched yet -> get them all
+                    url = new UrlBuilder(GetServiceUrl());
+                }
+            }
+
+            // read and parse
+            IResponse resp = Read(url);
+            JsonObject json = ParseObject(resp.Stream, resp.Charset);
+
+            IList<IRepositoryInfo> repInfos = new List<IRepositoryInfo>();
+
+            foreach (KeyValuePair<string, object> jri in json)
+            {
+                if (jri.Value is JsonObject)
+                {
+                    IRepositoryInfo ri = JsonConverter.ConvertRepositoryInfo((JsonObject)jri.Value);
+                    string id = ri.Id;
+
+                    if (ri is RepositoryInfoBrowserBinding)
+                    {
+                        string repositoryUrl = ((RepositoryInfoBrowserBinding)ri).RepositoryUrl;
+                        string rootUrl = ((RepositoryInfoBrowserBinding)ri).RootUrl;
+
+                        if (id == null || repositoryUrl == null || rootUrl == null)
+                        {
+                            throw new CmisConnectionException("Found invalid Repository Info! (id: " + id + ")");
+                        }
+
+                        GetRepositoryUrlCache().AddRepository(id, repositoryUrl, rootUrl);
+                    }
+
+                    repInfos.Add(ri);
+                }
+                else
+                {
+                    throw new CmisConnectionException("Found invalid Repository Info!");
+                }
+            }
+
+            return repInfos;
+        }
+
+        /**
+        * Retrieves a type definition.
+         */
+        public ITypeDefinition GetTypeDefinitionInternal(string repositoryId, string typeId)
+        {
+            // build URL
+            UrlBuilder url = GetRepositoryUrl(repositoryId, BindingConstants.SelectorTypeDefinition);
+            url.AddParameter(BindingConstants.ParamTypeId, typeId);
+
+            // read and parse
+            IResponse resp = Read(url);
+            JsonObject json = ParseObject(resp.Stream, resp.Charset);
+
+            return JsonConverter.ConvertTypeDefinition(json);
+        }
+    }
+
+    internal class RepositoryService : AbstractBrowserBindingService, IRepositoryService
+    {
+        public RepositoryService(BindingSession session)
+        {
+            Session = session;
+        }
+
+        public IList<IRepositoryInfo> GetRepositoryInfos(IExtensionsData extension)
+        {
+            return GetRepositoriesInternal(null);
+        }
+
+        public IRepositoryInfo GetRepositoryInfo(string repositoryId, IExtensionsData extension)
+        {
+            IList<IRepositoryInfo> repositoryInfos = GetRepositoriesInternal(repositoryId);
+
+            if (repositoryInfos.Count == 0)
+            {
+                throw new CmisObjectNotFoundException("Repository '" + repositoryId + "'not found!");
+            }
+
+            if (repositoryInfos.Count == 1)
+            {
+                return repositoryInfos[0];
+            }
+
+            // find the repository
+            foreach (IRepositoryInfo info in repositoryInfos)
+            {
+                if (info.Id == null)
+                {
+                    continue;
+                }
+
+                if (info.Id == repositoryId)
+                {
+                    return info;
+                }
+            }
+
+            throw new CmisObjectNotFoundException("Repository '" + repositoryId + "'not found!");
+        }
+
+        public ITypeDefinitionList GetTypeChildren(string repositoryId, string typeId, bool? includePropertyDefinitions,
+            BigInteger? maxItems, BigInteger? skipCount, IExtensionsData extension)
+        {
+            // build URL
+            UrlBuilder url = GetRepositoryUrl(repositoryId, BindingConstants.SelectorTypeChildren);
+            url.AddParameter(BindingConstants.ParamTypeId, typeId);
+            url.AddParameter(BindingConstants.ParamPropertyDefinitions, includePropertyDefinitions);
+            url.AddParameter(BindingConstants.ParamMaxItems, maxItems);
+            url.AddParameter(BindingConstants.ParamSkipCount, skipCount);
+            url.AddParameter(BindingConstants.ParamDateTimeFormat, DateTimeFormatParameter);
+
+            // read and parse
+            IResponse resp = Read(url);
+            JsonObject json = ParseObject(resp.Stream, resp.Charset);
+
+            return JsonConverter.ConvertTypeChildren(json);
+        }
+
+        public IList<ITypeDefinitionContainer> GetTypeDescendants(string repositoryId, string typeId, BigInteger? depth,
+            bool? includePropertyDefinitions, IExtensionsData extension)
+        {
+            // build URL
+            UrlBuilder url = GetRepositoryUrl(repositoryId, BindingConstants.SelectorTypeDecendants);
+            url.AddParameter(BindingConstants.ParamTypeId, typeId);
+            url.AddParameter(BindingConstants.ParamDepth, depth);
+            url.AddParameter(BindingConstants.ParamPropertyDefinitions, includePropertyDefinitions);
+            url.AddParameter(BindingConstants.ParamDateTimeFormat, DateTimeFormatParameter);
+
+            // read and parse
+            IResponse resp = Read(url);
+            JsonArray json = ParseArray(resp.Stream, resp.Charset);
+
+            return JsonConverter.ConvertTypeDescendants(json);
+        }
+
+        public ITypeDefinition GetTypeDefinition(string repositoryId, string typeId, IExtensionsData extension)
+        {
+            return GetTypeDefinitionInternal(repositoryId, typeId);
+        }
+
+
+        public ITypeDefinition CreateType(string repositoryId, ITypeDefinition type, IExtensionsData extension)
+        {
+            // build URL
+            UrlBuilder url = GetRepositoryUrl(repositoryId);
+
+            // prepare form data
+            FormDataComposer composer = new FormDataComposer(BindingConstants.CmisActionCreateType);
+            if (type != null)
+            {
+                StringWriter sw = new StringWriter();
+                JsonConverter.Convert(type, DateTimeFormat).WriteJsonString(sw);
+
+                composer.Parameters[BindingConstants.ControlType] = sw.ToString();
+            }
+
+            // send and parse
+            IResponse resp = Post(url, composer.CreateHttpContent());
+
+            JsonObject json = ParseObject(resp.Stream, resp.Charset);
+
+            return JsonConverter.ConvertTypeDefinition(json);
+        }
+
+        public ITypeDefinition UpdateType(string repositoryId, ITypeDefinition type, IExtensionsData extension)
+        {
+            // build URL
+            UrlBuilder url = GetRepositoryUrl(repositoryId);
+
+            // prepare form data
+            FormDataComposer composer = new FormDataComposer(BindingConstants.CmisActionUpdateType);
+            if (type != null)
+            {
+                StringWriter sw = new StringWriter();
+                JsonConverter.Convert(type, DateTimeFormat).WriteJsonString(sw);
+
+                composer.Parameters[BindingConstants.ControlType] = sw.ToString();
+            }
+
+            // send and parse
+            IResponse resp = Post(url, composer.CreateHttpContent());
+
+            JsonObject json = ParseObject(resp.Stream, resp.Charset);
+
+            return JsonConverter.ConvertTypeDefinition(json);
+        }
+
+        public void DeleteType(string repositoryId, string typeId, IExtensionsData extension)
+        {
+            // build URL
+            UrlBuilder url = GetRepositoryUrl(repositoryId);
+
+            // prepare form data
+            FormDataComposer composer = new FormDataComposer(BindingConstants.CmisActionDeleteType);
+            composer.Parameters[BindingConstants.ControlTypeId] = typeId;
+
+            // send
+            PostAndConsume(url, composer.CreateHttpContent());
+        }
+    }
+
+    internal class NavigationService : AbstractBrowserBindingService, INavigationService
+    {
+        public NavigationService(BindingSession session)
+        {
+            Session = session;
+        }
+
+        public IObjectInFolderList GetChildren(string repositoryId, string folderId, string filter, string orderBy,
+            bool? includeAllowableActions, IncludeRelationships? includeRelationships, string renditionFilter,
+            bool? includePathSegment, BigInteger? maxItems, BigInteger? skipCount, IExtensionsData extension)
+        {
+            // build URL
+            UrlBuilder url = GetObjectUrl(repositoryId, folderId, BindingConstants.SelectorChildren);
+            url.AddParameter(BindingConstants.Paramfilter, filter);
+            url.AddParameter(BindingConstants.ParamOrderBy, orderBy);
+            url.AddParameter(BindingConstants.ParamAllowableActions, includeAllowableActions);
+            url.AddParameter(BindingConstants.ParamRelationships, includeRelationships);
+            url.AddParameter(BindingConstants.ParamRenditionfilter, renditionFilter);
+            url.AddParameter(BindingConstants.ParamPathSegment, includePathSegment);
+            url.AddParameter(BindingConstants.ParamMaxItems, maxItems);
+            url.AddParameter(BindingConstants.ParamSkipCount, skipCount);
+            url.AddParameter(BindingConstants.ParamSuccinct, SuccinctParameter);
+            url.AddParameter(BindingConstants.ParamDateTimeFormat, DateTimeFormatParameter);
+
+            // read and parse
+            IResponse resp = Read(url);
+            JsonObject json = ParseObject(resp.Stream, resp.Charset);
+
+            ITypeCache typeCache = new ClientTypeCache(repositoryId, this);
+
+            return JsonConverter.ConvertObjectInFolderList(json, typeCache);
+        }
+
+        public IList<IObjectInFolderContainer> GetDescendants(string repositoryId, string folderId, BigInteger? depth, string filter,
+            bool? includeAllowableActions, IncludeRelationships? includeRelationships, string renditionFilter,
+            bool? includePathSegment, IExtensionsData extension)
+        {
+            // build URL
+            UrlBuilder url = GetObjectUrl(repositoryId, folderId, BindingConstants.SelectorDecendants);
+            url.AddParameter(BindingConstants.ParamDepth, depth);
+            url.AddParameter(BindingConstants.Paramfilter, filter);
+            url.AddParameter(BindingConstants.ParamAllowableActions, includeAllowableActions);
+            url.AddParameter(BindingConstants.ParamRelationships, includeRelationships);
+            url.AddParameter(BindingConstants.ParamRenditionfilter, renditionFilter);
+            url.AddParameter(BindingConstants.ParamPathSegment, includePathSegment);
+            url.AddParameter(BindingConstants.ParamSuccinct, SuccinctParameter);
+            url.AddParameter(BindingConstants.ParamDateTimeFormat, DateTimeFormatParameter);
+
+            // read and parse
+            IResponse resp = Read(url);
+            JsonArray json = ParseArray(resp.Stream, resp.Charset);
+
+            ITypeCache typeCache = new ClientTypeCache(repositoryId, this);
+
+            return JsonConverter.ConvertDescendants(json, typeCache);
+        }
+
+        public IList<IObjectInFolderContainer> GetFolderTree(string repositoryId, string folderId, BigInteger? depth, string filter,
+            bool? includeAllowableActions, IncludeRelationships? includeRelationships, string renditionFilter,
+            bool? includePathSegment, IExtensionsData extension)
+        {
+            // build URL
+            UrlBuilder url = GetObjectUrl(repositoryId, folderId, BindingConstants.SelectorFolderTree);
+            url.AddParameter(BindingConstants.ParamDepth, depth);
+            url.AddParameter(BindingConstants.Paramfilter, filter);
+            url.AddParameter(BindingConstants.ParamAllowableActions, includeAllowableActions);
+            url.AddParameter(BindingConstants.ParamRelationships, includeRelationships);
+            url.AddParameter(BindingConstants.ParamRenditionfilter, renditionFilter);
+            url.AddParameter(BindingConstants.ParamPathSegment, includePathSegment);
+            url.AddParameter(BindingConstants.ParamSuccinct, SuccinctParameter);
+            url.AddParameter(BindingConstants.ParamDateTimeFormat, DateTimeFormatParameter);
+
+            // read and parse
+            IResponse resp = Read(url);
+            JsonArray json = ParseArray(resp.Stream, resp.Charset);
+
+            ITypeCache typeCache = new ClientTypeCache(repositoryId, this);
+
+            return JsonConverter.ConvertDescendants(json, typeCache);
+        }
+
+        public IList<IObjectParentData> GetObjectParents(string repositoryId, string objectId, string filter,
+            bool? includeAllowableActions, IncludeRelationships? includeRelationships, string renditionFilter,
+            bool? includeRelativePathSegment, IExtensionsData extension)
+        {
+            // build URL
+            UrlBuilder url = GetObjectUrl(repositoryId, objectId, BindingConstants.SelectorParents);
+            url.AddParameter(BindingConstants.Paramfilter, filter);
+            url.AddParameter(BindingConstants.ParamAllowableActions, includeAllowableActions);
+            url.AddParameter(BindingConstants.ParamRelationships, includeRelationships);
+            url.AddParameter(BindingConstants.ParamRenditionfilter, renditionFilter);
+            url.AddParameter(BindingConstants.ParamRelativePathSegment, includeRelativePathSegment);
+            url.AddParameter(BindingConstants.ParamSuccinct, SuccinctParameter);
+            url.AddParameter(BindingConstants.ParamDateTimeFormat, DateTimeFormatParameter);
+
+            // read and parse
+            IResponse resp = Read(url);
+            JsonArray json = ParseArray(resp.Stream, resp.Charset);
+
+            ITypeCache typeCache = new ClientTypeCache(repositoryId, this);
+
+            return JsonConverter.ConvertObjectParents(json, typeCache);
+        }
+
+        public IObjectData GetFolderParent(string repositoryId, string folderId, string filter, ExtensionsData extension)
+        {
+            // build URL
+            UrlBuilder url = GetObjectUrl(repositoryId, folderId, BindingConstants.SelectorParent);
+            url.AddParameter(BindingConstants.Paramfilter, filter);
+            url.AddParameter(BindingConstants.ParamSuccinct, SuccinctParameter);
+            url.AddParameter(BindingConstants.ParamDateTimeFormat, DateTimeFormatParameter);
+
+            // read and parse
+            IResponse resp = Read(url);
+            JsonObject json = ParseObject(resp.Stream, resp.Charset);
+
+            ITypeCache typeCache = new ClientTypeCache(repositoryId, this);
+
+            return JsonConverter.ConvertObject(json, typeCache);
+        }
+
+        public IObjectList GetCheckedOutDocs(string repositoryId, string folderId, string filter, string orderBy,
+            bool? includeAllowableActions, IncludeRelationships? includeRelationships, string renditionFilter,
+            BigInteger? maxItems, BigInteger? skipCount, IExtensionsData extension)
+        {
+            // build URL
+            UrlBuilder url = (folderId != null ? GetObjectUrl(repositoryId, folderId, BindingConstants.SelectorCheckedout)
+                    : GetRepositoryUrl(repositoryId, BindingConstants.SelectorCheckedout));
+            url.AddParameter(BindingConstants.Paramfilter, filter);
+            url.AddParameter(BindingConstants.ParamOrderBy, orderBy);
+            url.AddParameter(BindingConstants.ParamAllowableActions, includeAllowableActions);
+            url.AddParameter(BindingConstants.ParamRelationships, includeRelationships);
+            url.AddParameter(BindingConstants.ParamRenditionfilter, renditionFilter);
+            url.AddParameter(BindingConstants.ParamMaxItems, maxItems);
+            url.AddParameter(BindingConstants.ParamSkipCount, skipCount);
+            url.AddParameter(BindingConstants.ParamSuccinct, SuccinctParameter);
+            url.AddParameter(BindingConstants.ParamDateTimeFormat, DateTimeFormatParameter);
+
+            // read and parse
+            IResponse resp = Read(url);
+            JsonObject json = ParseObject(resp.Stream, resp.Charset);
+
+            ITypeCache typeCache = new ClientTypeCache(repositoryId, this);
+
+            return JsonConverter.ConvertObjectList(json, typeCache, false);
+        }
+    }
+
+    internal class ObjectService : AbstractBrowserBindingService, IObjectService
+    {
+        public ObjectService(BindingSession session)
+        {
+            Session = session;
+        }
+
+        public string CreateDocument(string repositoryId, IProperties properties, string folderId, IContentStream contentStream,
+            VersioningState? versioningState, IList<string> policies, IAcl addAces, IAcl removeAces, IExtensionsData extension)
+        {
+            // build URL
+            UrlBuilder url = (folderId != null ? GetObjectUrl(repositoryId, folderId) : GetRepositoryUrl(repositoryId));
+
+            // prepare form data
+            FormDataComposer composer = new FormDataComposer(BindingConstants.CmisActionCreateDocument);
+            composer.Properties = properties;
+            composer.Succinct = Succinct;
+            composer.DateTimeFormat = DateTimeFormat;
+            composer.Parameters[BindingConstants.ParamVersioningState] = versioningState;
+            composer.Policies = policies;
+            composer.AddAces = addAces;
+            composer.RemoveAces = removeAces;
+            composer.Stream = contentStream;
+
+            // send and parse
+            IResponse resp = Post(url, composer.CreateHttpContent());
+
+            JsonObject json = ParseObject(resp.Stream, resp.Charset);
+            ITypeCache typeCache = new ClientTypeCache(repositoryId, this);
+
+            IObjectData newObj = JsonConverter.ConvertObject(json, typeCache);
+
+            return (newObj == null ? null : newObj.Id);
+        }
+
+        public string CreateDocumentFromSource(string repositoryId, string sourceId, IProperties properties, string folderId,
+            VersioningState? versioningState, IList<string> policies, IAcl addAces, IAcl removeAces, IExtensionsData extension)
+        {
+            // build URL
+            UrlBuilder url = (folderId != null ? GetObjectUrl(repositoryId, folderId) : GetRepositoryUrl(repositoryId));
+
+            // prepare form data
+            FormDataComposer composer = new FormDataComposer(BindingConstants.CmisActionCreateDocumentFromSource);
+            composer.Parameters[BindingConstants.ParamSourceId] = sourceId;
+            composer.Properties = properties;
+            composer.Succinct = Succinct;
+            composer.DateTimeFormat = DateTimeFormat;
+            composer.Parameters[BindingConstants.ParamVersioningState] = versioningState;
+            composer.Policies = policies;
+            composer.AddAces = addAces;
+            composer.RemoveAces = removeAces;
+
+            // send and parse
+            IResponse resp = Post(url, composer.CreateHttpContent());
+
+            JsonObject json = ParseObject(resp.Stream, resp.Charset);
+
+            ITypeCache typeCache = new ClientTypeCache(repositoryId, this);
+
+            IObjectData newObj = JsonConverter.ConvertObject(json, typeCache);
+
+            return (newObj == null ? null : newObj.Id);
+        }
+
+        public string CreateFolder(string repositoryId, IProperties properties, string folderId, IList<string> policies,
+            IAcl addAces, IAcl removeAces, IExtensionsData extension)
+        {
+            // build URL
+            UrlBuilder url = GetObjectUrl(repositoryId, folderId);
+
+            // prepare form data
+            FormDataComposer composer = new FormDataComposer(BindingConstants.CmisActionCreateFolder);
+            composer.Properties = properties;
+            composer.Succinct = Succinct;
+            composer.DateTimeFormat = DateTimeFormat;
+            composer.Policies = policies;
+            composer.AddAces = addAces;
+            composer.RemoveAces = removeAces;
+
+            // send and parse
+            IResponse resp = Post(url, composer.CreateHttpContent());
+
+            JsonObject json = ParseObject(resp.Stream, resp.Charset);
+
+            ITypeCache typeCache = new ClientTypeCache(repositoryId, this);
+
+            IObjectData newObj = JsonConverter.ConvertObject(json, typeCache);
+
+            return (newObj == null ? null : newObj.Id);
+        }
+
+        public string CreateRelationship(string repositoryId, IProperties properties, IList<string> policies, IAcl addAces,
+            IAcl removeAces, IExtensionsData extension)
+        {
+            // build URL
+            UrlBuilder url = GetRepositoryUrl(repositoryId);
+
+            // prepare form data
+            FormDataComposer composer = new FormDataComposer(BindingConstants.CmisActionCreateRelationship);
+            composer.Properties = properties;
+            composer.Succinct = Succinct;
+            composer.DateTimeFormat = DateTimeFormat;
+            composer.Policies = policies;
+            composer.AddAces = addAces;
+            composer.RemoveAces = removeAces;
+
+            // send and parse
+            IResponse resp = Post(url, composer.CreateHttpContent());
+
+            JsonObject json = ParseObject(resp.Stream, resp.Charset);
+
+            ITypeCache typeCache = new ClientTypeCache(repositoryId, this);
+
+            IObjectData newObj = JsonConverter.ConvertObject(json, typeCache);
+
+            return (newObj == null ? null : newObj.Id);
+        }
+
+        public string CreatePolicy(string repositoryId, IProperties properties, string folderId, IList<string> policies,
+            IAcl addAces, IAcl removeAces, IExtensionsData extension)
+        {
+            // build URL
+            UrlBuilder url = (folderId != null ? GetObjectUrl(repositoryId, folderId) : GetRepositoryUrl(repositoryId));
+
+            // prepare form data
+            FormDataComposer composer = new FormDataComposer(BindingConstants.CmisActionCreatePolicy);
+            composer.Properties = properties;
+            composer.Succinct = Succinct;
+            composer.DateTimeFormat = DateTimeFormat;
+            composer.Policies = policies;
+            composer.AddAces = addAces;
+            composer.RemoveAces = removeAces;
+
+            // send and parse
+            IResponse resp = Post(url, composer.CreateHttpContent());
+
+            JsonObject json = ParseObject(resp.Stream, resp.Charset);
+
+            ITypeCache typeCache = new ClientTypeCache(repositoryId, this);
+
+            IObjectData newObj = JsonConverter.ConvertObject(json, typeCache);
+
+            return (newObj == null ? null : newObj.Id);
+        }
+
+        public string CreateItem(string repositoryId, IProperties properties, string folderId, IList<string> policies,
+            IAcl addAces, IAcl removeAces, IExtensionsData extension)
+        {
+            // build URL
+            UrlBuilder url = (folderId != null ? GetObjectUrl(repositoryId, folderId) : GetRepositoryUrl(repositoryId));
+
+            // prepare form data
+            FormDataComposer composer = new FormDataComposer(BindingConstants.CmisActionCreateItem);
+            composer.Properties = properties;
+            composer.Succinct = Succinct;
+            composer.DateTimeFormat = DateTimeFormat;
+            composer.Policies = policies;
+            composer.AddAces = addAces;
+            composer.RemoveAces = removeAces;
+
+            // send and parse
+            IResponse resp = Post(url, composer.CreateHttpContent());
+
+            JsonObject json = ParseObject(resp.Stream, resp.Charset);
+
+            ITypeCache typeCache = new ClientTypeCache(repositoryId, this);
+
+            IObjectData newObj = JsonConverter.ConvertObject(json, typeCache);
+
+            return (newObj == null ? null : newObj.Id);
+        }
+
+        public IAllowableActions GetAllowableActions(string repositoryId, string objectId, IExtensionsData extension)
+        {
+            // build URL
+            UrlBuilder url = GetObjectUrl(repositoryId, objectId, BindingConstants.SelectorAllowableActionS);
+
+            // read and parse
+            IResponse resp = Read(url);
+            JsonObject json = ParseObject(resp.Stream, resp.Charset);
+
+            return JsonConverter.ConvertAllowableActions(json);
+        }
+
+        public IProperties GetProperties(string repositoryId, string objectId, string filter, IExtensionsData extension)
+        {
+            // build URL
+            UrlBuilder url = GetObjectUrl(repositoryId, objectId, BindingConstants.SelectorProperties);
+            url.AddParameter(BindingConstants.Paramfilter, filter);
+            url.AddParameter(BindingConstants.ParamSuccinct, SuccinctParameter);
+            url.AddParameter(BindingConstants.ParamDateTimeFormat, DateTimeFormatParameter);
+
+            // read and parse
+            IResponse resp = Read(url);
+            JsonObject json = ParseObject(resp.Stream, resp.Charset);
+
+            if (Succinct)
+            {
+                ITypeCache typeCache = new ClientTypeCache(repositoryId, this);
+                return JsonConverter.ConvertSuccinctProperties(json, null, typeCache);
+            }
+            else
+            {
+                return JsonConverter.ConvertProperties(json, null);
+            }
+        }
+
+        public IList<IRenditionData> GetRenditions(string repositoryId, string objectId, string renditionFilter,
+            BigInteger? maxItems, BigInteger? skipCount, IExtensionsData extension)
+        {
+            // build URL
+            UrlBuilder url = GetObjectUrl(repositoryId, objectId, BindingConstants.SelectorRenditions);
+            url.AddParameter(BindingConstants.ParamRenditionfilter, renditionFilter);
+            url.AddParameter(BindingConstants.ParamMaxItems, maxItems);
+            url.AddParameter(BindingConstants.ParamSkipCount, skipCount);
+
+            // read and parse
+            IResponse resp = Read(url);
+            JsonArray json = ParseArray(resp.Stream, resp.Charset);
+
+            return JsonConverter.ConvertRenditions(json);
+        }
+
+        public IObjectData GetObject(string repositoryId, string objectId, string filter, bool? includeAllowableActions,
+            IncludeRelationships? includeRelationships, string renditionFilter, bool? includePolicyIds,
+            bool? includeAcl, IExtensionsData extension)
+        {
+            // build URL
+            UrlBuilder url = GetObjectUrl(repositoryId, objectId, BindingConstants.SelectorObject);
+            url.AddParameter(BindingConstants.Paramfilter, filter);
+            url.AddParameter(BindingConstants.ParamAllowableActions, includeAllowableActions);
+            url.AddParameter(BindingConstants.ParamRelationships, includeRelationships);
+            url.AddParameter(BindingConstants.ParamRenditionfilter, renditionFilter);
+            url.AddParameter(BindingConstants.ParamPolicyIds, includePolicyIds);
+            url.AddParameter(BindingConstants.ParamAcl, includeAcl);
+            url.AddParameter(BindingConstants.ParamSuccinct, SuccinctParameter);
+            url.AddParameter(BindingConstants.ParamDateTimeFormat, DateTimeFormatParameter);
+
+            // read and parse
+            IResponse resp = Read(url);
+            JsonObject json = ParseObject(resp.Stream, resp.Charset);
+
+            ITypeCache typeCache = new ClientTypeCache(repositoryId, this);
+
+            return JsonConverter.ConvertObject(json, typeCache);
+        }
+
+        public IObjectData GetObjectByPath(string repositoryId, string path, string filter, bool? includeAllowableActions,
+            IncludeRelationships? includeRelationships, string renditionFilter, bool? includePolicyIds, bool? includeAcl,
+            IExtensionsData extension)
+        {
+            // build URL
+            UrlBuilder url = GetPathUrl(repositoryId, path, BindingConstants.SelectorObject);
+            url.AddParameter(BindingConstants.Paramfilter, filter);
+            url.AddParameter(BindingConstants.ParamAllowableActions, includeAllowableActions);
+            url.AddParameter(BindingConstants.ParamRelationships, includeRelationships);
+            url.AddParameter(BindingConstants.ParamRenditionfilter, renditionFilter);
+            url.AddParameter(BindingConstants.ParamPolicyIds, includePolicyIds);
+            url.AddParameter(BindingConstants.ParamAcl, includeAcl);
+            url.AddParameter(BindingConstants.ParamSuccinct, SuccinctParameter);
+            url.AddParameter(BindingConstants.ParamDateTimeFormat, DateTimeFormatParameter);
+
+            // read and parse
+            IResponse resp = Read(url);
+            JsonObject json = ParseObject(resp.Stream, resp.Charset);
+
+            ITypeCache typeCache = new ClientTypeCache(repositoryId, this);
+
+            return JsonConverter.ConvertObject(json, typeCache);
+        }
+
+        public IContentStream GetContentStream(string repositoryId, string objectId, string streamId, BigInteger? offset, BigInteger? length,
+            IExtensionsData extension)
+        {
+            // build URL
+            UrlBuilder url = GetObjectUrl(repositoryId, objectId, BindingConstants.SelectorContent);
+            url.AddParameter(BindingConstants.ParamStreamId, streamId);
+
+            // get the content
+            IResponse resp = Session.GetHttpInvoker().InvokeGET(url, Session, (long?)offset, (long?)length);
+
+            // check response code
+            if ((resp.StatusCode != 200) && (resp.StatusCode != 206))
+            {
+                throw ConvertStatusCode(resp.StatusCode, resp.Message, resp.ErrorContent, null);
+            }
+
+            // build result object
+            ContentStream result;
+            if (resp.StatusCode == 206)
+            {
+                result = new PartialContentStream();
+            }
+            else
+            {
+                result = new ContentStream();
+            }
+
+            result.FileName = resp.Filename;
+            result.Length = resp.ContentLength;
+            result.MimeType = resp.ContentType;
+            result.Stream = resp.Stream;
+
+            return result;
+        }
+
+        public void UpdateProperties(string repositoryId, ref string objectId, ref string changeToken, IProperties properties,
+            IExtensionsData extension)
+        {
+            // we need an object ID
+            if (objectId == null || objectId.Length == 0)
+            {
+                throw new CmisInvalidArgumentException("Object ID must be set!");
+            }
+
+            // build URL
+            UrlBuilder url = GetObjectUrl(repositoryId, objectId);
+
+            // prepare form data
+            FormDataComposer composer = new FormDataComposer(BindingConstants.CmisActionUpdateProperties);
+            composer.Properties = properties;
+            composer.Succinct = Succinct;
+            composer.DateTimeFormat = DateTimeFormat;
+            composer.Parameters[BindingConstants.ParamChangeToken] =
+                (changeToken == null || Session.GetValue(SessionParameter.OmitChangeTokens, false) ? null : changeToken);
+
+            // send and parse
+            IResponse resp = Post(url, composer.CreateHttpContent());
+
+            JsonObject json = ParseObject(resp.Stream, resp.Charset);
+
+            ITypeCache typeCache = new ClientTypeCache(repositoryId, this);
+
+            IObjectData newObj = JsonConverter.ConvertObject(json, typeCache);
+
+            objectId = (newObj == null ? null : newObj.Id);
+
+            SetChangeToken(ref changeToken, newObj);
+        }
+
+        public IList<IBulkUpdateObjectIdAndChangeToken> BulkUpdateProperties(string repositoryId,
+                IList<IBulkUpdateObjectIdAndChangeToken> objectIdAndChangeToken, IProperties properties,
+                IList<string> addSecondaryTypeIds, IList<string> removeSecondaryTypeIds, IExtensionsData extension)
+        {
+            // we need object ids
+            if (objectIdAndChangeToken == null || objectIdAndChangeToken.Count == 0)
+            {
+                throw new CmisInvalidArgumentException("Object IDs must be set!");
+            }
+
+            // build URL
+            UrlBuilder url = GetRepositoryUrl(repositoryId);
+
+            // prepare form data
+            FormDataComposer composer = new FormDataComposer(BindingConstants.CmisActionBulkUpdate);
+            composer.ObjectIdsAndChangeTokens = objectIdAndChangeToken;
+            composer.Properties = properties;
+            composer.Succinct = Succinct;
+            composer.DateTimeFormat = DateTimeFormat;
+            composer.AddSecondaryTypeIds = addSecondaryTypeIds;
+            composer.RemoveSecondaryTypeIds = removeSecondaryTypeIds;
+
+            // send and parse
+            IResponse resp = Post(url, composer.CreateHttpContent());
+
+            JsonArray json = ParseArray(resp.Stream, resp.Charset);
+
+            return JsonConverter.ConvertBulkUpdate(json);
+        }
+
+        public void MoveObject(string repositoryId, ref string objectId, string targetFolderId, string sourceFolderId,
+            IExtensionsData extension)
+        {
+            // we need an object id
+            if (objectId == null || objectId.Length == 0)
+            {
+                throw new CmisInvalidArgumentException("Object ID must be set!");
+            }
+
+            // build URL
+            UrlBuilder url = GetObjectUrl(repositoryId, objectId);
+
+            // prepare form data
+            FormDataComposer composer = new FormDataComposer(BindingConstants.CmisActionMove);
+            composer.Parameters[BindingConstants.ParamTargetFolderId] = targetFolderId;
+            composer.Parameters[BindingConstants.ParamSourceFolderId] = sourceFolderId;
+            composer.Succinct = Succinct;
+            composer.DateTimeFormat = DateTimeFormat;
+
+            // send and parse
+            IResponse resp = Post(url, composer.CreateHttpContent());
+
+            JsonObject json = ParseObject(resp.Stream, resp.Charset);
+
+            ITypeCache typeCache = new ClientTypeCache(repositoryId, this);
+
+            IObjectData newObj = JsonConverter.ConvertObject(json, typeCache);
+
+            objectId = (newObj == null ? null : newObj.Id);
+        }
+
+        public void DeleteObject(string repositoryId, string objectId, bool? allVersions, IExtensionsData extension)
+        {
+            // build URL
+            UrlBuilder url = GetObjectUrl(repositoryId, objectId);
+
+            // prepare form data
+            FormDataComposer composer = new FormDataComposer(BindingConstants.CmisActionDelete);
+            composer.Parameters[BindingConstants.ParamAllVersions] = allVersions;
+
+            // send
+            PostAndConsume(url, composer.CreateHttpContent());
+        }
+
+        public IFailedToDeleteData DeleteTree(string repositoryId, string folderId, bool? allVersions, UnfileObject? unfileObjects,
+            bool? continueOnFailure, ExtensionsData extension)
+        {
+            // build URL
+            UrlBuilder url = GetObjectUrl(repositoryId, folderId);
+
+            // prepare form data
+            FormDataComposer composer = new FormDataComposer(BindingConstants.CmisActionDeleteTree);
+            composer.Parameters[BindingConstants.ParamAllVersions] = allVersions;
+            composer.Parameters[BindingConstants.ParamUnfileObjects] = unfileObjects;
+            composer.Parameters[BindingConstants.ParamContinueOnFailure] = continueOnFailure;
+
+            // send
+            IResponse resp = Post(url, composer.CreateHttpContent());
+
+            if (resp.StatusCode == 200 && resp.ContentLength > 0 && (!resp.Stream.CanSeek || resp.Stream.Length > 0))
+            {
+                try
+                {
+                    JsonObject json = ParseObject(resp.Stream, resp.Charset);
+                    return JsonConverter.ConvertFailedToDelete(json);
+                }
+                catch (IOException e)
+                {
+                    throw new CmisConnectionException("Cannot read response!", e);
+                }
+            }
+
+            return new FailedToDeleteData();
+        }
+
+        public void SetContentStream(string repositoryId, ref string objectId, bool? overwriteFlag, ref string changeToken,
+            IContentStream contentStream, IExtensionsData extension)
+        {
+            // we need an object id
+            if (objectId == null || objectId.Length == 0)
+            {
+                throw new CmisInvalidArgumentException("Object ID must be set!");
+            }
+
+            // build URL
+            UrlBuilder url = GetObjectUrl(repositoryId, objectId);
+
+            // prepare form data
+            FormDataComposer composer = new FormDataComposer(BindingConstants.CmisActionSetContent);
+            composer.Stream = contentStream;
+            composer.Parameters[BindingConstants.ParamOverwriteFlag] = overwriteFlag;
+            composer.Succinct = Succinct;
+            composer.Parameters[BindingConstants.ParamChangeToken] =
+                (changeToken == null || Session.GetValue(SessionParameter.OmitChangeTokens, false) ? null : changeToken);
+
+
+            // send and parse
+            IResponse resp = Post(url, composer.CreateHttpContent());
+
+            JsonObject json = ParseObject(resp.Stream, resp.Charset);
+
+            ITypeCache typeCache = new ClientTypeCache(repositoryId, this);
+
+            IObjectData newObj = JsonConverter.ConvertObject(json, typeCache);
+
+            objectId = (newObj == null ? null : newObj.Id);
+
+            SetChangeToken(ref changeToken, newObj);
+        }
+
+        public void AppendContentStream(string repositoryId, ref string objectId, bool? isLastChunk, ref string changeToken,
+            IContentStream contentStream, IExtensionsData extension)
+        {
+            // we need an object id
+            if (objectId == null || objectId.Length == 0)
+            {
+                throw new CmisInvalidArgumentException("Object ID must be set!");
+            }
+
+            // build URL
+            UrlBuilder url = GetObjectUrl(repositoryId, objectId);
+
+            // prepare form data
+            FormDataComposer composer = new FormDataComposer(BindingConstants.CmisActionAppendContent);
+            composer.Stream = contentStream;
+            composer.Parameters[BindingConstants.ControlIsLastChunk] = isLastChunk;
+            composer.Succinct = Succinct;
+            composer.Parameters[BindingConstants.ParamChangeToken] =
+                (changeToken == null || Session.GetValue(SessionParameter.OmitChangeTokens, false) ? null : changeToken);
+
+            // send and parse
+            IResponse resp = Post(url, composer.CreateHttpContent());
+
+            JsonObject json = ParseObject(resp.Stream, resp.Charset);
+
+            ITypeCache typeCache = new ClientTypeCache(repositoryId, this);
+
+            IObjectData newObj = JsonConverter.ConvertObject(json, typeCache);
+
+            objectId = (newObj == null ? null : newObj.Id);
+
+            SetChangeToken(ref changeToken, newObj);
+        }
+
+        public void DeleteContentStream(string repositoryId, ref string objectId, ref string changeToken, IExtensionsData extension)
+        {
+            // we need an object id
+            if (objectId == null || objectId.Length == 0)
+            {
+                throw new CmisInvalidArgumentException("Object ID must be set!");
+            }
+
+            // build URL
+            UrlBuilder url = GetObjectUrl(repositoryId, objectId);
+
+            // prepare form data
+            FormDataComposer composer = new FormDataComposer(BindingConstants.CmisActionDeleteContent);
+            composer.Succinct = Succinct;
+            composer.Parameters[BindingConstants.ParamChangeToken] =
+                (changeToken == null || Session.GetValue(SessionParameter.OmitChangeTokens, false) ? null : changeToken);
+
+            // send and parse
+            IResponse resp = Post(url, composer.CreateHttpContent());
+
+            JsonObject json = ParseObject(resp.Stream, resp.Charset);
+
+            ITypeCache typeCache = new ClientTypeCache(repositoryId, this);
+
+            IObjectData newObj = JsonConverter.ConvertObject(json, typeCache);
+
+            objectId = (newObj == null ? null : newObj.Id);
+
+            SetChangeToken(ref changeToken, newObj);
+        }
+    }
+
+    internal class VersioningService : AbstractBrowserBindingService, IVersioningService
+    {
+        public VersioningService(BindingSession session)
+        {
+            Session = session;
+        }
+
+        public void CheckOut(string repositoryId, ref string objectId, IExtensionsData extension, out bool? contentCopied)
+        {
+            // we need an object id
+            if (objectId == null || objectId.Length == 0)
+            {
+                throw new CmisInvalidArgumentException("Object ID must be set!");
+            }
+
+            // build URL
+            UrlBuilder url = GetObjectUrl(repositoryId, objectId);
+
+            // prepare form data
+            FormDataComposer composer = new FormDataComposer(BindingConstants.CmisActionCheckOut);
+            composer.Succinct = Succinct;
+
+            // send and parse
+            IResponse resp = Post(url, composer.CreateHttpContent());
+
+            JsonObject json = ParseObject(resp.Stream, resp.Charset);
+
+            ITypeCache typeCache = new ClientTypeCache(repositoryId, this);
+
+            IObjectData newObj = JsonConverter.ConvertObject(json, typeCache);
+
+            objectId = (newObj == null ? null : newObj.Id);
+
+            contentCopied = null;
+        }
+
+        public void CancelCheckOut(string repositoryId, string objectId, IExtensionsData extension)
+        {
+            // build URL
+            UrlBuilder url = GetObjectUrl(repositoryId, objectId);
+
+            // prepare form data
+            FormDataComposer composer = new FormDataComposer(BindingConstants.CmisActionCancelCheckOut);
+
+            // send
+            PostAndConsume(url, composer.CreateHttpContent());
+        }
+
+        public void CheckIn(string repositoryId, ref string objectId, bool? major, IProperties properties,
+            IContentStream contentStream, string checkinComment, IList<string> policies, IAcl addAces, IAcl removeAces,
+            IExtensionsData extension)
+        {
+            // we need an object id
+            if (objectId == null || objectId.Length == 0)
+            {
+                throw new CmisInvalidArgumentException("Object ID must be set!");
+            }
+
+            // build URL
+            UrlBuilder url = GetObjectUrl(repositoryId, objectId);
+
+            // prepare form data
+            FormDataComposer composer = new FormDataComposer(BindingConstants.CmisActionCheckIn);
+            composer.Stream = contentStream;
+            composer.Parameters[BindingConstants.ParamMajor] = major;
+            composer.Properties = properties;
+            composer.Succinct = Succinct;
+            composer.DateTimeFormat = DateTimeFormat;
+            composer.Parameters[BindingConstants.ParamCheckinComment] = checkinComment;
+            composer.Policies = policies;
+            composer.AddAces = addAces;
+            composer.RemoveAces = removeAces;
+
+            // send and parse
+            IResponse resp = Post(url, composer.CreateHttpContent());
+
+            JsonObject json = ParseObject(resp.Stream, resp.Charset);
+
+            ITypeCache typeCache = new ClientTypeCache(repositoryId, this);
+
+            IObjectData newObj = JsonConverter.ConvertObject(json, typeCache);
+
+            objectId = (newObj == null ? null : newObj.Id);
+        }
+
+        public IObjectData GetObjectOfLatestVersion(string repositoryId, string objectId, string versionSeriesId, bool major,
+            string filter, bool? includeAllowableActions, IncludeRelationships? includeRelationships,
+            string renditionFilter, bool? includePolicyIds, bool? includeAcl, IExtensionsData extension)
+        {
+            // build URL
+            UrlBuilder url = GetObjectUrl(repositoryId, objectId, BindingConstants.SelectorObject);
+            url.AddParameter(BindingConstants.Paramfilter, filter);
+            url.AddParameter(BindingConstants.ParamAllowableActions, includeAllowableActions);
+            url.AddParameter(BindingConstants.ParamRelationships, includeRelationships);
+            url.AddParameter(BindingConstants.ParamRenditionfilter, renditionFilter);
+            url.AddParameter(BindingConstants.ParamPolicyIds, includePolicyIds);
+            url.AddParameter(BindingConstants.ParamAcl, includeAcl);
+            url.AddParameter(BindingConstants.ParamReturnVersion, (!major ? ReturnVersion.Latest : ReturnVersion.LatestMajor));
+            url.AddParameter(BindingConstants.ParamSuccinct, SuccinctParameter);
+            url.AddParameter(BindingConstants.ParamDateTimeFormat, DateTimeFormatParameter);
+
+            // read and parse
+            IResponse resp = Read(url);
+
+            JsonObject json = ParseObject(resp.Stream, resp.Charset);
+
+            ITypeCache typeCache = new ClientTypeCache(repositoryId, this);
+
+            return JsonConverter.ConvertObject(json, typeCache);
+        }
+
+        public IProperties GetPropertiesOfLatestVersion(string repositoryId, string objectId, string versionSeriesId, bool major,
+            string filter, IExtensionsData extension)
+        {
+            // build URL
+            UrlBuilder url = GetObjectUrl(repositoryId, objectId, BindingConstants.SelectorProperties);
+            url.AddParameter(BindingConstants.Paramfilter, filter);
+            url.AddParameter(BindingConstants.ParamReturnVersion, (!major ? ReturnVersion.Latest : ReturnVersion.LatestMajor));
+            url.AddParameter(BindingConstants.ParamSuccinct, SuccinctParameter);
+            url.AddParameter(BindingConstants.ParamDateTimeFormat, DateTimeFormatParameter);
+
+            // read and parse
+            IResponse resp = Read(url);
+            JsonObject json = ParseObject(resp.Stream, resp.Charset);
+
+            if (Succinct)
+            {
+                ITypeCache typeCache = new ClientTypeCache(repositoryId, this);
+                return JsonConverter.ConvertSuccinctProperties(json, null, typeCache);
+            }
+            else
+            {
+                return JsonConverter.ConvertProperties(json, null);
+            }
+        }
+
+        public IList<IObjectData> GetAllVersions(string repositoryId, string objectId, string versionSeriesId, string filter,
+            bool? includeAllowableActions, IExtensionsData extension)
+        {
+            // build URL
+            UrlBuilder url = GetObjectUrl(repositoryId, objectId, BindingConstants.SelectorVersions);
+            url.AddParameter(BindingConstants.Paramfilter, filter);
+            url.AddParameter(BindingConstants.ParamAllowableActions, includeAllowableActions);
+            url.AddParameter(BindingConstants.ParamSuccinct, SuccinctParameter);
+            url.AddParameter(BindingConstants.ParamDateTimeFormat, DateTimeFormatParameter);
+
+            // read and parse
+            IResponse resp = Read(url);
+
+            JsonArray json = ParseArray(resp.Stream, resp.Charset);
+
+            ITypeCache typeCache = new ClientTypeCache(repositoryId, this);
+
+            return JsonConverter.ConvertObjects(json, typeCache);
+        }
+    }
+
+    internal class DiscoveryService : AbstractBrowserBindingService, IDiscoveryService
+    {
+        public DiscoveryService(BindingSession session)
+        {
+            Session = session;
+        }
+
+        public IObjectList Query(string repositoryId, string statement, bool? searchAllVersions,
+            bool? includeAllowableActions, IncludeRelationships? includeRelationships, string renditionFilter,
+            BigInteger? maxItems, BigInteger? skipCount, IExtensionsData extension)
+        {
+            // build URL
+            UrlBuilder url = GetRepositoryUrl(repositoryId);
+
+            // prepare form data
+            FormDataComposer composer = new FormDataComposer(BindingConstants.CmisActionQuery);
+            composer.Parameters[BindingConstants.ParamStatement] = statement;
+            composer.Parameters[BindingConstants.ParamSearchAllVersions] = searchAllVersions;
+            composer.Parameters[BindingConstants.ParamAllowableActions] = includeAllowableActions;
+            composer.Parameters[BindingConstants.ParamRelationships] = includeRelationships;
+            composer.Parameters[BindingConstants.ParamRenditionfilter] = renditionFilter;
+            composer.Parameters[BindingConstants.ParamMaxItems] = maxItems;
+            composer.Parameters[BindingConstants.ParamSkipCount] = skipCount;
+            composer.DateTimeFormat = DateTimeFormat;
+            // Important: No succinct flag here!!!
+
+            // send and parse
+            IResponse resp = Post(url, composer.CreateHttpContent());
+
+            ITypeCache typeCache = new ClientTypeCache(repositoryId, this);
+
+            JsonObject json = ParseObject(resp.Stream, resp.Charset);
+            return JsonConverter.ConvertObjectList(json, typeCache, true);
+        }
+
+        public IObjectList GetContentChanges(string repositoryId, ref string changeLogToken, bool? includeProperties,
+           string filter, bool? includePolicyIds, bool? includeAcl, BigInteger? maxItems, IExtensionsData extension)
+        {
+            // build URL
+            UrlBuilder url = GetRepositoryUrl(repositoryId, BindingConstants.SelectorContentChanges);
+            url.AddParameter(BindingConstants.ParamChangeLogToken, changeLogToken);
+            url.AddParameter(BindingConstants.ParamProperties, includeProperties);
+            url.AddParameter(BindingConstants.Paramfilter, filter);
+            url.AddParameter(BindingConstants.ParamPolicyIds, includePolicyIds);
+            url.AddParameter(BindingConstants.ParamAcl, includeAcl);
+            url.AddParameter(BindingConstants.ParamMaxItems, maxItems);
+            url.AddParameter(BindingConstants.ParamSuccinct, SuccinctParameter);
+            url.AddParameter(BindingConstants.ParamDateTimeFormat, DateTimeFormatParameter);
+
+            // read and parse
+            IResponse resp = Read(url);
+            JsonObject json = ParseObject(resp.Stream, resp.Charset);
+
+            if (changeLogToken != null && json != null)
+            {
+                object token = json[BrowserConstants.JsonObjectListChangeLogToken];
+                if (token is string)
+                {
+                    changeLogToken = (string)token;
+                }
+            }
+
+            ITypeCache typeCache = new ClientTypeCache(repositoryId, this);
+
+            return JsonConverter.ConvertObjectList(json, typeCache, false);
+        }
+    }
+
+    internal class RelationshipService : AbstractBrowserBindingService, IRelationshipService
+    {
+        public RelationshipService(BindingSession session)
+        {
+            Session = session;
+        }
+
+        public IObjectList GetObjectRelationships(string repositoryId, string objectId, bool? includeSubRelationshipTypes,
+            RelationshipDirection? relationshipDirection, string typeId, string filter, bool? includeAllowableActions,
+            BigInteger? maxItems, BigInteger? skipCount, IExtensionsData extension)
+        {
+            // build URL
+            UrlBuilder url = GetObjectUrl(repositoryId, objectId, BindingConstants.SelectorRelationships);
+            url.AddParameter(BindingConstants.ParamSubRelationshipTypes, includeSubRelationshipTypes);
+            url.AddParameter(BindingConstants.ParamRelationshipDirection, relationshipDirection);
+            url.AddParameter(BindingConstants.ParamTypeId, typeId);
+            url.AddParameter(BindingConstants.Paramfilter, filter);
+            url.AddParameter(BindingConstants.ParamAllowableActions, includeAllowableActions);
+            url.AddParameter(BindingConstants.ParamMaxItems, maxItems);
+            url.AddParameter(BindingConstants.ParamSkipCount, skipCount);
+            url.AddParameter(BindingConstants.ParamSuccinct, SuccinctParameter);
+            url.AddParameter(BindingConstants.ParamDateTimeFormat, DateTimeFormatParameter);
+
+            // read and parse
+            IResponse resp = Read(url);
+            JsonObject json = ParseObject(resp.Stream, resp.Charset);
+
+            ITypeCache typeCache = new ClientTypeCache(repositoryId, this);
+
+            return JsonConverter.ConvertObjectList(json, typeCache, false);
+        }
+    }
+
+    internal class MultiFilingService : AbstractBrowserBindingService, IMultiFilingService
+    {
+        public MultiFilingService(BindingSession session)
+        {
+            Session = session;
+        }
+
+        public void AddObjectToFolder(string repositoryId, string objectId, string folderId, bool? allVersions, IExtensionsData extension)
+        {
+            // build URL
+            UrlBuilder url = GetObjectUrl(repositoryId, objectId);
+
+            // prepare form data
+            FormDataComposer composer = new FormDataComposer(BindingConstants.CmisActionRemoveObjectToFolder);
+            composer.Parameters[BindingConstants.ParamFolderId] = folderId;
+            composer.Parameters[BindingConstants.ParamAllVersions] = allVersions;
+
+            // send
+            PostAndConsume(url, composer.CreateHttpContent());
+        }
+
+        public void RemoveObjectFromFolder(string repositoryId, string objectId, string folderId, IExtensionsData extension)
+        {
+            // build URL

[... 104 lines stripped ...]