1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package davmail.http.request;
21
22 import davmail.exchange.XMLStreamUtil;
23 import org.apache.http.Header;
24 import org.apache.http.HttpResponse;
25 import org.apache.http.HttpStatus;
26 import org.apache.http.client.HttpResponseException;
27 import org.apache.http.client.ResponseHandler;
28 import org.apache.http.client.methods.HttpPost;
29 import org.apache.http.entity.AbstractHttpEntity;
30 import org.apache.jackrabbit.webdav.MultiStatusResponse;
31 import org.apache.jackrabbit.webdav.property.DefaultDavProperty;
32 import org.apache.jackrabbit.webdav.xml.Namespace;
33 import org.apache.log4j.Logger;
34
35 import javax.xml.stream.XMLStreamConstants;
36 import javax.xml.stream.XMLStreamException;
37 import javax.xml.stream.XMLStreamReader;
38 import java.io.ByteArrayInputStream;
39 import java.io.FilterInputStream;
40 import java.io.IOException;
41 import java.io.InputStream;
42 import java.io.OutputStream;
43 import java.util.ArrayList;
44 import java.util.List;
45
46 public abstract class ExchangeDavRequest extends HttpPost implements ResponseHandler<List<MultiStatusResponse>> {
47 protected static final Logger LOGGER = Logger.getLogger(ExchangeDavRequest.class);
48 private static final String XML_CONTENT_TYPE = "text/xml; charset=UTF-8";
49
50 private HttpResponse response;
51 private List<MultiStatusResponse> responses;
52
53
54
55
56
57
58 public ExchangeDavRequest(String path) {
59 super(path);
60 AbstractHttpEntity httpEntity = new AbstractHttpEntity() {
61 byte[] content;
62
63 @Override
64 public boolean isRepeatable() {
65 return true;
66 }
67
68 @Override
69 public long getContentLength() {
70 if (content == null) {
71 content = generateRequestContent();
72 }
73 return content.length;
74 }
75
76 @Override
77 public InputStream getContent() throws UnsupportedOperationException {
78 if (content == null) {
79 content = generateRequestContent();
80 }
81 return new ByteArrayInputStream(content);
82 }
83
84 @Override
85 public void writeTo(OutputStream outputStream) throws IOException {
86 if (content == null) {
87 content = generateRequestContent();
88 }
89 outputStream.write(content);
90 }
91
92 @Override
93 public boolean isStreaming() {
94 return false;
95 }
96 };
97
98 httpEntity.setContentType(XML_CONTENT_TYPE);
99 setEntity(httpEntity);
100 }
101
102
103
104
105
106
107 protected abstract byte[] generateRequestContent();
108
109 @Override
110 public List<MultiStatusResponse> handleResponse(HttpResponse response) {
111 this.response = response;
112 Header contentTypeHeader = response.getFirstHeader("Content-Type");
113 if (contentTypeHeader != null && "text/xml".equals(contentTypeHeader.getValue())) {
114 responses = new ArrayList<>();
115 XMLStreamReader reader;
116 try {
117 reader = XMLStreamUtil.createXMLStreamReader(new FilterInputStream(response.getEntity().getContent()) {
118 final byte[] lastbytes = new byte[3];
119
120 @Override
121 public int read(byte[] bytes, int off, int len) throws IOException {
122 int count = in.read(bytes, off, len);
123
124 for (int i = 0; i < count; i++) {
125 byte currentByte = bytes[off + i];
126 if ((lastbytes[0] == '<') && (currentByte >= '0' && currentByte <= '9')) {
127
128 bytes[off + i] = (byte) (currentByte + 49);
129 }
130 lastbytes[0] = lastbytes[1];
131 lastbytes[1] = lastbytes[2];
132 lastbytes[2] = currentByte;
133 }
134 return count;
135 }
136
137 });
138 while (reader.hasNext()) {
139 reader.next();
140 if (XMLStreamUtil.isStartTag(reader, "response")) {
141 handleResponse(reader);
142 }
143 }
144
145 } catch (IOException | XMLStreamException e) {
146 LOGGER.error("Error while parsing soap response: " + e, e);
147 }
148 }
149 return responses;
150 }
151
152 protected void handleResponse(XMLStreamReader reader) throws XMLStreamException {
153 MultiStatusResponse multiStatusResponse = null;
154 String href = null;
155 String responseStatus = "";
156 while (reader.hasNext() && !XMLStreamUtil.isEndTag(reader, "response")) {
157 reader.next();
158 if (XMLStreamUtil.isStartTag(reader)) {
159 String tagLocalName = reader.getLocalName();
160 if ("href".equals(tagLocalName)) {
161 href = reader.getElementText();
162 } else if ("status".equals(tagLocalName)) {
163 responseStatus = reader.getElementText();
164 } else if ("propstat".equals(tagLocalName)) {
165 if (multiStatusResponse == null) {
166 multiStatusResponse = new MultiStatusResponse(href, responseStatus);
167 }
168 handlePropstat(reader, multiStatusResponse);
169 }
170 }
171 }
172 if (multiStatusResponse != null) {
173 responses.add(multiStatusResponse);
174 }
175 }
176
177 protected void handlePropstat(XMLStreamReader reader, MultiStatusResponse multiStatusResponse) throws XMLStreamException {
178 int propstatStatus = 0;
179 while (reader.hasNext() && !XMLStreamUtil.isEndTag(reader, "propstat")) {
180 reader.next();
181 if (XMLStreamUtil.isStartTag(reader)) {
182 String tagLocalName = reader.getLocalName();
183 if ("status".equals(tagLocalName)) {
184 if ("HTTP/1.1 200 OK".equals(reader.getElementText())) {
185 propstatStatus = HttpStatus.SC_OK;
186 } else {
187 propstatStatus = 0;
188 }
189 } else if ("prop".equals(tagLocalName) && propstatStatus == HttpStatus.SC_OK) {
190 handleProperty(reader, multiStatusResponse);
191 }
192 }
193 }
194
195 }
196
197 protected void handleProperty(XMLStreamReader reader, MultiStatusResponse multiStatusResponse) throws XMLStreamException {
198 while (reader.hasNext() && !XMLStreamUtil.isEndTag(reader, "prop")) {
199 reader.next();
200 if (XMLStreamUtil.isStartTag(reader)) {
201 Namespace namespace = Namespace.getNamespace(reader.getNamespaceURI());
202 String tagLocalName = reader.getLocalName();
203 if (reader.getAttributeCount() > 0 && "mv.string".equals(reader.getAttributeValue(0))) {
204 handleMultiValuedProperty(reader, multiStatusResponse);
205 } else {
206 String tagContent = getTagContent(reader);
207 if (tagContent != null) {
208 multiStatusResponse.add(new DefaultDavProperty<>(tagLocalName, tagContent, namespace));
209 }
210 }
211 }
212 }
213 }
214
215 protected void handleMultiValuedProperty(XMLStreamReader reader, MultiStatusResponse multiStatusResponse) throws XMLStreamException {
216 String tagLocalName = reader.getLocalName();
217 Namespace namespace = Namespace.getNamespace(reader.getNamespaceURI());
218 ArrayList<String> values = new ArrayList<>();
219 while (reader.hasNext() && !XMLStreamUtil.isEndTag(reader, tagLocalName)) {
220 reader.next();
221 if (XMLStreamUtil.isStartTag(reader)) {
222 String tagContent = getTagContent(reader);
223 if (tagContent != null) {
224 values.add(tagContent);
225 }
226 }
227 }
228 multiStatusResponse.add(new DefaultDavProperty<>(tagLocalName, values, namespace));
229 }
230
231 protected String getTagContent(XMLStreamReader reader) throws XMLStreamException {
232 String value = null;
233 String tagLocalName = reader.getLocalName();
234 while (reader.hasNext() &&
235 !((reader.getEventType() == XMLStreamConstants.END_ELEMENT) && tagLocalName.equals(reader.getLocalName()))) {
236 reader.next();
237 if (reader.getEventType() == XMLStreamConstants.CHARACTERS) {
238 value = reader.getText();
239 }
240 }
241
242 if (!reader.hasNext()) {
243 throw new XMLStreamException("End element for " + tagLocalName + " not found");
244 }
245 return value;
246 }
247
248
249
250
251
252
253
254 public MultiStatusResponse[] getResponses() throws HttpResponseException {
255 if (responses == null) {
256
257 throw new HttpResponseException(response.getStatusLine().getStatusCode(),
258 response.getStatusLine().getReasonPhrase());
259 }
260 return responses.toArray(new MultiStatusResponse[0]);
261 }
262
263
264
265
266
267
268
269 public MultiStatusResponse getResponse() throws HttpResponseException {
270 if (responses == null || responses.size() != 1) {
271 throw new HttpResponseException(response.getStatusLine().getStatusCode(), response.getStatusLine().getReasonPhrase());
272 }
273 return responses.get(0);
274 }
275
276
277
278
279
280
281
282 public int getResponseStatusCode() throws HttpResponseException {
283 String responseDescription = getResponse().getResponseDescription();
284 if ("HTTP/1.1 201 Created".equals(responseDescription)) {
285 return HttpStatus.SC_CREATED;
286 } else {
287 return HttpStatus.SC_OK;
288 }
289 }
290
291 }