View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   * http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.chemistry.opencmis.client.bindings.spi.browser;
20  
21  import static org.apache.chemistry.opencmis.commons.impl.CollectionsHelper.isNotEmpty;
22  import static org.apache.chemistry.opencmis.commons.impl.CollectionsHelper.isNullOrEmpty;
23  
24  import java.io.IOException;
25  import java.io.InputStream;
26  import java.io.OutputStream;
27  import java.net.URLEncoder;
28  import java.util.GregorianCalendar;
29  import java.util.LinkedHashMap;
30  import java.util.List;
31  import java.util.Map;
32  
33  import org.apache.chemistry.opencmis.commons.data.Ace;
34  import org.apache.chemistry.opencmis.commons.data.Acl;
35  import org.apache.chemistry.opencmis.commons.data.BulkUpdateObjectIdAndChangeToken;
36  import org.apache.chemistry.opencmis.commons.data.ContentStream;
37  import org.apache.chemistry.opencmis.commons.data.Properties;
38  import org.apache.chemistry.opencmis.commons.data.PropertyData;
39  import org.apache.chemistry.opencmis.commons.enums.DateTimeFormat;
40  import org.apache.chemistry.opencmis.commons.impl.Constants;
41  import org.apache.chemistry.opencmis.commons.impl.DateTimeHelper;
42  import org.apache.chemistry.opencmis.commons.impl.IOUtils;
43  import org.apache.chemistry.opencmis.commons.impl.MimeHelper;
44  import org.apache.chemistry.opencmis.commons.impl.UrlBuilder;
45  
46  public final class FormDataWriter {
47  
48      private static final String CONTENT_TYPE_URLENCODED = "application/x-www-form-urlencoded;charset=utf-8";
49      private static final String CONTENT_TYPE_FORMDATA = "multipart/form-data; boundary=";
50      private static final String CRLF = "\r\n";
51      private static final int BUFFER_SIZE = 64 * 1024;
52  
53      private final String boundary;
54      private final Map<String, String> parameters = new LinkedHashMap<String, String>();
55      private ContentStream contentStream;
56  
57      public FormDataWriter(String action) {
58          this(action, null);
59      }
60  
61      public FormDataWriter(String action, ContentStream contentStream) {
62          addParameter(Constants.CONTROL_CMISACTION, action);
63          this.contentStream = contentStream;
64          boundary = "aPacHeCheMIStryoPEncmiS" + Long.toHexString(action.hashCode()) + action
65                  + Long.toHexString(System.currentTimeMillis()) + Long.toHexString(this.hashCode());
66      }
67  
68      public void addParameter(String name, Object value) {
69          if (name == null || value == null) {
70              return;
71          }
72  
73          parameters.put(name, UrlBuilder.normalizeParameter(value));
74      }
75  
76      public void addPropertiesParameters(Properties properties, DateTimeFormat dateTimeFormat) {
77          if (properties == null) {
78              return;
79          }
80  
81          int idx = 0;
82          for (PropertyData<?> prop : properties.getPropertyList()) {
83              if (prop == null) {
84                  continue;
85              }
86  
87              String idxStr = "[" + idx + "]";
88              addParameter(Constants.CONTROL_PROP_ID + idxStr, prop.getId());
89  
90              if (isNotEmpty(prop.getValues())) {
91                  if (prop.getValues().size() == 1) {
92                      addParameter(Constants.CONTROL_PROP_VALUE + idxStr,
93                              convertPropertyValue(prop.getFirstValue(), dateTimeFormat));
94                  } else {
95                      int vidx = 0;
96                      for (Object obj : prop.getValues()) {
97                          String vidxStr = "[" + vidx + "]";
98                          addParameter(Constants.CONTROL_PROP_VALUE + idxStr + vidxStr,
99                                  convertPropertyValue(obj, dateTimeFormat));
100                         vidx++;
101                     }
102                 }
103             }
104 
105             idx++;
106         }
107     }
108 
109     public void addSuccinctFlag(boolean succinct) {
110         if (succinct) {
111             addParameter(Constants.CONTROL_SUCCINCT, "true");
112         }
113     }
114 
115     public void addPoliciesParameters(List<String> policies) {
116         if (policies == null) {
117             return;
118         }
119 
120         int idx = 0;
121         for (String policy : policies) {
122             if (policy != null) {
123                 String idxStr = "[" + idx + "]";
124                 addParameter(Constants.CONTROL_POLICY + idxStr, policy);
125                 idx++;
126             }
127         }
128     }
129 
130     public void addPolicyIdParameter(String policyId) {
131         if (policyId == null) {
132             return;
133         }
134 
135         addParameter(Constants.CONTROL_POLICY_ID, policyId);
136     }
137 
138     public void addAddAcesParameters(Acl acl) {
139         addAcesParameters(acl, Constants.CONTROL_ADD_ACE_PRINCIPAL, Constants.CONTROL_ADD_ACE_PERMISSION);
140     }
141 
142     public void addRemoveAcesParameters(Acl acl) {
143         addAcesParameters(acl, Constants.CONTROL_REMOVE_ACE_PRINCIPAL, Constants.CONTROL_REMOVE_ACE_PERMISSION);
144     }
145 
146     private void addAcesParameters(Acl acl, String principalControl, String permissionControl) {
147         if (acl == null || acl.getAces() == null) {
148             return;
149         }
150 
151         int idx = 0;
152         for (Ace ace : acl.getAces()) {
153             if (ace.getPrincipalId() != null && isNotEmpty(ace.getPermissions())) {
154                 String idxStr = "[" + idx + "]";
155                 addParameter(principalControl + idxStr, ace.getPrincipalId());
156 
157                 int permIdx = 0;
158                 for (String perm : ace.getPermissions()) {
159                     if (perm != null) {
160                         String permIdxStr = "[" + permIdx + "]";
161                         addParameter(permissionControl + idxStr + permIdxStr, perm);
162                         permIdx++;
163                     }
164                 }
165                 idx++;
166             }
167         }
168     }
169 
170     public void addObjectIdsAndChangeTokens(List<BulkUpdateObjectIdAndChangeToken> objectIdsAndChangeTokens) {
171         if (isNullOrEmpty(objectIdsAndChangeTokens)) {
172             return;
173         }
174 
175         int idx = 0;
176         for (BulkUpdateObjectIdAndChangeToken oc : objectIdsAndChangeTokens) {
177             if (oc == null || oc.getId() == null || oc.getId().length() == 0) {
178                 continue;
179             }
180 
181             String idxStr = "[" + idx + "]";
182             addParameter(Constants.CONTROL_OBJECT_ID + idxStr, oc.getId());
183             addParameter(Constants.CONTROL_CHANGE_TOKEN + idxStr,
184                     (oc.getChangeToken() == null ? "" : oc.getChangeToken()));
185 
186             idx++;
187         }
188     }
189 
190     public void addSecondaryTypeIds(List<String> secondaryTypeIds) {
191         addSecondaryTypeIdParameters(secondaryTypeIds, Constants.CONTROL_ADD_SECONDARY_TYPE);
192     }
193 
194     public void removeSecondaryTypeIds(List<String> secondaryTypeIds) {
195         addSecondaryTypeIdParameters(secondaryTypeIds, Constants.CONTROL_REMOVE_SECONDARY_TYPE);
196     }
197 
198     private void addSecondaryTypeIdParameters(List<String> secondaryTypeIds, String secondaryTypeIdControl) {
199         if (isNullOrEmpty(secondaryTypeIds)) {
200             return;
201         }
202 
203         int idx = 0;
204         for (String typeId : secondaryTypeIds) {
205             if (typeId == null || typeId.length() == 0) {
206                 continue;
207             }
208 
209             String idxStr = "[" + idx + "]";
210             addParameter(secondaryTypeIdControl + idxStr, typeId);
211 
212             idx++;
213         }
214     }
215 
216     private String convertPropertyValue(Object value, DateTimeFormat dateTimeFormat) {
217         if (value == null) {
218             return null;
219         }
220 
221         if (value instanceof GregorianCalendar) {
222             if (dateTimeFormat == DateTimeFormat.EXTENDED) {
223                 return DateTimeHelper.formatXmlDateTime((GregorianCalendar) value);
224             } else {
225                 return String.valueOf(((GregorianCalendar) value).getTimeInMillis());
226             }
227         }
228 
229         return value.toString();
230     }
231 
232     public String getContentType() {
233         return contentStream == null ? CONTENT_TYPE_URLENCODED : CONTENT_TYPE_FORMDATA + boundary;
234     }
235 
236     public void write(OutputStream out) throws IOException {
237         InputStream stream = contentStream == null ? null : contentStream.getStream();
238 
239         if (stream == null) {
240             boolean first = true;
241             byte[] amp = IOUtils.toUTF8Bytes("&");
242 
243             for (Map.Entry<String, String> param : parameters.entrySet()) {
244                 if (first) {
245                     first = false;
246                 } else {
247                     out.write(amp);
248                 }
249 
250                 out.write(IOUtils.toUTF8Bytes(param.getKey() + "=" + URLEncoder.encode(param.getValue(), IOUtils.UTF8)));
251             }
252         } else {
253             writeLine(out);
254 
255             // parameters
256             for (Map.Entry<String, String> param : parameters.entrySet()) {
257                 writeLine(out, "--" + boundary);
258                 writeLine(out, "Content-Disposition: form-data; name=\"" + param.getKey() + "\"");
259                 writeLine(out, "Content-Type: text/plain; charset=utf-8");
260                 writeLine(out);
261                 writeLine(out, param.getValue());
262             }
263 
264             // content
265             String filename = contentStream.getFileName();
266             if (filename == null || filename.length() == 0) {
267                 filename = "content";
268             }
269 
270             String mediaType = contentStream.getMimeType();
271             if (mediaType == null || mediaType.indexOf('/') < 1 || mediaType.indexOf('\n') > -1
272                     || mediaType.indexOf('\r') > -1) {
273                 mediaType = Constants.MEDIATYPE_OCTETSTREAM;
274             }
275 
276             writeLine(out, "--" + boundary);
277             writeLine(
278                     out,
279                     "Content-Disposition: "
280                             + MimeHelper.encodeContentDisposition(MimeHelper.DISPOSITION_FORM_DATA_CONTENT, filename));
281             writeLine(out, "Content-Type: " + mediaType);
282             writeLine(out, "Content-Transfer-Encoding: binary");
283             writeLine(out);
284 
285             IOUtils.copy(stream, out, BUFFER_SIZE);
286 
287             writeLine(out);
288             writeLine(out, "--" + boundary + "--");
289         }
290     }
291 
292     private void writeLine(OutputStream out) throws IOException {
293         writeLine(out, null);
294     }
295 
296     private void writeLine(OutputStream out, String s) throws IOException {
297         out.write(IOUtils.toUTF8Bytes(s == null ? CRLF : s + CRLF));
298     }
299 }