- CMIS-331: Send Content-Disposition header when using AtomPub PUT for setContentStream
- CMIS-332: Support compression
- bugfixes

git-svn-id: https://svn.apache.org/repos/asf/chemistry/dotcmis/trunk@1082208 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/DotCMIS/binding/atompub/atompub.cs b/DotCMIS/binding/atompub/atompub.cs
index 02e5132..f3313a1 100644
--- a/DotCMIS/binding/atompub/atompub.cs
+++ b/DotCMIS/binding/atompub/atompub.cs
@@ -453,7 +453,7 @@
 

         protected HttpUtils.Response Put(UrlBuilder url, string contentType, HttpUtils.Output writer)

         {

-            HttpUtils.Response resp = HttpUtils.InvokePUT(url, contentType, writer, Session);

+            HttpUtils.Response resp = HttpUtils.InvokePUT(url, contentType, null, writer, Session);

 

             if ((int)resp.StatusCode < 200 || (int)resp.StatusCode > 299)

             {

@@ -1945,7 +1945,7 @@
             {

                 int b;

                 byte[] buffer = new byte[4096];

-                while ((b = contentStream.Stream.Read(buffer, 0, buffer.Length)) > -1)

+                while ((b = contentStream.Stream.Read(buffer, 0, buffer.Length)) > 0)

                 {

                     stream.Write(buffer, 0, b);

                 }

@@ -1953,8 +1953,16 @@
                 contentStream.Stream.Close();

             };

 

+            IDictionary<string, string> headers = null;

+            if (contentStream.FileName != null)

+            {

+                headers = new Dictionary<string, string>();

+                headers.Add(MimeHelper.ContentDisposition,

+                    MimeHelper.EncodeContentDisposition(MimeHelper.DispositionAttachment, contentStream.FileName));

+            }

+

             // send content

-            HttpUtils.Response resp = HttpUtils.InvokePUT(url, contentStream.MimeType, output, Session);

+            HttpUtils.Response resp = HttpUtils.InvokePUT(url, contentStream.MimeType, headers, output, Session);

 

             // check response code

             if (resp.StatusCode != HttpStatusCode.OK && resp.StatusCode != HttpStatusCode.Created && resp.StatusCode != HttpStatusCode.NoContent)

diff --git a/DotCMIS/binding/http.cs b/DotCMIS/binding/http.cs
index aabd311..68baaa4 100644
--- a/DotCMIS/binding/http.cs
+++ b/DotCMIS/binding/http.cs
@@ -18,6 +18,7 @@
  */

 using System;

 using System.Diagnostics;

+using System.Collections.Generic;

 using System.IO;

 using System.Net;

 using System.Text;

@@ -33,31 +34,31 @@
 

         public static Response InvokeGET(UrlBuilder url, BindingSession session)

         {

-            return Invoke(url, "GET", null, null, session, null, null);

+            return Invoke(url, "GET", null, null, session, null, null, null);

         }

 

         public static Response InvokeGET(UrlBuilder url, BindingSession session, int? offset, int? length)

         {

-            return Invoke(url, "GET", null, null, session, offset, length);

+            return Invoke(url, "GET", null, null, session, offset, length, null);

         }

 

         public static Response InvokePOST(UrlBuilder url, String contentType, Output writer, BindingSession session)

         {

-            return Invoke(url, "POST", contentType, writer, session, null, null);

+            return Invoke(url, "POST", contentType, writer, session, null, null, null);

         }

 

-        public static Response InvokePUT(UrlBuilder url, String contentType, Output writer, BindingSession session)

+        public static Response InvokePUT(UrlBuilder url, String contentType, IDictionary<string, string> headers, Output writer, BindingSession session)

         {

-            return Invoke(url, "PUT", contentType, writer, session, null, null);

+            return Invoke(url, "PUT", contentType, writer, session, null, null, headers);

         }

 

         public static Response InvokeDELETE(UrlBuilder url, BindingSession session)

         {

-            return Invoke(url, "DELETE", null, null, session, null, null);

+            return Invoke(url, "DELETE", null, null, session, null, null, null);

         }

 

         private static Response Invoke(UrlBuilder url, String method, String contentType, Output writer, BindingSession session,

-                int? offset, int? length)

+                int? offset, int? length, IDictionary<string, string> headers)

         {

             try

             {

@@ -67,6 +68,7 @@
                 // create connection           

                 HttpWebRequest conn = (HttpWebRequest)WebRequest.Create(url.Url);

                 conn.Method = method;

+                conn.UserAgent = "Apache Chemistry DotCMIS";

 

                 // set content type

                 if (contentType != null)

@@ -74,6 +76,15 @@
                     conn.ContentType = contentType;

                 }

 

+                // set additional headers

+                if (headers != null)

+                {

+                    foreach (KeyValuePair<string, string> header in headers)

+                    {

+                        conn.Headers.Add(header.Key, header.Value);

+                    }

+                }

+

                 // authenticate

                 AbstractAuthenticationProvider authProvider = session.GetAuthenticationProvider();

                 if (authProvider != null)

@@ -92,6 +103,13 @@
                     conn.AddRange(offset ?? 0);

                 }

 

+                // compression

+                string compressionFlag = session.GetValue(SessionParameter.Compression) as string;

+                if (compressionFlag != null && compressionFlag.ToLower().Equals("true"))

+                {

+                    conn.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;

+                }

+

                 // send data

                 if (writer != null)

                 {

@@ -255,4 +273,71 @@
             return Url.ToString();

         }

     }

+

+    internal class MimeHelper

+    {

+        public const string ContentDisposition = "Content-Disposition";

+        public const string DispositionAttachment = "attachment";

+        public const string DispositionFilename = "filename";

+

+        private const string MIMESpecials = "()<>@,;:\\\"/[]?=" + "\t ";

+        private const string RFC2231Specials = "*'%" + MIMESpecials;

+        private static char[] HexDigits = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };

+

+        public static string EncodeContentDisposition(string disposition, string filename)

+        {

+            if (disposition == null)

+            {

+                disposition = DispositionAttachment;

+            }

+            return disposition + EncodeRFC2231(DispositionFilename, filename);

+        }

+

+        protected static string EncodeRFC2231(string key, string value)

+        {

+            StringBuilder buf = new StringBuilder();

+            bool encoded = EncodeRFC2231value(value, buf);

+            if (encoded)

+            {

+                return "; " + key + "*=" + buf.ToString();

+            }

+            else

+            {

+                return "; " + key + "=" + value;

+            }

+        }

+

+        protected static bool EncodeRFC2231value(string value, StringBuilder buf)

+        {

+            buf.Append("UTF-8");

+            buf.Append("''"); // no language

+            byte[] bytes;

+            try

+            {

+                bytes = UTF8Encoding.UTF8.GetBytes(value);

+            }

+            catch (Exception)

+            {

+                return true;

+            }

+

+            bool encoded = false;

+            for (int i = 0; i < bytes.Length; i++)

+            {

+                int ch = bytes[i] & 0xff;

+                if (ch <= 32 || ch >= 127 || RFC2231Specials.IndexOf((char)ch) != -1)

+                {

+                    buf.Append('%');

+                    buf.Append(HexDigits[ch >> 4]);

+                    buf.Append(HexDigits[ch & 0xf]);

+                    encoded = true;

+                }

+                else

+                {

+                    buf.Append((char)ch);

+                }

+            }

+            return encoded;

+        }

+    }

 }

diff --git a/DotCMIS/const.cs b/DotCMIS/const.cs
index 2c70fa2..0555cbd 100644
--- a/DotCMIS/const.cs
+++ b/DotCMIS/const.cs
@@ -55,6 +55,9 @@
         // authentication provider

         public const string AuthenticationProviderClass = "org.apache.chemistry.dotcmis.binding.auth.classname";

 

+        // compression flag

+        public const string Compression = "org.apache.chemistry.dotcmis.binding.compression";

+

         // binding caches

         public const string CacheSizeRepositories = "org.apache.chemistry.dotcmis.binding.cache.repositories.size";

         public const string CacheSizeTypes = "org.apache.chemistry.dotcmis.binding.cache.types.size";

diff --git a/DotCMISUnitTest/SmokeTest.cs b/DotCMISUnitTest/SmokeTest.cs
index 7dfc352..e91761a 100644
--- a/DotCMISUnitTest/SmokeTest.cs
+++ b/DotCMISUnitTest/SmokeTest.cs
@@ -245,7 +245,18 @@
             Assert.AreEqual(properties[PropertyIds.Name], nameProperty.Value);

             Assert.AreEqual(properties[PropertyIds.Name], nameProperty.FirstValue);

 

-            doc.Delete(true);

+

+            byte[] content2 = UTF8Encoding.UTF8.GetBytes("Hello Universe!");

+

+            ContentStream contentStream2 = new ContentStream();

+            contentStream2.FileName = properties[PropertyIds.Name] as string;

+            contentStream2.MimeType = "text/plain";

+            contentStream2.Length = content2.Length;

+            contentStream2.Stream = new MemoryStream(content2);

+

+            // doc.SetContentStream(contentStream2, true);

+

+            doc2.Delete(true);

 

             try

             {