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.exchange.graph;
21  
22  import davmail.Settings;
23  import davmail.exchange.ExchangeSession;
24  import davmail.exchange.ews.ExtendedFieldURI;
25  import davmail.exchange.ews.FieldURI;
26  import davmail.util.IOUtil;
27  import org.apache.http.client.methods.HttpDelete;
28  import org.apache.http.client.methods.HttpGet;
29  import org.apache.http.client.methods.HttpPatch;
30  import org.apache.http.client.methods.HttpPost;
31  import org.apache.http.client.methods.HttpPut;
32  import org.apache.http.client.methods.HttpRequestBase;
33  import org.apache.http.client.utils.URIBuilder;
34  import org.apache.http.entity.ByteArrayEntity;
35  import org.apache.log4j.Logger;
36  import org.codehaus.jettison.json.JSONException;
37  import org.codehaus.jettison.json.JSONObject;
38  
39  import java.io.IOException;
40  import java.net.URISyntaxException;
41  import java.util.ArrayList;
42  import java.util.List;
43  import java.util.Set;
44  
45  /**
46   * Build Microsoft graph request
47   */
48  public class GraphRequestBuilder {
49      protected static final Logger LOGGER = Logger.getLogger("davmail.exchange.graph.GraphRequestBuilder");
50  
51      String method = "POST";
52  
53      String contentType = "application/json";
54      String baseUrl = Settings.GRAPH_URL;
55      String version = "beta";
56      String mailbox;
57      String objectType;
58  
59      String childType;
60      String childId;
61      String childSuffix;
62  
63      String select;
64  
65      String filter;
66  
67      String startDateTime;
68      String endDateTime;
69  
70      String timeZone;
71  
72      Set<FieldURI> expandFields;
73  
74      String accessToken;
75  
76      JSONObject jsonBody = null;
77  
78      byte[] mimeContent;
79      private String objectId;
80  
81      /**
82       * Set property in the JSON body.
83       * @param name property name
84       * @param value property value
85       * @throws JSONException on error
86       */
87      public GraphRequestBuilder setProperty(String name, String value) throws JSONException {
88          if (jsonBody == null) {
89              jsonBody = new JSONObject();
90          }
91          jsonBody.put(name, value);
92          return this;
93      }
94  
95      /**
96       * Replace JSON body;
97       * @return this
98       */
99      public GraphRequestBuilder setJsonBody(JSONObject jsonBody) {
100         this.jsonBody = jsonBody;
101         return this;
102     }
103 
104     /**
105      * Set expand fields (returning attributes).
106      * @param expandFields set of fields to return
107      * @return this
108      */
109     public GraphRequestBuilder setExpandFields(Set<FieldURI> expandFields) {
110         this.expandFields = expandFields;
111         return this;
112     }
113 
114     public GraphRequestBuilder setObjectType(String objectType) {
115         this.objectType = objectType;
116         return this;
117     }
118 
119     public GraphRequestBuilder setChildType(String childType) {
120         this.childType = childType;
121         return this;
122     }
123 
124     public GraphRequestBuilder setChildId(String childId) {
125         this.childId = childId;
126         return this;
127     }
128 
129     public GraphRequestBuilder setChildSuffix(String childSuffix) {
130         this.childSuffix = childSuffix;
131         return this;
132     }
133 
134     public GraphRequestBuilder setFilter(String filter) {
135         this.filter = filter;
136         return this;
137     }
138 
139     public GraphRequestBuilder setFilter(ExchangeSession.Condition condition) {
140         if (condition != null && !condition.isEmpty()) {
141             StringBuilder buffer = new StringBuilder();
142             condition.appendTo(buffer);
143             LOGGER.debug("filter: " + filter);
144             this.filter = buffer.toString();
145         }
146         return this;
147     }
148 
149     public GraphRequestBuilder setStartDateTime(String startDateTime) {
150         this.startDateTime = startDateTime;
151         return this;
152     }
153 
154     public GraphRequestBuilder setEndDateTime(String endDateTime) {
155         this.endDateTime = endDateTime;
156         return this;
157     }
158 
159     public GraphRequestBuilder setTimezone(String timeZone) {
160         this.timeZone = timeZone;
161         return this;
162     }
163 
164     public GraphRequestBuilder setAccessToken(String accessToken) {
165         this.accessToken = accessToken;
166         return this;
167     }
168 
169     public GraphRequestBuilder setMethod(String method) {
170         this.method = method;
171         return this;
172     }
173 
174     public GraphRequestBuilder setContentType(String contentType) {
175         this.contentType = contentType;
176         return this;
177     }
178 
179     public GraphRequestBuilder setMimeContent(byte[] mimeContent) {
180         this.mimeContent = mimeContent;
181         return this;
182     }
183 
184     public GraphRequestBuilder setMailbox(String mailbox) {
185         this.mailbox = mailbox;
186         return this;
187     }
188 
189     public GraphRequestBuilder setObjectId(String objectId) {
190         this.objectId = objectId;
191         return this;
192     }
193 
194     public GraphRequestBuilder setSelect(String select) {
195         this.select = select;
196         return this;
197     }
198 
199     /**
200      * Build request path based on version, username, object type and object id.
201      * @return request path
202      */
203     protected String buildPath() {
204         StringBuilder buffer = new StringBuilder();
205         buffer.append("/").append(version);
206         if (mailbox != null) {
207             buffer.append("/users/").append(mailbox);
208         } else {
209             buffer.append("/me");
210         }
211         if (objectType != null) {
212             buffer.append("/").append(objectType);
213         }
214         if (objectId != null) {
215             buffer.append("/").append(objectId);
216         }
217         if (childType != null) {
218             buffer.append("/").append(childType);
219         }
220         if (childId != null) {
221             buffer.append("/").append(childId);
222         }
223         if (childSuffix != null) {
224             buffer.append("/").append(childSuffix);
225         }
226 
227         return buffer.toString();
228     }
229 
230     /**
231      * Compute expand parameters from properties.
232      * @return $expand value
233      */
234     private String buildExpand() {
235         ArrayList<String> singleValueProperties = new ArrayList<>();
236         ArrayList<String> multiValueProperties = new ArrayList<>();
237         for (FieldURI fieldURI : expandFields) {
238             if (fieldURI.isMultiValued()) {
239                 multiValueProperties.add(fieldURI.getGraphId());
240             } else if (fieldURI instanceof ExtendedFieldURI) {
241                 String graphId = fieldURI.getGraphId();
242                 // only expanded properties need a singleValueProperties entry
243                 // TODO refactor fields to only use fieldURI for expanded properties
244                 if (graphId.contains(" ")) {
245                     singleValueProperties.add(fieldURI.getGraphId());
246                 }
247             }
248         }
249         StringBuilder expand = new StringBuilder();
250         if (!singleValueProperties.isEmpty()) {
251             expand.append("singleValueExtendedProperties($filter=");
252             appendExpandProperties(expand, singleValueProperties);
253             expand.append(")");
254         }
255         if (!multiValueProperties.isEmpty()) {
256             if (!singleValueProperties.isEmpty()) {
257                 expand.append(",");
258             }
259             expand.append("multiValueExtendedProperties($filter=");
260             appendExpandProperties(expand, multiValueProperties);
261             expand.append(")");
262         }
263         return expand.toString();
264     }
265 
266     /**
267      * Build expand graph parameter to retrieve mapi properties.
268      * @param buffer expand buffer
269      * @param properties mapi properties list
270      */
271     protected void appendExpandProperties(StringBuilder buffer, List<String> properties) {
272         boolean first = true;
273         for (String id : properties) {
274             if (first) {
275                 first = false;
276             } else {
277                 buffer.append(" or ");
278             }
279             buffer.append("id eq '").append(id).append("'");
280         }
281     }
282 
283 
284     /**
285      * Build http request.
286      * @return Http request
287      * @throws IOException on error
288      */
289     public HttpRequestBase build() throws IOException {
290         try {
291             URIBuilder uriBuilder = new URIBuilder(baseUrl).setPath(buildPath());
292             if (select != null) {
293                 uriBuilder.addParameter("$select", select);
294             }
295 
296             if (expandFields != null) {
297                 uriBuilder.addParameter("$expand", buildExpand());
298             }
299 
300             if (filter != null) {
301                 uriBuilder.addParameter("$filter", filter);
302             }
303 
304             if (startDateTime != null) {
305                 uriBuilder.addParameter("startDateTime", startDateTime);
306             }
307 
308             if (endDateTime != null) {
309                 uriBuilder.addParameter("endDateTime", endDateTime);
310             }
311 
312             HttpRequestBase httpRequest;
313             if ("POST".equals(method)) {
314                 httpRequest = new HttpPost(uriBuilder.build());
315                 if (mimeContent != null) {
316                     ((HttpPost) httpRequest).setEntity(new ByteArrayEntity(mimeContent));
317                 } else if (jsonBody != null) {
318                     ((HttpPost) httpRequest).setEntity(new ByteArrayEntity(IOUtil.convertToBytes(jsonBody)));
319                 }
320             } else if (HttpPut.METHOD_NAME.equals(method)) {
321                 // contact picture
322                 httpRequest = new HttpPut(uriBuilder.build());
323                 if (mimeContent != null) {
324                     ((HttpPut) httpRequest).setEntity(new ByteArrayEntity(mimeContent));
325                 }
326             } else if (HttpPatch.METHOD_NAME.equals(method)) {
327                 httpRequest = new HttpPatch(uriBuilder.build());
328                 if (jsonBody != null) {
329                     ((HttpPatch) httpRequest).setEntity(new ByteArrayEntity(IOUtil.convertToBytes(jsonBody)));
330                 }
331             } else if ("DELETE".equals(method)) {
332                 httpRequest = new HttpDelete(uriBuilder.build());
333             } else {
334                 // default to GET request
335                 httpRequest = new HttpGet(uriBuilder.build());
336             }
337             httpRequest.setHeader("Content-Type", contentType);
338             httpRequest.setHeader("Authorization", "Bearer " + accessToken);
339 
340             if (timeZone != null) {
341                 httpRequest.setHeader("Prefer", "outlook.timezone=\"" + timeZone + "\"");
342             }
343 
344             if (LOGGER.isDebugEnabled()) {
345                 LOGGER.debug(httpRequest.getMethod() + " " + httpRequest.getURI());
346                 if (jsonBody != null) {
347                     LOGGER.debug(jsonBody.toString());
348                 }
349             }
350 
351             return httpRequest;
352         } catch (URISyntaxException e) {
353             throw new IOException(e.getMessage(), e);
354         }
355     }
356 
357 }