View Javadoc
1   /*
2    * DavMail POP/IMAP/SMTP/CalDav/LDAP Exchange Gateway
3    * Copyright (C) 2010  Mickael Guessant
4    *
5    * This program is free software; you can redistribute it and/or
6    * modify it under the terms of the GNU General Public License
7    * as published by the Free Software Foundation; either version 2
8    * of the License, or (at your option) any later version.
9    *
10   * This program is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   * GNU General Public License for more details.
14   *
15   * You should have received a copy of the GNU General Public License
16   * along with this program; if not, write to the Free Software
17   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18   */
19  
20  package davmail.http.request;
21  
22  import davmail.exchange.dav.PropertyValue;
23  import org.apache.http.HttpResponse;
24  import org.apache.http.StatusLine;
25  import org.apache.jackrabbit.webdav.MultiStatusResponse;
26  import org.apache.log4j.Logger;
27  
28  import java.io.ByteArrayOutputStream;
29  import java.io.IOException;
30  import java.io.OutputStreamWriter;
31  import java.nio.charset.StandardCharsets;
32  import java.util.HashMap;
33  import java.util.HashSet;
34  import java.util.List;
35  import java.util.Map;
36  import java.util.Set;
37  
38  /**
39   * Custom Exchange PROPPATCH method.
40   * Supports extended property update with type.
41   */
42  
43  public class ExchangePropPatchRequest extends ExchangeDavRequest {
44      protected static final Logger LOGGER = Logger.getLogger(ExchangePropPatchRequest.class);
45      static final String TYPE_NAMESPACE = "urn:schemas-microsoft-com:datatypes";
46      final Set<PropertyValue> propertyValues;
47      private StatusLine statusLine;
48  
49      /**
50       * Create PROPPATCH method.
51       *
52       * @param path           path
53       * @param propertyValues property values
54       */
55      public ExchangePropPatchRequest(String path, Set<PropertyValue> propertyValues) {
56          super(path);
57          this.propertyValues = propertyValues;
58      }
59  
60      @Override
61      protected byte[] generateRequestContent() {
62          try {
63              // build namespace map
64              int currentChar = 'e';
65              final Map<String, Integer> nameSpaceMap = new HashMap<>();
66              final Set<PropertyValue> setPropertyValues = new HashSet<>();
67              final Set<PropertyValue> deletePropertyValues = new HashSet<>();
68              for (PropertyValue propertyValue : propertyValues) {
69                  // data type namespace
70                  if (!nameSpaceMap.containsKey(TYPE_NAMESPACE) && propertyValue.getTypeString() != null) {
71                      nameSpaceMap.put(TYPE_NAMESPACE, currentChar++);
72                  }
73                  // property namespace
74                  String namespaceUri = propertyValue.getNamespaceUri();
75                  if (!nameSpaceMap.containsKey(namespaceUri)) {
76                      nameSpaceMap.put(namespaceUri, currentChar++);
77                  }
78                  if (propertyValue.getXmlEncodedValue() == null) {
79                      deletePropertyValues.add(propertyValue);
80                  } else {
81                      setPropertyValues.add(propertyValue);
82                  }
83              }
84              ByteArrayOutputStream baos = new ByteArrayOutputStream();
85              try (
86                      OutputStreamWriter writer = new OutputStreamWriter(baos, StandardCharsets.UTF_8)
87              ) {
88                  writer.write("<D:propertyupdate xmlns:D=\"DAV:\"");
89                  for (Map.Entry<String, Integer> mapEntry : nameSpaceMap.entrySet()) {
90                      writer.write(" xmlns:");
91                      writer.write((char) mapEntry.getValue().intValue());
92                      writer.write("=\"");
93                      writer.write(mapEntry.getKey());
94                      writer.write("\"");
95                  }
96                  writer.write(">");
97                  if (!setPropertyValues.isEmpty()) {
98                      writer.write("<D:set><D:prop>");
99                      for (PropertyValue propertyValue : setPropertyValues) {
100                         String typeString = propertyValue.getTypeString();
101                         char nameSpaceChar = (char) nameSpaceMap.get(propertyValue.getNamespaceUri()).intValue();
102                         writer.write('<');
103                         writer.write(nameSpaceChar);
104                         writer.write(':');
105                         writer.write(propertyValue.getName());
106                         if (typeString != null) {
107                             writer.write(' ');
108                             writer.write(nameSpaceMap.get(TYPE_NAMESPACE));
109                             writer.write(":dt=\"");
110                             writer.write(typeString);
111                             writer.write("\"");
112                         }
113                         writer.write('>');
114                         writer.write(propertyValue.getXmlEncodedValue());
115                         writer.write("</");
116                         writer.write(nameSpaceChar);
117                         writer.write(':');
118                         writer.write(propertyValue.getName());
119                         writer.write('>');
120                     }
121                     writer.write("</D:prop></D:set>");
122                 }
123                 if (!deletePropertyValues.isEmpty()) {
124                     writer.write("<D:remove><D:prop>");
125                     for (PropertyValue propertyValue : deletePropertyValues) {
126                         char nameSpaceChar = (char) nameSpaceMap.get(propertyValue.getNamespaceUri()).intValue();
127                         writer.write('<');
128                         writer.write(nameSpaceChar);
129                         writer.write(':');
130                         writer.write(propertyValue.getName());
131                         writer.write("/>");
132                     }
133                     writer.write("</D:prop></D:remove>");
134                 }
135                 writer.write("</D:propertyupdate>");
136             }
137             return baos.toByteArray();
138         } catch (IOException e) {
139             throw new RuntimeException(e);
140         }
141     }
142 
143     @Override
144     public String getMethod() {
145         return "PROPPATCH";
146     }
147 
148     @Override
149     public List<MultiStatusResponse> handleResponse(HttpResponse response) {
150         this.statusLine = response.getStatusLine();
151         return super.handleResponse(response);
152     }
153 
154 
155     public StatusLine getStatusLine() {
156         return statusLine;
157     }
158 }