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.ews.ExtendedFieldURI;
24  import davmail.exchange.ews.FieldURI;
25  import davmail.exchange.ews.IndexedFieldURI;
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 timeZone;
68  
69      Set<FieldURI> expandFields;
70  
71      String accessToken;
72  
73      JSONObject jsonBody = null;
74  
75      byte[] mimeContent;
76      private String objectId;
77  
78      /**
79       * Set property in Json body.
80       * @param name property name
81       * @param value property value
82       * @throws JSONException on error
83       */
84      public GraphRequestBuilder setProperty(String name, String value) throws JSONException {
85          if (jsonBody == null) {
86              jsonBody = new JSONObject();
87          }
88          jsonBody.put(name, value);
89          return this;
90      }
91  
92      /**
93       * Replace json body;
94       * @return this
95       */
96      public GraphRequestBuilder setJsonBody(JSONObject jsonBody) {
97          this.jsonBody = jsonBody;
98          return this;
99      }
100 
101     /**
102      * Set epxand fields (returning attributes).
103      * @param expandFields set of fields to return
104      * @return this
105      */
106     public GraphRequestBuilder setExpandFields(Set<FieldURI> expandFields) {
107         this.expandFields = expandFields;
108         return this;
109     }
110 
111     public GraphRequestBuilder setObjectType(String objectType) {
112         this.objectType = objectType;
113         return this;
114     }
115 
116     public GraphRequestBuilder setChildType(String childType) {
117         this.childType = childType;
118         return this;
119     }
120 
121     public GraphRequestBuilder setChildId(String childId) {
122         this.childId = childId;
123         return this;
124     }
125 
126     public GraphRequestBuilder setChildSuffix(String childSuffix) {
127         this.childSuffix = childSuffix;
128         return this;
129     }
130 
131     public GraphRequestBuilder setFilter(String filter) {
132         this.filter = filter;
133         return this;
134     }
135 
136     public GraphRequestBuilder setTimezone(String timeZone) {
137         this.timeZone = timeZone;
138         return this;
139     }
140 
141     public GraphRequestBuilder setAccessToken(String accessToken) {
142         this.accessToken = accessToken;
143         return this;
144     }
145 
146     public GraphRequestBuilder setMethod(String method) {
147         this.method = method;
148         return this;
149     }
150 
151     public GraphRequestBuilder setContentType(String contentType) {
152         this.contentType = contentType;
153         return this;
154     }
155 
156     public GraphRequestBuilder setMimeContent(byte[] mimeContent) {
157         this.mimeContent = mimeContent;
158         return this;
159     }
160 
161     public GraphRequestBuilder setMailbox(String mailbox) {
162         this.mailbox = mailbox;
163         return this;
164     }
165 
166     public GraphRequestBuilder setObjectId(String objectId) {
167         this.objectId = objectId;
168         return this;
169     }
170 
171     public GraphRequestBuilder setSelect(String select) {
172         this.select = select;
173         return this;
174     }
175 
176     /**
177      * Build request path based on version, username, object type and object id.
178      * @return request path
179      */
180     protected String buildPath() {
181         StringBuilder buffer = new StringBuilder();
182         buffer.append("/").append(version);
183         if (mailbox != null) {
184             buffer.append("/users/").append(mailbox);
185         } else {
186             buffer.append("/me");
187         }
188         if (objectType != null) {
189             buffer.append("/").append(objectType);
190         }
191         if (objectId != null) {
192             buffer.append("/").append(objectId);
193         }
194         if (childType != null) {
195             buffer.append("/").append(childType);
196         }
197         if (childId != null) {
198             buffer.append("/").append(childId);
199         }
200         if (childSuffix != null) {
201             buffer.append("/").append(childSuffix);
202         }
203 
204         return buffer.toString();
205     }
206 
207     /**
208      * Compute expand parameters from properties.
209      * @return $expand value
210      */
211     private String buildExpand() {
212         ArrayList<String> singleValueProperties = new ArrayList<>();
213         ArrayList<String> multiValueProperties = new ArrayList<>();
214         for (FieldURI fieldURI : expandFields) {
215             if (fieldURI.isMultiValued()) {
216                 multiValueProperties.add(fieldURI.getGraphId());
217             } else if (fieldURI instanceof ExtendedFieldURI) {
218                 String graphId = fieldURI.getGraphId();
219                 // only expanded properties need a singleValueProperties entry
220                 if (graphId.contains(" ")) {
221                     singleValueProperties.add(fieldURI.getGraphId());
222                 }
223             } else if (fieldURI instanceof IndexedFieldURI) {
224                 // TODO never happens
225                 multiValueProperties.add(fieldURI.getGraphId());
226             }
227         }
228         StringBuilder expand = new StringBuilder();
229         if (!singleValueProperties.isEmpty()) {
230             expand.append("singleValueExtendedProperties($filter=");
231             appendExpandProperties(expand, singleValueProperties);
232             expand.append(")");
233         }
234         if (!multiValueProperties.isEmpty()) {
235             if (!singleValueProperties.isEmpty()) {
236                 expand.append(",");
237             }
238             expand.append("multiValueExtendedProperties($filter=");
239             appendExpandProperties(expand, multiValueProperties);
240             expand.append(")");
241         }
242         if (LOGGER.isDebugEnabled()) {
243             LOGGER.debug("Expand: " + expand);
244         }
245         return expand.toString();
246     }
247 
248     protected void appendExpandProperties(StringBuilder buffer, List<String> properties) {
249         boolean first = true;
250         for (String id : properties) {
251             if (first) {
252                 first = false;
253             } else {
254                 buffer.append(" or ");
255             }
256             buffer.append("id eq '").append(id).append("'");
257         }
258     }
259 
260 
261     /**
262      * Build http request.
263      * @return Http request
264      * @throws IOException on error
265      */
266     public HttpRequestBase build() throws IOException {
267         try {
268             URIBuilder uriBuilder = new URIBuilder(baseUrl).setPath(buildPath());
269             if (select != null) {
270                 uriBuilder.addParameter("$select", select);
271             }
272 
273             if (expandFields != null) {
274                 uriBuilder.addParameter("$expand", buildExpand());
275             }
276 
277             if (filter != null) {
278                 uriBuilder.addParameter("$filter", filter);
279             }
280 
281             HttpRequestBase httpRequest;
282             if ("POST".equals(method)) {
283                 httpRequest = new HttpPost(uriBuilder.build());
284                 if (mimeContent != null) {
285                     ((HttpPost) httpRequest).setEntity(new ByteArrayEntity(mimeContent));
286                 } else if (jsonBody != null) {
287                     ((HttpPost) httpRequest).setEntity(new ByteArrayEntity(IOUtil.convertToBytes(jsonBody)));
288                 }
289             } else if (HttpPut.METHOD_NAME.equals(method)) {
290                 // contact picture
291                 httpRequest = new HttpPut(uriBuilder.build());
292                 if (mimeContent != null) {
293                     ((HttpPut) httpRequest).setEntity(new ByteArrayEntity(mimeContent));
294                 }
295             } else if (HttpPatch.METHOD_NAME.equals(method)) {
296                 httpRequest = new HttpPatch(uriBuilder.build());
297                 if (jsonBody != null) {
298                     ((HttpPatch) httpRequest).setEntity(new ByteArrayEntity(IOUtil.convertToBytes(jsonBody)));
299                 }
300             } else if ("DELETE".equals(method)) {
301                 httpRequest = new HttpDelete(uriBuilder.build());
302             } else {
303                 // default to GET request
304                 httpRequest = new HttpGet(uriBuilder.build());
305             }
306             httpRequest.setHeader("Content-Type", contentType);
307             httpRequest.setHeader("Authorization", "Bearer " + accessToken);
308 
309             if (timeZone != null) {
310                 httpRequest.setHeader("Prefer", "outlook.timezone=\""+timeZone+"\"");
311             }
312 
313             if (LOGGER.isDebugEnabled()) {
314                 LOGGER.debug(httpRequest.getMethod() + " " + httpRequest.getURI());
315                 if (jsonBody != null) {
316                     LOGGER.debug(jsonBody.toString());
317                 }
318             }
319 
320             return httpRequest;
321         } catch (URISyntaxException e) {
322             throw new IOException(e.getMessage(), e);
323         }
324     }
325 
326 }