1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package davmail.exchange.graph;
21
22 import davmail.exception.DavMailException;
23 import davmail.exchange.VCalendar;
24 import davmail.exchange.VObject;
25 import davmail.exchange.VProperty;
26 import davmail.util.DateUtil;
27 import davmail.util.StringUtil;
28 import org.apache.log4j.Logger;
29 import org.codehaus.jettison.json.JSONArray;
30 import org.codehaus.jettison.json.JSONException;
31 import org.codehaus.jettison.json.JSONObject;
32
33 import java.text.ParseException;
34 import java.text.SimpleDateFormat;
35 import java.util.Date;
36 import java.util.HashMap;
37 import java.util.HashSet;
38 import java.util.Map;
39 import java.util.ResourceBundle;
40 import java.util.TimeZone;
41
42 import static davmail.util.DateUtil.CALDAV_DATE_TIME;
43 import static davmail.util.DateUtil.GRAPH_DATE_TIME;
44
45
46
47
48 public class GraphObject {
49 protected static final Logger LOGGER = Logger.getLogger(GraphObject.class);
50
51 protected final JSONObject jsonObject;
52 protected int statusCode;
53
54 public GraphObject() {
55 this.jsonObject = new JSONObject();
56 }
57
58 public GraphObject(JSONObject jsonObject) {
59 this.jsonObject = jsonObject;
60 }
61
62 public String optString(String key) {
63
64 return optString(GraphField.get(key));
65 }
66
67 public String optString(GraphField field) {
68 String key = field.getGraphId();
69 if (key == null) {
70 return null;
71 }
72 String value = null;
73
74 if ("keywords".equals(key) || "categories".equals(key)) {
75 JSONArray categoriesArray = jsonObject.optJSONArray("categories");
76 HashSet<String> keywords = new HashSet<>();
77
78 if (categoriesArray != null) {
79 for (int j = 0; j < categoriesArray.length(); j++) {
80 keywords.add(categoriesArray.optString(j));
81 }
82 value = StringUtil.join(keywords, ",");
83 }
84 } else if ("from".equals(key)) {
85 value = formatEmailAddress(jsonObject.optJSONObject(key));
86 } else if ("@odata.etag".equals(key)) {
87
88 String odataEtag = jsonObject.optString("@odata.etag");
89 if (odataEtag != null && odataEtag.startsWith("W/\"") && odataEtag.endsWith("\"")) {
90 value = odataEtag.substring(3, odataEtag.length() - 1);
91 }
92 } else if (!field.isExtended()) {
93
94 value = jsonObject.optString(key, null);
95 } else {
96 JSONArray singleValueExtendedProperties = jsonObject.optJSONArray("singleValueExtendedProperties");
97 if (singleValueExtendedProperties != null) {
98
99 for (int i = 0; i < singleValueExtendedProperties.length(); i++) {
100 JSONObject singleValueObject = singleValueExtendedProperties.optJSONObject(i);
101 if (singleValueObject != null && key.equals(singleValueObject.optString("id"))) {
102 value = singleValueObject.optString("value");
103 }
104 }
105 }
106 }
107 if (field.isDate()) {
108 try {
109 value = GraphExchangeSession.convertDateFromExchange(value);
110 } catch (DavMailException e) {
111 LOGGER.warn("Invalid date " + value + " on field " + key);
112 }
113 }
114 return value;
115 }
116
117 protected String formatEmailAddress(JSONObject jsonObject) {
118 String value = null;
119 if (jsonObject != null) {
120 JSONObject jsonEmailAddress = jsonObject.optJSONObject("emailAddress");
121 if (jsonEmailAddress != null) {
122 value = jsonEmailAddress.optString("name", "") + " <" + jsonEmailAddress.optString("address", "") + ">";
123 }
124 }
125 return value;
126 }
127
128 public JSONArray optJSONArray(String key) {
129 return jsonObject.optJSONArray(key);
130 }
131
132
133
134
135
136
137
138
139 public GraphObject put(String alias, String value) throws JSONException {
140 GraphField field = GraphField.get(alias);
141 String key = field.getGraphId();
142
143 if (field.isExtended()) {
144
145 if (field.isNumber() && value == null) {
146 value = "0";
147 }
148 if (field.isBoolean() && value == null) {
149 value = "false";
150 }
151 getSingleValueExtendedProperties().put(new JSONObject().put("id", key).put("value", value == null ? JSONObject.NULL : value));
152 } else {
153 jsonObject.put(key, value == null ? JSONObject.NULL : value);
154 }
155 return this;
156 }
157
158
159
160
161
162
163
164
165 public GraphObject put(String alias, boolean value) throws JSONException {
166 GraphField field = GraphField.get(alias);
167 String key = field.getGraphId();
168
169 if (field.isExtended()) {
170 getSingleValueExtendedProperties().put(new JSONObject().put("id", key).put("value", value));
171 } else {
172 jsonObject.put(key, value);
173 }
174 return this;
175 }
176
177 public GraphObject put(String key, JSONArray value) throws JSONException {
178 jsonObject.put(key, value);
179 return this;
180 }
181
182 public GraphObject put(String key, JSONObject value) throws JSONException {
183 jsonObject.put(key, value);
184 return this;
185 }
186
187
188
189
190
191
192 public void setCategories(String values) throws JSONException {
193 if (values != null) {
194 setCategories(values.split(","));
195 } else {
196 jsonObject.put("categories", new JSONArray());
197 }
198 }
199
200 public void setCategories(String[] values) throws JSONException {
201
202 JSONArray jsonValues = new JSONArray();
203 for (String singleValue : values) {
204 jsonValues.put(singleValue);
205 }
206 jsonObject.put("categories", jsonValues);
207 }
208
209
210
211
212
213
214
215
216 protected JSONArray getSingleValueExtendedProperties() throws JSONException {
217 JSONArray singleValueExtendedProperties = jsonObject.optJSONArray("singleValueExtendedProperties");
218 if (singleValueExtendedProperties == null) {
219 singleValueExtendedProperties = new JSONArray();
220 jsonObject.put("singleValueExtendedProperties", singleValueExtendedProperties);
221 }
222 return singleValueExtendedProperties;
223 }
224
225
226
227
228
229
230
231
232 public String getString(String key) throws JSONException {
233 String value = optString(key);
234 if (value == null) {
235 throw new JSONException("JSONObject[" + key + "] not found.");
236 }
237 return value;
238 }
239
240
241
242
243
244
245
246 public String optString(String section, String key) {
247 JSONObject sectionObject = jsonObject.optJSONObject(section);
248 if (sectionObject != null) {
249 return sectionObject.optString(key, null);
250 }
251 return null;
252 }
253
254 public boolean getBoolean(String key) throws JSONException {
255 return getBoolean(GraphField.get(key));
256 }
257
258 public boolean optBoolean(String key) {
259 return optBoolean(GraphField.get(key));
260 }
261
262 public boolean getBoolean(GraphField field) throws JSONException {
263 String key = field.getGraphId();
264 if (key == null) {
265 return false;
266 }
267 return jsonObject.getBoolean(key);
268 }
269
270 public boolean optBoolean(GraphField field) {
271 String key = field.getGraphId();
272 if (key == null) {
273 return false;
274 }
275 return jsonObject.optBoolean(key);
276 }
277
278 public JSONObject optJSONObject(String key) {
279 return jsonObject.optJSONObject(key);
280 }
281
282
283
284
285
286
287
288
289
290 public Date optDateTimeTimeZone(String key) throws DavMailException {
291 JSONObject sectionObject = jsonObject.optJSONObject(key);
292 if (sectionObject != null) {
293 String timeZone = sectionObject.optString("timeZone");
294 String dateTime = sectionObject.optString("dateTime");
295 if (timeZone != null && dateTime != null) {
296 try {
297 SimpleDateFormat parser = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSSSSS");
298 parser.setTimeZone(TimeZone.getTimeZone(convertTimezoneFromExchange(timeZone)));
299 return parser.parse(dateTime);
300 } catch (ParseException e) {
301 throw new DavMailException("EXCEPTION_INVALID_DATE", dateTime);
302 }
303 }
304 }
305 return null;
306 }
307
308
309
310
311
312
313 public VProperty getRecurrenceId() throws DavMailException {
314
315 String originalStartTimeZone = optString("originalStartTimeZone");
316
317 String originalStart = optString("originalStart");
318
319 if (originalStartTimeZone != null && originalStart != null && originalStart.length() >= 19) {
320 String convertedOriginalStart = originalStart;
321
322
323 SimpleDateFormat parser = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");
324 parser.setTimeZone(TimeZone.getTimeZone("UTC"));
325
326 String standardTimeZoneId = DateUtil.getStandardTimeZone(originalStartTimeZone);
327 SimpleDateFormat formatter;
328 if (standardTimeZoneId == null) {
329
330 formatter = new SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'");
331 formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
332 } else {
333
334 formatter = new SimpleDateFormat("yyyyMMdd'T'HHmmss");
335 formatter.setTimeZone(DateUtil.getTimeZone(standardTimeZoneId));
336 }
337 try {
338 convertedOriginalStart = formatter.format(parser.parse(originalStart));
339 } catch (ParseException e) {
340 LOGGER.warn("Unable to convert to original timezone: " + originalStart + ", " + originalStartTimeZone);
341 }
342
343
344 VProperty recurrenceId = new VProperty("RECURRENCE-ID", convertedOriginalStart);
345 if (standardTimeZoneId != null) {
346 recurrenceId.setParam("TZID", originalStartTimeZone);
347 }
348 return recurrenceId;
349 } else {
350 throw new DavMailException("LOG_MESSAGE", "Missing original start date and timezone");
351 }
352 }
353
354
355
356
357
358
359
360 public static String convertTimezoneFromExchange(String exchangeTimezone) {
361 ResourceBundle tzidsBundle = ResourceBundle.getBundle("stdtimezones");
362 if (tzidsBundle.containsKey(exchangeTimezone)) {
363 return tzidsBundle.getString(exchangeTimezone);
364 } else {
365 return exchangeTimezone;
366 }
367 }
368
369
370 public int optInt(String key) {
371 return jsonObject.optInt(key);
372 }
373
374 public String toString(int indentFactor) throws JSONException {
375 return jsonObject.toString(indentFactor);
376 }
377
378 protected static final Map<String, String> vTodoToTaskStatusMap = new HashMap<>();
379 protected static final Map<String, String> taskTovTodoStatusMap = new HashMap<>();
380
381 static {
382
383
384 taskTovTodoStatusMap.put("inProgress", "IN-PROCESS");
385 taskTovTodoStatusMap.put("completed", "COMPLETED");
386 taskTovTodoStatusMap.put("waitingOnOthers", "NEEDS-ACTION");
387 taskTovTodoStatusMap.put("deferred", "CANCELLED");
388
389
390 vTodoToTaskStatusMap.put("IN-PROCESS", "inProgress");
391 vTodoToTaskStatusMap.put("COMPLETED", "completed");
392 vTodoToTaskStatusMap.put("NEEDS-ACTION", "waitingOnOthers");
393 vTodoToTaskStatusMap.put("CANCELLED", "deferred");
394 }
395
396 public void setTaskStatusFromVTodo(VObject vEvent) throws JSONException {
397 String taskStatus = vTodoToTaskStatusMap.get(vEvent.getPropertyValue("STATUS"));
398 if (taskStatus == null) {
399 taskStatus = "notStarted";
400 }
401 put("status", taskStatus);
402 }
403
404 public String getVTodoStatusFromTask() {
405 return taskTovTodoStatusMap.get(optString("status"));
406 }
407
408 protected static final Map<String, String> importanceToPriorityMap = new HashMap<>();
409
410 static {
411 importanceToPriorityMap.put("high", "1");
412 importanceToPriorityMap.put("normal", "5");
413 importanceToPriorityMap.put("low", "9");
414 }
415
416 protected static final Map<String, String> priorityToImportanceMap = new HashMap<>();
417
418 static {
419
420 priorityToImportanceMap.put("0", "normal");
421
422 priorityToImportanceMap.put("1", "high");
423 priorityToImportanceMap.put("2", "high");
424 priorityToImportanceMap.put("3", "high");
425 priorityToImportanceMap.put("4", "normal");
426 priorityToImportanceMap.put("5", "normal");
427 priorityToImportanceMap.put("6", "normal");
428 priorityToImportanceMap.put("7", "low");
429 priorityToImportanceMap.put("8", "low");
430 priorityToImportanceMap.put("9", "low");
431 }
432
433 protected String getTaskPriority() {
434 String taskImportance = optString("importance");
435 String taskPriority = null;
436 if (taskImportance != null) {
437 taskPriority = importanceToPriorityMap.get(taskImportance);
438 }
439 return taskPriority;
440 }
441
442 public void setTaskImportanceFromVTodo(VObject vEvent) throws JSONException {
443 String taskImportance = priorityToImportanceMap.get(vEvent.getPropertyValue("PRIORITY"));
444 if (taskImportance == null) {
445 taskImportance = "normal";
446 }
447 put("importance", taskImportance);
448 }
449
450 }
451