1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package davmail.exchange.ews;
20
21 import davmail.BundleMessage;
22 import davmail.Settings;
23 import davmail.exchange.XMLStreamUtil;
24 import davmail.http.HttpClientAdapter;
25 import davmail.ui.tray.DavGatewayTray;
26 import davmail.util.StringUtil;
27 import org.apache.commons.codec.binary.Base64;
28 import org.apache.http.HttpResponse;
29 import org.apache.http.HttpStatus;
30 import org.apache.http.client.ResponseHandler;
31 import org.apache.http.client.methods.HttpPost;
32 import org.apache.http.entity.AbstractHttpEntity;
33 import org.apache.http.entity.ContentType;
34 import org.apache.log4j.Level;
35 import org.apache.log4j.Logger;
36 import org.codehaus.stax2.typed.TypedXMLStreamReader;
37
38 import javax.xml.stream.XMLStreamConstants;
39 import javax.xml.stream.XMLStreamException;
40 import javax.xml.stream.XMLStreamReader;
41 import java.io.ByteArrayInputStream;
42 import java.io.ByteArrayOutputStream;
43 import java.io.FilterInputStream;
44 import java.io.IOException;
45 import java.io.InputStream;
46 import java.io.OutputStream;
47 import java.io.OutputStreamWriter;
48 import java.io.Writer;
49 import java.net.URI;
50 import java.nio.charset.StandardCharsets;
51 import java.util.ArrayList;
52 import java.util.HashMap;
53 import java.util.HashSet;
54 import java.util.List;
55 import java.util.Set;
56 import java.util.zip.GZIPInputStream;
57
58
59
60
61 public abstract class EWSMethod extends HttpPost implements ResponseHandler<EWSMethod> {
62 protected static final String CONTENT_TYPE = ContentType.create("text/xml", StandardCharsets.UTF_8).toString();
63 protected static final Logger LOGGER = Logger.getLogger(EWSMethod.class);
64 protected static final int CHUNK_LENGTH = 131072;
65
66 protected FolderQueryTraversal traversal;
67 protected BaseShape baseShape;
68 protected boolean includeMimeContent;
69 protected FolderId folderId;
70 protected FolderId savedItemFolderId;
71 protected FolderId toFolderId;
72 protected FolderId parentFolderId;
73 protected ItemId itemId;
74 protected List<ItemId> itemIds;
75 protected ItemId parentItemId;
76 protected Set<FieldURI> additionalProperties;
77 protected Disposal deleteType;
78 protected Set<AttributeOption> methodOptions;
79 protected ElementOption unresolvedEntry;
80
81
82 protected int maxCount;
83 protected int offset;
84
85 protected boolean includesLastItemInRange;
86
87 protected List<FieldUpdate> updates;
88
89 protected FileAttachment attachment;
90
91 protected String attachmentId;
92
93 protected final String itemType;
94 protected final String methodName;
95 protected final String responseCollectionName;
96
97 protected List<Item> responseItems;
98 protected String errorDetail;
99 protected String errorDescription;
100 protected String errorValue;
101 protected long backOffMilliseconds;
102 protected Item item;
103
104 protected SearchExpression searchExpression;
105 protected FieldOrder fieldOrder;
106
107 protected String serverVersion;
108 protected String timezoneContext;
109 private HttpResponse response;
110
111
112
113
114
115
116
117 public EWSMethod(String itemType, String methodName) {
118 this(itemType, methodName, itemType + 's');
119 }
120
121
122
123
124
125
126
127
128 public EWSMethod(String itemType, String methodName, String responseCollectionName) {
129 super(URI.create("/ews/exchange.asmx"));
130 this.itemType = itemType;
131 this.methodName = methodName;
132 this.responseCollectionName = responseCollectionName;
133 if (Settings.getBooleanProperty("davmail.acceptEncodingGzip", true) &&
134 !Level.DEBUG.toString().equals(Settings.getProperty("log4j.logger.httpclient.wire"))) {
135 setHeader("Accept-Encoding", "gzip");
136 }
137
138 AbstractHttpEntity httpEntity = new AbstractHttpEntity() {
139 byte[] content;
140
141 @Override
142 public boolean isRepeatable() {
143 return true;
144 }
145
146 @Override
147 public long getContentLength() {
148 if (content == null) {
149 content = generateSoapEnvelope();
150 }
151 return content.length;
152 }
153
154 @Override
155 public InputStream getContent() throws UnsupportedOperationException {
156 if (content == null) {
157 content = generateSoapEnvelope();
158 }
159 return new ByteArrayInputStream(content);
160 }
161
162 @Override
163 public void writeTo(OutputStream outputStream) throws IOException {
164 boolean firstPass = content == null;
165 if (content == null) {
166 content = generateSoapEnvelope();
167 }
168 if (content.length < CHUNK_LENGTH) {
169 outputStream.write(content);
170 } else {
171 int i = 0;
172 while (i < content.length) {
173 int length = CHUNK_LENGTH;
174 if (i + CHUNK_LENGTH > content.length) {
175 length = content.length - i;
176 }
177 outputStream.write(content, i, length);
178 if (!firstPass) {
179 DavGatewayTray.debug(new BundleMessage("LOG_UPLOAD_PROGRESS", String.valueOf((i + length) / 1024), (i + length) * 100 / content.length));
180 DavGatewayTray.switchIcon();
181 }
182 i += CHUNK_LENGTH;
183 }
184 }
185 }
186
187 @Override
188 public boolean isStreaming() {
189 return false;
190 }
191 };
192
193 httpEntity.setContentType(CONTENT_TYPE);
194 setEntity(httpEntity);
195 }
196
197 protected void addAdditionalProperty(FieldURI additionalProperty) {
198 if (additionalProperties == null) {
199 additionalProperties = new HashSet<>();
200 }
201 additionalProperties.add(additionalProperty);
202 }
203
204 protected void addMethodOption(AttributeOption attributeOption) {
205 if (methodOptions == null) {
206 methodOptions = new HashSet<>();
207 }
208 methodOptions.add(attributeOption);
209 }
210
211 protected void setSearchExpression(SearchExpression searchExpression) {
212 this.searchExpression = searchExpression;
213 }
214
215 protected void setFieldOrder(FieldOrder fieldOrder) {
216 this.fieldOrder = fieldOrder;
217 }
218
219 protected void writeShape(Writer writer) throws IOException {
220 if (baseShape != null) {
221 writer.write("<m:");
222 writer.write(itemType);
223 writer.write("Shape>");
224 baseShape.write(writer);
225 if (includeMimeContent) {
226 writer.write("<t:IncludeMimeContent>true</t:IncludeMimeContent>");
227 }
228 if (additionalProperties != null) {
229 writer.write("<t:AdditionalProperties>");
230 StringBuilder buffer = new StringBuilder();
231 for (FieldURI fieldURI : additionalProperties) {
232 fieldURI.appendTo(buffer);
233 }
234 writer.write(buffer.toString());
235 writer.write("</t:AdditionalProperties>");
236 }
237 writer.write("</m:");
238 writer.write(itemType);
239 writer.write("Shape>");
240 }
241 }
242
243 protected void writeItemId(Writer writer) throws IOException {
244 if (itemId != null || itemIds != null) {
245 if (updates == null) {
246 writer.write("<m:ItemIds>");
247 }
248 if (itemId != null) {
249 itemId.write(writer);
250 }
251 if (itemIds != null) {
252 for (ItemId localItemId : itemIds) {
253 localItemId.write(writer);
254 }
255 }
256 if (updates == null) {
257 writer.write("</m:ItemIds>");
258 }
259 }
260 }
261
262 protected void writeParentItemId(Writer writer) throws IOException {
263 if (parentItemId != null) {
264 writer.write("<m:ParentItemId Id=\"");
265 writer.write(parentItemId.id);
266 if (parentItemId.changeKey != null) {
267 writer.write("\" ChangeKey=\"");
268 writer.write(parentItemId.changeKey);
269 }
270 writer.write("\"/>");
271 }
272 }
273
274 protected void writeFolderId(Writer writer) throws IOException {
275 if (folderId != null) {
276 if (updates == null) {
277 writer.write("<m:FolderIds>");
278 }
279 folderId.write(writer);
280 if (updates == null) {
281 writer.write("</m:FolderIds>");
282 }
283 }
284 }
285
286 protected void writeSavedItemFolderId(Writer writer) throws IOException {
287 if (savedItemFolderId != null) {
288 writer.write("<m:SavedItemFolderId>");
289 savedItemFolderId.write(writer);
290 writer.write("</m:SavedItemFolderId>");
291 }
292 }
293
294 protected void writeToFolderId(Writer writer) throws IOException {
295 if (toFolderId != null) {
296 writer.write("<m:ToFolderId>");
297 toFolderId.write(writer);
298 writer.write("</m:ToFolderId>");
299 }
300 }
301
302 protected void writeParentFolderId(Writer writer) throws IOException {
303 if (parentFolderId != null) {
304 writer.write("<m:ParentFolderId");
305 if (item == null) {
306 writer.write("s");
307 }
308 writer.write(">");
309 parentFolderId.write(writer);
310 writer.write("</m:ParentFolderId");
311 if (item == null) {
312 writer.write("s");
313 }
314 writer.write(">");
315 }
316 }
317
318 protected void writeItem(Writer writer) throws IOException {
319 if (item != null) {
320 writer.write("<m:");
321 writer.write(itemType);
322 writer.write("s>");
323 item.write(writer);
324 writer.write("</m:");
325 writer.write(itemType);
326 writer.write("s>");
327 }
328 }
329
330 protected void writeRestriction(Writer writer) throws IOException {
331 if (searchExpression != null) {
332 writer.write("<m:Restriction>");
333 StringBuilder buffer = new StringBuilder();
334 searchExpression.appendTo(buffer);
335 writer.write(buffer.toString());
336 writer.write("</m:Restriction>");
337 }
338 }
339
340 protected void writeSortOrder(Writer writer) throws IOException {
341 if (fieldOrder != null) {
342 writer.write("<m:SortOrder>");
343 StringBuilder buffer = new StringBuilder();
344 fieldOrder.appendTo(buffer);
345 writer.write(buffer.toString());
346 writer.write("</m:SortOrder>");
347 }
348 }
349
350 protected void startChanges(Writer writer) throws IOException {
351
352 if (updates != null) {
353 writer.write("<m:");
354 writer.write(itemType);
355 writer.write("Changes>");
356 writer.write("<t:");
357 writer.write(itemType);
358 writer.write("Change>");
359 }
360 }
361
362 protected void writeUpdates(Writer writer) throws IOException {
363 if (updates != null) {
364 writer.write("<t:Updates>");
365 for (FieldUpdate fieldUpdate : updates) {
366 fieldUpdate.write(itemType, writer);
367 }
368 writer.write("</t:Updates>");
369 }
370 }
371
372 protected void writeUnresolvedEntry(Writer writer) throws IOException {
373 if (unresolvedEntry != null) {
374 unresolvedEntry.write(writer);
375 }
376 }
377
378 protected void endChanges(Writer writer) throws IOException {
379
380 if (updates != null) {
381 writer.write("</t:");
382 writer.write(itemType);
383 writer.write("Change>");
384 writer.write("</m:");
385 writer.write(itemType);
386 writer.write("Changes>");
387 }
388 }
389
390 protected byte[] generateSoapEnvelope() {
391 ByteArrayOutputStream baos = new ByteArrayOutputStream();
392 try {
393 OutputStreamWriter writer = new OutputStreamWriter(baos, StandardCharsets.UTF_8);
394 writer.write("<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" " +
395 "xmlns:t=\"http://schemas.microsoft.com/exchange/services/2006/types\" " +
396 "xmlns:m=\"http://schemas.microsoft.com/exchange/services/2006/messages\">");
397 writer.write("<soap:Header>");
398 if (serverVersion != null) {
399 writer.write("<t:RequestServerVersion Version=\"");
400 writer.write(serverVersion);
401 writer.write("\"/>");
402 }
403 if (timezoneContext != null) {
404 writer.write("<t:TimeZoneContext><t:TimeZoneDefinition Id=\"");
405 writer.write(timezoneContext);
406 writer.write("\"/></t:TimeZoneContext>");
407 }
408 writer.write("</soap:Header>");
409
410 writer.write("<soap:Body>");
411 writer.write("<m:");
412 writer.write(methodName);
413 if (traversal != null) {
414 traversal.write(writer);
415 }
416 if (deleteType != null) {
417 deleteType.write(writer);
418 }
419 if (methodOptions != null) {
420 for (AttributeOption attributeOption : methodOptions) {
421 attributeOption.write(writer);
422 }
423 }
424 writer.write(">");
425 writeSoapBody(writer);
426 writer.write("</m:");
427 writer.write(methodName);
428 writer.write(">");
429 writer.write("</soap:Body>" +
430 "</soap:Envelope>");
431 writer.flush();
432 } catch (IOException e) {
433 throw new RuntimeException(e);
434 }
435 return baos.toByteArray();
436 }
437
438 protected void writeSoapBody(Writer writer) throws IOException {
439 startChanges(writer);
440 writeShape(writer);
441 writeIndexedPageView(writer);
442 writeRestriction(writer);
443 writeSortOrder(writer);
444 writeParentFolderId(writer);
445 writeToFolderId(writer);
446 writeItemId(writer);
447 writeParentItemId(writer);
448 writeAttachments(writer);
449 writeAttachmentId(writer);
450 writeFolderId(writer);
451 writeSavedItemFolderId(writer);
452 writeItem(writer);
453 writeUpdates(writer);
454 writeUnresolvedEntry(writer);
455 endChanges(writer);
456 }
457
458
459 protected void writeIndexedPageView(Writer writer) throws IOException {
460 if (maxCount > 0) {
461 writer.write("<m:IndexedPage" + itemType + "View MaxEntriesReturned=\"");
462 writer.write(String.valueOf(maxCount));
463 writer.write("\" Offset=\"");
464 writer.write(String.valueOf(offset));
465 writer.write("\" BasePoint=\"Beginning\"/>");
466
467 }
468 }
469
470 protected void writeAttachmentId(Writer writer) throws IOException {
471 if (attachmentId != null) {
472 if ("CreateAttachment".equals(methodName)) {
473 writer.write("<m:AttachmentShape>");
474 writer.write("<t:IncludeMimeContent>true</t:IncludeMimeContent>");
475 writer.write("</m:AttachmentShape>");
476 }
477 writer.write("<m:AttachmentIds>");
478 writer.write("<t:AttachmentId Id=\"");
479 writer.write(attachmentId);
480 writer.write("\"/>");
481 writer.write("</m:AttachmentIds>");
482 }
483 }
484
485 protected void writeAttachments(Writer writer) throws IOException {
486 if (attachment != null) {
487 writer.write("<m:Attachments>");
488 attachment.write(writer);
489 writer.write("</m:Attachments>");
490 }
491 }
492
493
494
495
496
497
498 public String getServerVersion() {
499 return serverVersion;
500 }
501
502
503
504
505
506
507 public void setServerVersion(String serverVersion) {
508 this.serverVersion = serverVersion;
509 }
510
511
512
513
514
515
516 public void setTimezoneContext(String timezoneContext) {
517 this.timezoneContext = timezoneContext;
518 }
519
520
521
522
523 public static class Attendee {
524
525
526
527 public String role;
528
529
530
531 public String email;
532
533
534
535 public String partstat;
536
537
538
539 public String name;
540 }
541
542
543
544
545 public static class Occurrence {
546
547
548
549 public String originalStart;
550
551
552
553
554 public ItemId itemId;
555 }
556
557
558
559
560 public static class Item extends HashMap<String, String> {
561
562
563
564 public String type;
565 protected byte[] mimeContent;
566 protected List<FieldUpdate> fieldUpdates;
567 protected List<FileAttachment> attachments;
568 protected List<Attendee> attendees;
569 protected final List<String> fieldNames = new ArrayList<>();
570 protected List<Occurrence> occurrences;
571 protected List<String> members;
572 protected ItemId referenceItemId;
573
574 @Override
575 public String toString() {
576 return "type: " + type + ' ' + super.toString();
577 }
578
579 @Override
580 public String put(String key, String value) {
581 if (value != null) {
582 if (get(key) == null) {
583 fieldNames.add(key);
584 }
585 return super.put(key, value);
586 } else {
587 return null;
588 }
589 }
590
591
592
593
594
595
596
597 public void write(Writer writer) throws IOException {
598 writer.write("<t:");
599 writer.write(type);
600 writer.write(">");
601 if (mimeContent != null) {
602 writer.write("<t:MimeContent>");
603 for (byte c : mimeContent) {
604 writer.write(c);
605 }
606 writer.write("</t:MimeContent>");
607 }
608
609 for (String key : fieldNames) {
610 if ("MeetingTimeZone".equals(key)) {
611 writer.write("<t:MeetingTimeZone TimeZoneName=\"");
612 writer.write(StringUtil.xmlEncodeAttribute(get(key)));
613 writer.write("\"></t:MeetingTimeZone>");
614 } else if ("StartTimeZone".equals(key)) {
615 writer.write("<t:StartTimeZone Id=\"");
616 writer.write(StringUtil.xmlEncodeAttribute(get(key)));
617 writer.write("\"></t:StartTimeZone>");
618 } else if ("Body".equals(key)) {
619 writer.write("<t:Body BodyType=\"Text\">");
620 writer.write(StringUtil.xmlEncode(get(key)));
621 writer.write("</t:Body>");
622 } else {
623 writer.write("<t:");
624 writer.write(key);
625 writer.write(">");
626 writer.write(StringUtil.xmlEncode(get(key)));
627 writer.write("</t:");
628 writer.write(key);
629 writer.write(">");
630 }
631 }
632 if (fieldUpdates != null) {
633 for (FieldUpdate fieldUpdate : fieldUpdates) {
634 fieldUpdate.write(null, writer);
635 }
636 }
637 if (referenceItemId != null) {
638 referenceItemId.write(writer);
639 }
640 writer.write("</t:");
641 writer.write(type);
642 writer.write(">");
643 }
644
645
646
647
648
649
650 public void setFieldUpdates(List<FieldUpdate> fieldUpdates) {
651 this.fieldUpdates = fieldUpdates;
652 }
653
654
655
656
657
658
659
660 public int getInt(String key) {
661 int result = 0;
662 String value = get(key);
663 if (value != null && !value.isEmpty()) {
664 result = Integer.parseInt(value);
665 }
666 return result;
667 }
668
669
670
671
672
673
674
675 public long getLong(String key) {
676 long result = 0;
677 String value = get(key);
678 if (value != null && !value.isEmpty()) {
679 result = Long.parseLong(value);
680 }
681 return result;
682 }
683
684
685
686
687
688
689
690
691 public boolean getBoolean(String key) {
692 boolean result = false;
693 String value = get(key);
694 if (value != null && !value.isEmpty()) {
695 result = Boolean.parseBoolean(value);
696 }
697 return result;
698 }
699
700
701
702
703
704
705
706 public FileAttachment getAttachmentByName(String attachmentName) {
707 FileAttachment result = null;
708 if (attachments != null) {
709 for (FileAttachment fileAttachment : attachments) {
710 if (attachmentName.equals(fileAttachment.name)) {
711 result = fileAttachment;
712 break;
713 }
714 }
715 }
716 return result;
717 }
718
719
720
721
722
723
724 public List<Attendee> getAttendees() {
725 return attendees;
726 }
727
728
729
730
731
732
733 public void addAttendee(Attendee attendee) {
734 if (attendees == null) {
735 attendees = new ArrayList<>();
736 }
737 attendees.add(attendee);
738 }
739
740
741
742
743
744
745 public void addOccurrence(Occurrence occurrence) {
746 if (occurrences == null) {
747 occurrences = new ArrayList<>();
748 }
749 occurrences.add(occurrence);
750 }
751
752
753
754
755
756
757 public List<Occurrence> getOccurrences() {
758 return occurrences;
759 }
760
761
762
763
764
765
766 public void addMember(String member) {
767 if (members == null) {
768 members = new ArrayList<>();
769 }
770 members.add(member);
771 }
772
773
774
775
776
777
778 public List<String> getMembers() {
779 return members;
780 }
781 }
782
783
784
785
786
787
788 public void checkSuccess() throws EWSException {
789 if ("The server cannot service this request right now. Try again later.".equals(errorDetail)) {
790 throw new EWSThrottlingException(errorDetail);
791 }
792 if (errorDetail != null) {
793 if (!"ErrorAccessDenied".equals(errorDetail)
794 && !"ErrorMailRecipientNotFound".equals(errorDetail)
795 && !"ErrorItemNotFound".equals(errorDetail)
796 && !"ErrorCalendarOccurrenceIsDeletedFromRecurrence".equals(errorDetail)
797 ) {
798 throw new EWSException(errorDetail
799 + ' ' + ((errorDescription != null) ? errorDescription : "")
800 + ' ' + ((errorValue != null) ? errorValue : "")
801 + "\n request: " + new String(generateSoapEnvelope(), StandardCharsets.UTF_8));
802 }
803 }
804 if (getStatusCode() == HttpStatus.SC_BAD_REQUEST || getStatusCode() == HttpStatus.SC_INSUFFICIENT_STORAGE) {
805 throw new EWSException(response.getStatusLine().getReasonPhrase());
806 }
807 }
808
809 public int getStatusCode() {
810 if ("ErrorAccessDenied".equals(errorDetail)) {
811 return HttpStatus.SC_FORBIDDEN;
812 } else if ("ErrorItemNotFound".equals(errorDetail)) {
813 return HttpStatus.SC_NOT_FOUND;
814 } else {
815 return response.getStatusLine().getStatusCode();
816 }
817 }
818
819
820
821
822
823
824
825 public List<Item> getResponseItems() throws EWSException {
826 checkSuccess();
827 if (responseItems != null) {
828 return responseItems;
829 } else {
830 return new ArrayList<>();
831 }
832 }
833
834
835
836
837
838
839
840 public Item getResponseItem() throws EWSException {
841 checkSuccess();
842 if (responseItems != null && !responseItems.isEmpty()) {
843 return responseItems.get(0);
844 } else {
845 return null;
846 }
847 }
848
849
850
851
852
853
854
855 public byte[] getMimeContent() throws EWSException {
856 checkSuccess();
857 Item responseItem = getResponseItem();
858 if (responseItem != null) {
859 return responseItem.mimeContent;
860 } else {
861 return null;
862 }
863 }
864
865 protected String handleTag(XMLStreamReader reader, String localName) throws XMLStreamException {
866 StringBuilder result = null;
867 int event = reader.getEventType();
868 if (event == XMLStreamConstants.START_ELEMENT && localName.equals(reader.getLocalName())) {
869 result = new StringBuilder();
870 while (reader.hasNext() &&
871 !((event == XMLStreamConstants.END_ELEMENT && localName.equals(reader.getLocalName())))) {
872 event = reader.next();
873 if (event == XMLStreamConstants.CHARACTERS) {
874 result.append(reader.getText());
875 } else if ("MessageXml".equals(localName) && event == XMLStreamConstants.START_ELEMENT) {
876 String attributeValue = null;
877 for (int i = 0; i < reader.getAttributeCount(); i++) {
878 if (result.length() > 0) {
879 result.append(", ");
880 }
881 attributeValue = reader.getAttributeValue(i);
882 result.append(reader.getAttributeLocalName(i)).append(": ").append(reader.getAttributeValue(i));
883 }
884
885 if ("BackOffMilliseconds".equals(attributeValue)) {
886 try {
887 backOffMilliseconds = Long.parseLong(reader.getElementText());
888 } catch (NumberFormatException e) {
889 LOGGER.error("Unable to parse BackOffMilliseconds");
890 }
891 }
892 }
893 }
894 }
895 if (result != null && result.length() > 0) {
896 return result.toString();
897 } else {
898 return null;
899 }
900 }
901
902 protected void handleErrors(XMLStreamReader reader) throws XMLStreamException {
903 String result = handleTag(reader, "ResponseCode");
904
905 String messageText = handleTag(reader, "MessageText");
906 if (messageText != null) {
907 errorDescription = messageText;
908 }
909 String messageXml = handleTag(reader, "MessageXml");
910 if (messageXml != null) {
911
912 errorValue = messageXml;
913 }
914 if (errorDetail == null && result != null
915 && !"NoError".equals(result)
916 && !"ErrorNameResolutionMultipleResults".equals(result)
917 && !"ErrorNameResolutionNoResults".equals(result)
918 && !"ErrorFolderExists".equals(result)
919 ) {
920 errorDetail = result;
921 }
922 if (XMLStreamUtil.isStartTag(reader, "faultstring")) {
923 errorDetail = XMLStreamUtil.getElementText(reader);
924 }
925 }
926
927 protected Item handleItem(XMLStreamReader reader) throws XMLStreamException {
928 Item responseItem = new Item();
929 responseItem.type = reader.getLocalName();
930 while (reader.hasNext() && !XMLStreamUtil.isEndTag(reader, responseItem.type)) {
931 reader.next();
932 if (XMLStreamUtil.isStartTag(reader)) {
933 String tagLocalName = reader.getLocalName();
934 String value = null;
935 if ("ExtendedProperty".equals(tagLocalName)) {
936 addExtendedPropertyValue(reader, responseItem);
937 } else if ("Members".equals(tagLocalName)) {
938 handleMembers(reader, responseItem);
939 } else if (tagLocalName.endsWith("MimeContent")) {
940 handleMimeContent(reader, responseItem);
941 } else if ("Attachments".equals(tagLocalName)) {
942 responseItem.attachments = handleAttachments(reader);
943 } else if ("EmailAddresses".equals(tagLocalName)) {
944 handleEmailAddresses(reader, responseItem);
945 } else if ("RequiredAttendees".equals(tagLocalName) || "OptionalAttendees".equals(tagLocalName)) {
946 handleAttendees(reader, responseItem, tagLocalName);
947 } else if ("ModifiedOccurrences".equals(tagLocalName)) {
948 handleModifiedOccurrences(reader, responseItem);
949 } else {
950 if (tagLocalName.endsWith("Id")) {
951 value = getAttributeValue(reader, "Id");
952
953 responseItem.put("ChangeKey", getAttributeValue(reader, "ChangeKey"));
954 }
955 if (value == null) {
956 value = getTagContent(reader);
957 }
958 if (value != null) {
959 responseItem.put(tagLocalName, value);
960 }
961 }
962 }
963 }
964 return responseItem;
965 }
966
967 protected void handleEmailAddresses(XMLStreamReader reader, Item item) throws XMLStreamException {
968 while (reader.hasNext() && !(XMLStreamUtil.isEndTag(reader, "EmailAddresses"))) {
969 reader.next();
970 if (XMLStreamUtil.isStartTag(reader)) {
971 String tagLocalName = reader.getLocalName();
972 if ("Entry".equals(tagLocalName)) {
973 item.put(reader.getAttributeValue(null, "Key"), XMLStreamUtil.getElementText(reader));
974 }
975 }
976 }
977 }
978
979 protected void handleAttendees(XMLStreamReader reader, Item item, String attendeeType) throws XMLStreamException {
980 while (reader.hasNext() && !(XMLStreamUtil.isEndTag(reader, attendeeType))) {
981 reader.next();
982 if (XMLStreamUtil.isStartTag(reader)) {
983 String tagLocalName = reader.getLocalName();
984 if ("Attendee".equals(tagLocalName)) {
985 handleAttendee(reader, item, attendeeType);
986 }
987 }
988 }
989 }
990
991 protected void handleModifiedOccurrences(XMLStreamReader reader, Item item) throws XMLStreamException {
992 while (reader.hasNext() && !(XMLStreamUtil.isEndTag(reader, "ModifiedOccurrences"))) {
993 reader.next();
994 if (XMLStreamUtil.isStartTag(reader)) {
995 String tagLocalName = reader.getLocalName();
996 if ("Occurrence".equals(tagLocalName)) {
997 handleOccurrence(reader, item);
998 }
999 }
1000 }
1001 }
1002
1003 protected void handleOccurrence(XMLStreamReader reader, Item item) throws XMLStreamException {
1004 Occurrence occurrence = new Occurrence();
1005 while (reader.hasNext() && !(XMLStreamUtil.isEndTag(reader, "Occurrence"))) {
1006 reader.next();
1007 if (XMLStreamUtil.isStartTag(reader)) {
1008 String tagLocalName = reader.getLocalName();
1009 if ("ItemId".equals(tagLocalName)) {
1010 occurrence.itemId = new ItemId("ItemId", getAttributeValue(reader, "Id"), getAttributeValue(reader, "ChangeKey"));
1011 }
1012 if ("OriginalStart".equals(tagLocalName)) {
1013 occurrence.originalStart = XMLStreamUtil.getElementText(reader);
1014 }
1015 }
1016 }
1017 item.addOccurrence(occurrence);
1018 }
1019
1020 protected void handleMembers(XMLStreamReader reader, Item responseItem) throws XMLStreamException {
1021 while (reader.hasNext() && !XMLStreamUtil.isEndTag(reader, "Members")) {
1022 reader.next();
1023 if (XMLStreamUtil.isStartTag(reader)) {
1024 String tagLocalName = reader.getLocalName();
1025 if ("Member".equals(tagLocalName)) {
1026 handleMember(reader, responseItem);
1027 }
1028 }
1029 }
1030 }
1031
1032 protected void handleMember(XMLStreamReader reader, Item responseItem) throws XMLStreamException {
1033 String member = null;
1034 while (reader.hasNext() && !XMLStreamUtil.isEndTag(reader, "Member")) {
1035 reader.next();
1036 if (XMLStreamUtil.isStartTag(reader)) {
1037 String tagLocalName = reader.getLocalName();
1038 if ("EmailAddress".equals(tagLocalName) && member == null) {
1039 member = "mailto:" + XMLStreamUtil.getElementText(reader);
1040 }
1041 }
1042 }
1043 if (member != null) {
1044 responseItem.addMember(member);
1045 }
1046 }
1047
1048
1049
1050
1051
1052
1053
1054 public static String responseTypeToPartstat(String responseType) {
1055 if ("Accept".equals(responseType) || "Organizer".equals(responseType)) {
1056 return "ACCEPTED";
1057 } else if ("Tentative".equals(responseType)) {
1058 return "TENTATIVE";
1059 } else if ("Decline".equals(responseType)) {
1060 return "DECLINED";
1061 } else {
1062 return "NEEDS-ACTION";
1063 }
1064 }
1065
1066 protected void handleAttendee(XMLStreamReader reader, Item item, String attendeeType) throws XMLStreamException {
1067 Attendee attendee = new Attendee();
1068 if ("RequiredAttendees".equals(attendeeType)) {
1069 attendee.role = "REQ-PARTICIPANT";
1070 } else {
1071 attendee.role = "OPT-PARTICIPANT";
1072 }
1073 while (reader.hasNext() && !(XMLStreamUtil.isEndTag(reader, "Attendee"))) {
1074 reader.next();
1075 if (XMLStreamUtil.isStartTag(reader)) {
1076 String tagLocalName = reader.getLocalName();
1077 if ("EmailAddress".equals(tagLocalName)) {
1078 attendee.email = reader.getElementText();
1079 } else if ("Name".equals(tagLocalName)) {
1080 attendee.name = XMLStreamUtil.getElementText(reader);
1081 } else if ("ResponseType".equals(tagLocalName)) {
1082 String responseType = XMLStreamUtil.getElementText(reader);
1083 attendee.partstat = responseTypeToPartstat(responseType);
1084 }
1085 }
1086 }
1087 item.addAttendee(attendee);
1088 }
1089
1090 protected List<FileAttachment> handleAttachments(XMLStreamReader reader) throws XMLStreamException {
1091 List<FileAttachment> attachments = new ArrayList<>();
1092 while (reader.hasNext() && !(XMLStreamUtil.isEndTag(reader, "Attachments"))) {
1093 reader.next();
1094 if (XMLStreamUtil.isStartTag(reader)) {
1095 String tagLocalName = reader.getLocalName();
1096 if ("FileAttachment".equals(tagLocalName)) {
1097 attachments.add(handleFileAttachment(reader));
1098 }
1099 }
1100 }
1101 return attachments;
1102 }
1103
1104 protected FileAttachment handleFileAttachment(XMLStreamReader reader) throws XMLStreamException {
1105 FileAttachment fileAttachment = new FileAttachment();
1106 while (reader.hasNext() && !(XMLStreamUtil.isEndTag(reader, "FileAttachment"))) {
1107 reader.next();
1108 if (XMLStreamUtil.isStartTag(reader)) {
1109 String tagLocalName = reader.getLocalName();
1110 if ("AttachmentId".equals(tagLocalName)) {
1111 fileAttachment.attachmentId = getAttributeValue(reader, "Id");
1112 } else if ("Name".equals(tagLocalName)) {
1113 fileAttachment.name = getTagContent(reader);
1114 } else if ("ContentType".equals(tagLocalName)) {
1115 fileAttachment.contentType = getTagContent(reader);
1116 }
1117 }
1118 }
1119 return fileAttachment;
1120 }
1121
1122
1123 protected void handleMimeContent(XMLStreamReader reader, Item responseItem) throws XMLStreamException {
1124 if (reader instanceof TypedXMLStreamReader) {
1125
1126 responseItem.mimeContent = ((TypedXMLStreamReader) reader).getElementAsBinary();
1127 } else {
1128
1129 responseItem.mimeContent = Base64.decodeBase64(reader.getElementText().getBytes(StandardCharsets.US_ASCII));
1130 }
1131 }
1132
1133 protected void addExtendedPropertyValue(XMLStreamReader reader, Item item) throws XMLStreamException {
1134 String propertyTag = null;
1135 String propertyValue = null;
1136 while (reader.hasNext() && !(XMLStreamUtil.isEndTag(reader, "ExtendedProperty"))) {
1137 reader.next();
1138 if (XMLStreamUtil.isStartTag(reader)) {
1139 String tagLocalName = reader.getLocalName();
1140 if ("ExtendedFieldURI".equals(tagLocalName)) {
1141 propertyTag = getAttributeValue(reader, "PropertyTag");
1142
1143 if (propertyTag == null) {
1144 propertyTag = getAttributeValue(reader, "PropertyId");
1145 }
1146 if (propertyTag == null) {
1147 propertyTag = getAttributeValue(reader, "PropertyName");
1148 }
1149 } else if ("Value".equals(tagLocalName)) {
1150 propertyValue = XMLStreamUtil.getElementText(reader);
1151 } else if ("Values".equals(tagLocalName)) {
1152 StringBuilder buffer = new StringBuilder();
1153 while (reader.hasNext() && !(XMLStreamUtil.isEndTag(reader, "Values"))) {
1154 reader.next();
1155 if (XMLStreamUtil.isStartTag(reader)) {
1156
1157 if (buffer.length() > 0) {
1158 buffer.append(',');
1159 }
1160 String singleValue = XMLStreamUtil.getElementText(reader);
1161 if (singleValue != null) {
1162 buffer.append(singleValue);
1163 }
1164 }
1165 }
1166 propertyValue = buffer.toString();
1167 }
1168 }
1169 }
1170 if ((propertyTag != null) && (propertyValue != null)) {
1171 item.put(propertyTag, propertyValue);
1172 }
1173 }
1174
1175 protected String getTagContent(XMLStreamReader reader) throws XMLStreamException {
1176 String tagLocalName = reader.getLocalName();
1177 while (reader.hasNext() && !(reader.getEventType() == XMLStreamConstants.END_ELEMENT)) {
1178 reader.next();
1179 if (reader.getEventType() == XMLStreamConstants.CHARACTERS) {
1180 return reader.getText();
1181 }
1182 }
1183
1184 if (reader.hasNext()) {
1185 return null;
1186 } else {
1187 throw new XMLStreamException("End element for " + tagLocalName + " not found");
1188 }
1189 }
1190
1191 protected String getAttributeValue(XMLStreamReader reader, String attributeName) {
1192 for (int i = 0; i < reader.getAttributeCount(); i++) {
1193 if (attributeName.equals(reader.getAttributeLocalName(i))) {
1194 return reader.getAttributeValue(i);
1195 }
1196 }
1197 return null;
1198 }
1199
1200 @Override
1201 public EWSMethod handleResponse(HttpResponse response) {
1202 this.response = response;
1203 org.apache.http.Header contentTypeHeader = response.getFirstHeader("Content-Type");
1204 if (contentTypeHeader != null && "text/xml; charset=utf-8".equals(contentTypeHeader.getValue())) {
1205 try (
1206 InputStream inputStream = response.getEntity().getContent()
1207 ) {
1208 if (HttpClientAdapter.isGzipEncoded(response)) {
1209 processResponseStream(new GZIPInputStream(inputStream));
1210 } else {
1211 processResponseStream(inputStream);
1212 }
1213 } catch (IOException e) {
1214 LOGGER.error("Error while parsing soap response: " + e, e);
1215 }
1216 }
1217 return this;
1218 }
1219
1220 protected void processResponseStream(InputStream inputStream) {
1221 responseItems = new ArrayList<>();
1222 XMLStreamReader reader = null;
1223 try {
1224 inputStream = new FilterInputStream(inputStream) {
1225 int totalCount;
1226 int lastLogCount;
1227
1228 @Override
1229 public int read(byte[] buffer, int offset, int length) throws IOException {
1230 int count = super.read(buffer, offset, length);
1231 totalCount += count;
1232 if (totalCount - lastLogCount > 1024 * 128) {
1233 DavGatewayTray.debug(new BundleMessage("LOG_DOWNLOAD_PROGRESS", String.valueOf(totalCount / 1024), EWSMethod.this.getURI()));
1234 DavGatewayTray.switchIcon();
1235 lastLogCount = totalCount;
1236 }
1237
1238
1239
1240 return count;
1241 }
1242 };
1243 reader = XMLStreamUtil.createXMLStreamReader(inputStream);
1244 while (reader.hasNext()) {
1245 reader.next();
1246 handleErrors(reader);
1247 if (serverVersion == null && XMLStreamUtil.isStartTag(reader, "ServerVersionInfo")) {
1248 String majorVersion = getAttributeValue(reader, "MajorVersion");
1249 String minorVersion = getAttributeValue(reader, "MinorVersion");
1250 if ("15".equals(majorVersion)) {
1251 if ("0".equals(minorVersion)) {
1252 serverVersion = "Exchange2013";
1253 } else {
1254 serverVersion = "Exchange2013_SP1";
1255 }
1256 } else if ("14".equals(majorVersion)) {
1257 if ("0".equals(minorVersion)) {
1258 serverVersion = "Exchange2010";
1259 } else {
1260 serverVersion = "Exchange2010_SP1";
1261 }
1262 } else {
1263 serverVersion = "Exchange2007_SP1";
1264 }
1265 } else if (XMLStreamUtil.isStartTag(reader, "RootFolder")) {
1266 includesLastItemInRange = "true".equals(reader.getAttributeValue(null, "IncludesLastItemInRange"));
1267 } else if (XMLStreamUtil.isStartTag(reader, responseCollectionName)) {
1268 handleItems(reader);
1269 } else {
1270 handleCustom(reader);
1271 }
1272 }
1273 } catch (XMLStreamException e) {
1274 errorDetail = e.getMessage();
1275 LOGGER.error("Error while parsing soap response: " + e, e);
1276 if (reader != null) {
1277 try {
1278 String content = reader.getText();
1279 if (content != null && content.length() > 4096) {
1280 content = content.substring(0, 4096)+" ...";
1281 }
1282 LOGGER.debug("Current text: " + content);
1283 } catch (IllegalStateException ise) {
1284 LOGGER.error(e + " " + e.getMessage());
1285 }
1286 }
1287 }
1288 if (errorDetail != null) {
1289 LOGGER.debug(errorDetail);
1290 }
1291 }
1292
1293 @SuppressWarnings({"NoopMethodInAbstractClass"})
1294 protected void handleCustom(XMLStreamReader reader) throws XMLStreamException {
1295
1296 }
1297
1298 private void handleItems(XMLStreamReader reader) throws XMLStreamException {
1299 while (reader.hasNext() && !XMLStreamUtil.isEndTag(reader, responseCollectionName)) {
1300 reader.next();
1301 if (XMLStreamUtil.isStartTag(reader)) {
1302 responseItems.add(handleItem(reader));
1303 }
1304 }
1305
1306 }
1307
1308 }