Added: chemistry/portcmis/PortCMISWin/binding/WindowsHttp.cs URL: http://svn.apache.org/viewvc/chemistry/portcmis/PortCMISWin/binding/WindowsHttp.cs?rev=1691890&view=auto ============================================================================== --- chemistry/portcmis/PortCMISWin/binding/WindowsHttp.cs (added) +++ chemistry/portcmis/PortCMISWin/binding/WindowsHttp.cs Mon Jul 20 08:48:57 2015 @@ -0,0 +1,391 @@ +/* +* 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.Client; +using PortCMIS.Exceptions; +using PortCMIS.Utils; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices.WindowsRuntime; +using System.Threading; +using System.Threading.Tasks; +using Windows.Foundation; +using Windows.Storage.Streams; +using Windows.Web.Http; +using Windows.Web.Http.Filters; +using Windows.Web.Http.Headers; + + +namespace PortCMIS.Binding.Http +{ + public class WindowsHttpInvoker : IHttpInvoker + { + private const string InvokerHttpClient = "org.apache.chemistry.portcmis.invoker.httpclient"; + private object invokerLock = new object(); + + public IResponse InvokeGET(UrlBuilder url, IBindingSession session) + { + return Invoke(url, HttpMethod.Get, null, session, null, null, null); + } + + public IResponse InvokeGET(UrlBuilder url, IBindingSession session, long? offset, long? length) + { + return Invoke(url, HttpMethod.Get, null, session, offset, length, null); + } + + public IResponse InvokePOST(UrlBuilder url, System.Net.Http.HttpContent content, IBindingSession session) + { + return Invoke(url, HttpMethod.Post, content, session, null, null, null); + } + + public IResponse InvokePUT(UrlBuilder url, IDictionary<string, string> headers, System.Net.Http.HttpContent content, IBindingSession session) + { + return Invoke(url, HttpMethod.Put, content, session, null, null, headers); + } + + public IResponse InvokeDelete(UrlBuilder url, IBindingSession session) + { + return Invoke(url, HttpMethod.Delete, null, session, null, null, null); + } + + private IResponse Invoke(UrlBuilder url, HttpMethod method, System.Net.Http.HttpContent content, IBindingSession session, + long? offset, long? length, IDictionary<string, string> headers) + { + if (Logger.IsDebugEnabled) + { + Logger.Debug("HTTP: " + method.ToString() + " " + url.ToString()); + } + + IWindowsAuthenticationProvider authProvider = session.GetAuthenticationProvider() as IWindowsAuthenticationProvider; + + HttpClient httpClient = session.GetValue(InvokerHttpClient) as HttpClient; + + if (httpClient == null) + { + lock (invokerLock) + { + httpClient = session.GetValue(InvokerHttpClient) as HttpClient; + if (httpClient == null) + { + HttpBaseProtocolFilter httpClientFilter = new HttpBaseProtocolFilter(); + + // redirects + httpClientFilter.AllowAutoRedirect = false; + + // compression + string compressionFlag = session.GetValue(SessionParameter.Compression) as string; + if (compressionFlag != null && compressionFlag.ToLowerInvariant().Equals("true")) + { + httpClientFilter.AutomaticDecompression = true; + } + + // authentictaion + httpClientFilter.AllowUI = false; + + // authentication provider + if (authProvider != null) + { + authProvider.PrepareHttpClientFilter(httpClientFilter); + } + + // create HttpClient + httpClient = new HttpClient(httpClientFilter); + + session.PutValue(InvokerHttpClient, httpClient); + } + } + } + + HttpRequestMessage request = new HttpRequestMessage(method, new Uri(url.ToString())); + + // set additional headers + request.Headers.UserAgent.Add(new HttpProductInfoHeaderValue("ApacheChemistryPortCMIS", "0.1")); + if (headers != null) + { + foreach (KeyValuePair<string, string> header in headers) + { + request.Headers.Add(header.Key, header.Value); + } + } + + // range + if (offset != null && length != null) + { + long longOffset = offset.Value < 0 ? 0 : offset.Value; + if (length.Value > 0) + { + request.Headers.Add(new KeyValuePair<string, string>("Range", "bytes=" + longOffset + "-" + (longOffset + length.Value - 1))); + } + else + { + request.Headers.Add(new KeyValuePair<string, string>("Range", "bytes=" + longOffset + "-")); + } + } + else if (offset != null && offset.Value > 0) + { + request.Headers.Add(new KeyValuePair<string, string>("Range", "bytes=" + offset.Value + "-")); + } + + // content + if (content != null) + { + request.Content = new ConvertedHttpContent(content); + + if (request.Content.Headers.ContentLength == null) + { + request.Headers.TransferEncoding.TryParseAdd("chunked"); + } + } + + // authentication provider + if (authProvider != null) + { + authProvider.PrepareHttpRequestMessage(request); + } + + // timeouts + int timeout = session.GetValue(SessionParameter.ConnectTimeout, -2); + + WindowsResponse response; + try + { + Task<HttpResponseMessage> task = Send(httpClient, request, timeout); + if (task.IsFaulted) + { + throw task.Exception; + } + else + { + HttpResponseMessage httpResponseMessage = task.Result; + + if (authProvider != null) + { + authProvider.HandleResponse(httpResponseMessage); + } + response = new WindowsResponse(httpResponseMessage); + } + } + catch (Exception e) + { + throw new CmisConnectionException("Cannot access " + url + ": " + e.Message, e); + } + + return response; + } + + private async Task<HttpResponseMessage> Send(HttpClient httpClient, HttpRequestMessage request, int timeout) + { + if (timeout > 0) + { + CancellationTokenSource cts = new CancellationTokenSource(); + cts.CancelAfter(TimeSpan.FromMilliseconds(timeout)); + return await httpClient.SendRequestAsync(request, HttpCompletionOption.ResponseHeadersRead).AsTask(cts.Token).ConfigureAwait(false); + } + else + { + return await httpClient.SendRequestAsync(request, HttpCompletionOption.ResponseHeadersRead).AsTask().ConfigureAwait(false); + } + } + } + + class ConvertedHttpContent : IHttpContent + { + private System.Net.Http.HttpContent content; + + public ConvertedHttpContent(System.Net.Http.HttpContent httpContent) + { + content = httpContent; + Headers = new HttpContentHeaderCollection(); + + foreach (KeyValuePair<string, IEnumerable<string>> header in httpContent.Headers) + { + Headers.Add(header.Key, header.Value.First()); + } + } + + public HttpContentHeaderCollection Headers { get; set; } + + public IAsyncOperationWithProgress<ulong, ulong> BufferAllAsync() + { + return AsyncInfo.Run<ulong, ulong>(async (token, progress) => + { + await content.LoadIntoBufferAsync(); + return 0; + }); + } + + public IAsyncOperationWithProgress<IBuffer, ulong> ReadAsBufferAsync() + { + return AsyncInfo.Run<IBuffer, ulong>(async (token, progress) => + { + return (await content.ReadAsByteArrayAsync()).AsBuffer(); + }); + } + + public IAsyncOperationWithProgress<IInputStream, ulong> ReadAsInputStreamAsync() + { + return AsyncInfo.Run<IInputStream, ulong>(async (token, progress) => + { + return (await content.ReadAsStreamAsync()).AsInputStream(); + }); + } + + public IAsyncOperationWithProgress<string, ulong> ReadAsStringAsync() + { + return AsyncInfo.Run<string, ulong>((token, progress) => + { + return content.ReadAsStringAsync(); + }); + } + + public bool TryComputeLength(out ulong length) + { + length = 0; + return false; + } + + public IAsyncOperationWithProgress<ulong, ulong> WriteToStreamAsync(IOutputStream outputStream) + { + return AsyncInfo.Run<ulong, ulong>(async (token, progress) => + { + Stream stream = outputStream.AsStreamForWrite(); + await content.CopyToAsync(stream); + stream.Flush(); + + return 0; + }); + } + + public void Dispose() + { + } + } + + + class WindowsResponse : IResponse + { + private HttpResponseMessage response; + + public int 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 string Charset { get; private set; } + public long? ContentLength { get; private set; } + public string Filename { get; private set; } + + public WindowsResponse(HttpResponseMessage httpResponse) + { + this.response = httpResponse; + StatusCode = (int)httpResponse.StatusCode; + Message = httpResponse.ReasonPhrase; + + bool isBase64 = false; + + if (httpResponse.Content != null) + { + if (httpResponse.Content.Headers.ContentType != null) + { + ContentType = httpResponse.Content.Headers.ContentType.MediaType; + Charset = httpResponse.Content.Headers.ContentType.CharSet; + } + ContentLength = (long?)httpResponse.Content.Headers.ContentLength; + + if (httpResponse.Content.Headers.ContentDisposition != null) + { + Filename = httpResponse.Content.Headers.ContentDisposition.FileName; + } + + string contentTransferEncoding; + if (httpResponse.Content.Headers.TryGetValue("Content-Transfer-Encoding", out contentTransferEncoding)) + { + isBase64 = contentTransferEncoding == "base64"; + } + } + + if (httpResponse.StatusCode == HttpStatusCode.Ok || + httpResponse.StatusCode == HttpStatusCode.Created || + httpResponse.StatusCode == HttpStatusCode.NonAuthoritativeInformation || + httpResponse.StatusCode == HttpStatusCode.PartialContent) + { + Stream = GetContentStream().Result.AsStreamForRead(); + + if (isBase64) + { + // TODO: this is only required for the AtomPub binding of SharePoint 2010 + // Stream = new CryptoStream(Stream, new FromBase64Transform(), CryptoStreamMode.Read); + } + } + else + { + if (httpResponse.StatusCode != HttpStatusCode.NoContent) + { + if (ContentType != null && + (ContentType.ToLowerInvariant().StartsWith("text/") || + ContentType.ToLowerInvariant().EndsWith("+xml") || + ContentType.ToLowerInvariant().StartsWith("application/xml") || + ContentType.ToLowerInvariant().StartsWith("application/json"))) + { + + ErrorContent = GetContentString().Result; + } + } + + try + { + response.Dispose(); + response = null; + } + catch (Exception) { } + } + } + + public void CloseStream() + { + if (Stream != null) + { + Stream.Dispose(); + Stream = null; + } + + if (response != null) + { + try + { + response.Dispose(); + response = null; + } + catch (Exception) { } + } + } + + async private Task<IInputStream> GetContentStream() + { + return await response.Content.ReadAsInputStreamAsync().AsTask().ConfigureAwait(false); + } + + async private Task<String> GetContentString() + { + return await response.Content.ReadAsStringAsync().AsTask().ConfigureAwait(false); + } + } +}
Added: chemistry/portcmis/PortCMISWin/build.bat URL: http://svn.apache.org/viewvc/chemistry/portcmis/PortCMISWin/build.bat?rev=1691890&view=auto ============================================================================== --- chemistry/portcmis/PortCMISWin/build.bat (added) +++ chemistry/portcmis/PortCMISWin/build.bat Mon Jul 20 08:48:57 2015 @@ -0,0 +1,34 @@ +@echo off + +rem +rem Licensed to the Apache Software Foundation (ASF) under one +rem or more contributor license agreements. See the NOTICE file +rem distributed with this work for additional information +rem regarding copyright ownership. The ASF licenses this file +rem to you under the Apache License, Version 2.0 (the +rem "License"); you may not use this file except in compliance +rem with the License. You may obtain a copy of the License at +rem +rem http://www.apache.org/licenses/LICENSE-2.0 +rem +rem Unless required by applicable law or agreed to in writing, +rem software distributed under the License is distributed on an +rem "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +rem KIND, either express or implied. See the License for the +rem specific language governing permissions and limitations +rem under the License. + +echo Removing old DLLs +rmdir /Q /S bin +rmdir /Q /S obj + +echo Building Debug DLL... +msbuild PortCMISWin.csproj /ToolsVersion:4.0 /p:Configuration=Debug + +echo Building Release DLL... +msbuild PortCMISWin.csproj /ToolsVersion:4.0 /p:Configuration=Release + +nuget pack + +rem echo Building documentation... +rem msbuild PortCMISWin.shfbproj /ToolsVersion:4.0 /p:Configuration=Release \ No newline at end of file Added: chemistry/portcmis/PortCMISWin/client/ClientImpl.cs URL: http://svn.apache.org/viewvc/chemistry/portcmis/PortCMISWin/client/ClientImpl.cs?rev=1691890&view=auto ============================================================================== --- chemistry/portcmis/PortCMISWin/client/ClientImpl.cs (added) +++ chemistry/portcmis/PortCMISWin/client/ClientImpl.cs Mon Jul 20 08:48:57 2015 @@ -0,0 +1,89 @@ +/* +* 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 System.Collections.Generic; + +namespace PortCMIS.Client.Impl +{ + /// <summary> + /// Session factory implementation for Store Apps. + /// </summary> + public class WindowsSessionFactory : ISessionFactory + { + protected SessionFactory DefaultSessionFactory { get; private set; } + + private WindowsSessionFactory() + { + DefaultSessionFactory = SessionFactory.NewInstance(); + } + + public static WindowsSessionFactory NewInstance() + { + return new WindowsSessionFactory(); + } + + public ISession CreateSession(IDictionary<string, string> parameters) + { + return DefaultSessionFactory.CreateSession(AddWindowClasses(parameters)); + } + + public ISession CreateSession(IDictionary<string, string> parameters, IObjectFactory objectFactory, IAuthenticationProvider authenticationProvider, ICache cache) + { + return DefaultSessionFactory.CreateSession(AddWindowClasses(parameters), objectFactory, authenticationProvider, cache); + } + + public IList<IRepository> GetRepositories(IDictionary<string, string> parameters) + { + return DefaultSessionFactory.GetRepositories(AddWindowClasses(parameters)); + } + + public IList<IRepository> GetRepositories(IDictionary<string, string> parameters, IObjectFactory objectFactory, IAuthenticationProvider authenticationProvider, ICache cache) + { + return DefaultSessionFactory.GetRepositories(AddWindowClasses(parameters), objectFactory, authenticationProvider, cache); + } + + protected IDictionary<string, string> AddWindowClasses(IDictionary<string, string> parameters) + { + if (parameters == null) + { + return parameters; + } + + if (parameters.ContainsKey(SessionParameter.HttpInvokerClass) && parameters.ContainsKey(SessionParameter.AuthenticationProviderClass)) + { + return parameters; + } + + IDictionary<string, string> newParameters = new Dictionary<string, string>(parameters); + + if (!newParameters.ContainsKey(SessionParameter.HttpInvokerClass)) + { + newParameters.Add(SessionParameter.HttpInvokerClass, typeof(PortCMIS.Binding.Http.WindowsHttpInvoker).AssemblyQualifiedName); + } + + if (!newParameters.ContainsKey(SessionParameter.AuthenticationProviderClass)) + { + newParameters.Add(SessionParameter.AuthenticationProviderClass, typeof(PortCMIS.Binding.StandardWindowsAuthenticationProvider).AssemblyQualifiedName); + } + + return newParameters; + } + } +} Added: chemistry/portcmis/README URL: http://svn.apache.org/viewvc/chemistry/portcmis/README?rev=1691890&view=auto ============================================================================== --- chemistry/portcmis/README (added) +++ chemistry/portcmis/README Mon Jul 20 08:48:57 2015 @@ -0,0 +1,29 @@ +Apache Chemistry PortCMIS 0.1 +============================= + +Apache Chemistry PortCMIS is a Content Management Interoperability Services (CMIS) +client library for the .NET framework 4.5 or higher. + +See http://chemistry.apache.org/dotnet/portcmis.html for more information. + +This release is a bug fix release. +If you find a problem, please let us know: https://issues.apache.org/jira/browse/CMIS + + +Change log +---------- + +PortCMIS 0.1: + +Initial relase + + +Strong-Name signing +------------------- + +The DLL included in this release is signed with a public/private key pair that is also +included in this package. This allows you to deploy it to the global assembly cache (GAC) +and to make changes to the PortCMIS source code without recompiling your application. +However, since the private key is publicly available, this strong name cannot be trusted. +If a trust relationship between your application and the PortCMIS DLL is important to you, +you have to rebuild the DLL from the source code and sign it yourself. Added: chemistry/portcmis/release.bat URL: http://svn.apache.org/viewvc/chemistry/portcmis/release.bat?rev=1691890&view=auto ============================================================================== --- chemistry/portcmis/release.bat (added) +++ chemistry/portcmis/release.bat Mon Jul 20 08:48:57 2015 @@ -0,0 +1,93 @@ +@echo off + +rem +rem Licensed to the Apache Software Foundation (ASF) under one +rem or more contributor license agreements. See the NOTICE file +rem distributed with this work for additional information +rem regarding copyright ownership. The ASF licenses this file +rem to you under the Apache License, Version 2.0 (the +rem "License"); you may not use this file except in compliance +rem with the License. You may obtain a copy of the License at +rem +rem http://www.apache.org/licenses/LICENSE-2.0 +rem +rem Unless required by applicable law or agreed to in writing, +rem software distributed under the License is distributed on an +rem "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +rem KIND, either express or implied. See the License for the +rem specific language governing permissions and limitations +rem under the License. +rem + +rem This batch file creates a release. +rem It requires Cygwin. + +set PORTCMISVERSION=0.1 +set PORTCMISZIPSRC=chemistry-portcmis-%PORTCMISVERSION%-src.zip +set PORTCMISZIPBIN=chemistry-portcmis-%PORTCMISVERSION%-bin.zip +set PORTCMISRC=RC1 + +set CYGWIN=ntea + +echo Building... +cd PortCMIS +call build.bat +cd .. + +echo Creating release directories... +rmdir /S /Q release-src +mkdir release-src +rmdir /S /Q release-bin +mkdir release-bin + +echo Copying readme, etc... +copy LICENSE release-src +copy LICENSE release-bin +copy NOTICE release-src +copy NOTICE release-bin +copy DEPENDENCIES release-src +copy DEPENDENCIES release-bin +copy README release-src +copy README release-bin + +echo Copying binaries ... +copy PortCMIS\bin\Release\PortCMIS.dll release-bin +copy PortCMIS\doc\PortCMIS.chm release-bin +chmod -R a+rwx release-bin + +echo Copying source... +mkdir release-src\src +xcopy PortCMIS release-src\src /E +rmdir /S /Q release-src\src\bin +rmdir /S /Q release-src\src\obj +rmdir /S /Q release-src\src\doc +chmod -R a+rwx release-src + +echo Creating release file... +rmdir /S /Q artifacts +mkdir artifacts + +cd release-src +zip -r ../artifacts/%PORTCMISZIPSRC% * +cd .. + +cd release-bin +zip -r ../artifacts/%PORTCMISZIPBIN% * +cd .. + +echo Signing release file... +cd artifacts + +gpg --armor --output %PORTCMISZIPSRC%.asc --detach-sig %PORTCMISZIPSRC% +gpg --print-md MD5 %PORTCMISZIPSRC% > %PORTCMISZIPSRC%.md5 +gpg --print-md SHA512 %PORTCMISZIPSRC% > %PORTCMISZIPSRC%.sha + +gpg --armor --output %PORTCMISZIPBIN%.asc --detach-sig %PORTCMISZIPBIN% +gpg --print-md MD5 %PORTCMISZIPBIN% > %PORTCMISZIPBIN%.md5 +gpg --print-md SHA512 %PORTCMISZIPBIN% > %PORTCMISZIPBIN%.sha + +cd .. + +echo Creating RC tag +rem svn copy https://svn.apache.org/repos/asf/chemistry/portcmis/trunk https://svn.apache.org/repos/asf/chemistry/portcmis/tags/chemistry-portcmis-%PORTCMISVERSION%-%PORTCMISRC% +
