001/*
002 * Stallion Core: A Modern Web Framework
003 *
004 * Copyright (C) 2015 - 2016 Stallion Software LLC.
005 *
006 * This program is free software: you can redistribute it and/or modify it under the terms of the
007 * GNU General Public License as published by the Free Software Foundation, either version 2 of
008 * the License, or (at your option) any later version. This program is distributed in the hope that
009 * it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
010 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
011 * License for more details. You should have received a copy of the GNU General Public License
012 * along with this program.  If not, see <http://www.gnu.org/licenses/gpl-2.0.html>.
013 *
014 *
015 *
016 */
017
018package io.stallion.requests;
019
020import javax.persistence.Column;
021import javax.servlet.MultipartConfigElement;
022import javax.servlet.ServletException;
023import javax.servlet.http.Cookie;
024import javax.servlet.http.HttpServletRequest;
025import javax.servlet.http.Part;
026
027import com.fasterxml.jackson.core.type.TypeReference;
028import com.fasterxml.jackson.databind.ObjectMapper;
029import io.stallion.Context;
030import io.stallion.exceptions.ClientException;
031import io.stallion.plugins.javascript.Sandbox;
032import io.stallion.services.Log;
033import io.stallion.settings.Settings;
034import io.stallion.users.EmptyOrg;
035import io.stallion.users.EmptyUser;
036import io.stallion.users.IOrg;
037import io.stallion.users.IUser;
038import org.apache.commons.io.IOUtils;
039
040import org.apache.commons.lang3.StringUtils;
041import org.apache.http.NameValuePair;
042import org.apache.http.client.utils.URLEncodedUtils;
043import org.eclipse.jetty.server.Request;
044
045import java.io.BufferedReader;
046import java.io.IOException;
047import java.io.UnsupportedEncodingException;
048import java.net.URLDecoder;
049import java.nio.charset.Charset;
050import java.util.*;
051
052import static io.stallion.utils.Literals.*;
053
054public class StRequest implements IRequest {
055    private static final MultipartConfigElement MULTI_PART_CONFIG = new MultipartConfigElement(System.getProperty("java.io.tmpdir"));
056
057
058    private HttpServletRequest request;
059    private Request baseRequest;
060    private String path;
061    private String query = null;
062    private IUser user = new EmptyUser();
063    private Long valetUserId = 0L;
064    private String valetEmail = "";
065    private boolean scoped = false;
066    private Set<String> scopes;
067    private IOrg org = new EmptyOrg();
068    private String rawRequestBody = null;
069    private Map<String, Object> bodyMap;
070    private String content = null;
071    private Object bodyObject = null;
072    private Boolean isJsonRequest = null;
073    private Map<String, String> queryParams = null;
074    private Map<String, Object> items = map();
075    private SandboxedRequest sandboxedRequest;
076    private String scheme;
077
078
079    public StRequest() {
080
081    }
082
083    public StRequest(String path, Request baseRequest, HttpServletRequest request) {
084        this.setPath(path);
085        this.request = request;
086        this.baseRequest = baseRequest;
087        this.query = request.getQueryString();
088    }
089
090
091    @Override
092    public void setAsMultiPartRequest() {
093        if (request.getContentType() != null && request.getContentType().startsWith("multipart/form-data")) {
094            baseRequest.setAttribute(Request.__MULTIPART_CONFIG_ELEMENT, MULTI_PART_CONFIG);
095        }
096    }
097
098    public HttpServletRequest getHttpServletRequest() {
099        return this.request;
100    }
101
102
103
104    public Part getPart(String name) {
105        try {
106            return request.getPart(name);
107        } catch (IOException e) {
108            throw new RuntimeException(e);
109        } catch (ServletException e) {
110            throw new RuntimeException(e);
111        }
112    }
113
114    /**
115     * Gets the absolute url of the original request.
116     * Reconstructs the URL using the x-forwarded-host, if necessary,
117     * so this will work in the case of proxies.
118     * @return
119     */
120    @Override
121    public String requestUrl() {
122        String host = getHost();
123        String proto = getScheme();
124        String baseUrl = proto + "://" + host;
125        return baseUrl + this.getPath();
126    }
127
128
129
130    @Override
131    public String getScheme() {
132        if (empty(scheme) && this.request != null) {
133            if (!empty(getHeader("x-upstream-forwarded-proto"))) {
134                scheme = getHeader("x-upstream-forwarded-proto");
135            }
136            if (empty(scheme) && !empty(getHeader("x-forwarded-proto"))) {
137                scheme = getHeader("x-forwarded-proto");
138            }
139        }
140        if (empty(scheme)) {
141            String baseUrl = "";
142            if (Context.getSettings() != null ) {
143                baseUrl = Context.getSettings().getSiteUrl();
144            }
145            if (baseUrl != null) {
146                int i = baseUrl.indexOf("://");
147                if (i > -1) {
148                    scheme = baseUrl.substring(0, i);
149                }
150            }
151        }
152        if (empty(scheme) && this.request != null) {
153            scheme = this.request.getScheme();
154        }
155        if (empty(scheme)){
156            scheme = "http";
157        }
158        return scheme;
159    }
160
161    @Override
162    public String getHost() {
163        if (this.request == null) {
164            return "http://localhost" + this.getPath();
165        }
166        String host = this.request.getHeader("host");
167        if (!empty(getHeader("x-forwarded-host"))) {
168            host = getHeader("x-forwarded-host");
169        }
170        return host;
171    }
172
173    @Override
174    public String getQueryString() {
175        if (query == null) {
176            if (!requestUrl().contains("?")) {
177                query = "";
178            } else {
179                query = requestUrl().split("\\?", 2)[1];
180            }
181        }
182        return query;
183    }
184
185    @Override
186    public String getRemoteAddr() {
187        if (request != null) {
188            return request.getRemoteAddr();
189        } else {
190            return "0.0.0.0";
191        }
192    }
193
194    @Override
195    public String getActualIp() {
196        if (!empty(Settings.instance().getIpHeaderName())) {
197            return or(request.getHeader(Settings.instance().getIpHeaderName()), getRemoteAddr());
198        }
199        return getRemoteAddr();
200    }
201
202    @Override
203    public Object getBodyObject(Class clazz) {
204
205        if (bodyObject == null) {
206            ObjectMapper mapper = new ObjectMapper();
207            try {
208                bodyObject = mapper.readValue(getContent(), clazz);
209            } catch (IOException e) {
210                Log.exception(e, "Could not parse JSON body for the request: " + content);
211                throw new ClientException("Could not parse JSON body for the request.");
212            }
213        }
214        return bodyObject;
215    }
216
217    @Override
218    public Map<String, Object> getBodyMap() {
219        if (bodyMap == null) {
220            if (or(getHeader("Content-type"), "").startsWith("application/x-www-form-urlencoded")) {
221                bodyMap = new HashMap<>();
222
223                for (NameValuePair pair: URLEncodedUtils.parse(getContent(), Charset.forName(or(request.getCharacterEncoding(), "UTF8")))) {
224                    if (bodyMap.containsKey(pair.getName())) {
225                        Object existing = bodyMap.get(pair.getName());
226                        if (existing instanceof List) {
227                            ((List)existing).add(pair.getValue());
228                        } else {
229                            bodyMap.put(pair.getName(), list(existing, pair.getValue()));
230                        }
231                    } else {
232                        bodyMap.put(pair.getName(), pair.getValue());
233                    }
234                }
235            } else {
236                ObjectMapper mapper = new ObjectMapper();
237                String content = getContent();
238                if (StringUtils.isEmpty(content)) {
239                    bodyMap = new HashMap<>();
240                } else {
241                    try {
242                        bodyMap = mapper.readValue(getContent(),
243                                    new TypeReference<HashMap<String, Object>>() {
244                                    });
245                    } catch (IOException e) {
246                        Log.exception(e, "Could not parse JSON body for the request: " + content);
247                        throw new ClientException("Could not parse JSON body for the request.");
248                    }
249
250                }
251            }
252        }
253        return bodyMap;
254    }
255
256    @Override
257    public Object getBodyParam(String name) {
258
259        return getBodyMap().getOrDefault(name, null);
260    }
261
262    @Override
263    public Map<String, String> getQueryParams() {
264        if (queryParams == null) {
265            queryParams = queryToParams();
266        }
267        return queryParams;
268    }
269
270
271    private Map<String, String> queryToParams() {
272        Map<String, String> query_pairs = new HashMap<String, String>();
273        if (empty(getQueryString())) {
274            return map();
275        }
276        String[] pairs = getQueryString().split("&");
277        for (String pair : pairs) {
278            int idx = pair.indexOf("=");
279            if (idx < 0) {
280                continue;
281            }
282            try {
283                query_pairs.put(URLDecoder.decode(pair.substring(0, idx), "UTF-8"), URLDecoder.decode(pair.substring(idx + 1), "UTF-8"));
284            } catch (UnsupportedEncodingException e) {
285                Log.exception(e, "Error parsing url {0}", getQueryString());
286            }
287        }
288        return query_pairs;
289    }
290
291
292    @Override
293    public Cookie[] getCookies() {
294        return this.request.getCookies();
295    }
296
297    @Override
298    public Cookie getCookie(String cookieName) {
299        if (request.getCookies() != null) {
300            for (Cookie cookie : request.getCookies()) {
301                if (cookieName.equals(cookie.getName())) {
302                    return cookie;
303                }
304            }
305        }
306        return null;
307    }
308
309    @Override
310    public String getPath() {
311        return path;
312    }
313
314    @Override
315    public void setPath(String path) {
316        this.path = path;
317    }
318
319    @Override
320    public String getHeader(String name) {
321        return this.request.getHeader(name);
322    }
323
324    @Override
325    public BufferedReader getReader() throws IOException {
326        return this.request.getReader();
327    }
328
329    @Override
330    public String getContent() {
331        if (content == null) {
332            try {
333                //this.content = IOUtils.toString(getReader());
334                this.content = IOUtils.toString(this.request.getInputStream(), "UTF8");
335            //} catch (java.lang.IllegalStateException e) {
336            //    try {
337            //        this.content = IOUtils.toString(this.request.getInputStream());
338            //   } catch (IOException e2) {
339            //        throw new RuntimeException(e);
340            //    }
341            } catch (IOException e) {
342                throw new RuntimeException(e);
343            }
344        }
345        return content;
346
347    }
348
349    @Override
350    public Enumeration<String> getHeaderNames() {
351        if (request == null) {
352            //return (Enumeration<String>)new ArrayList<String>();
353            return new Vector().elements();
354        } else {
355            return request.getHeaderNames();
356        }
357
358    }
359
360
361
362
363
364    @Override
365    public IUser getUser() {
366        return user;
367    }
368
369    @Override
370    public void setUser(IUser user) {
371        this.user = user;
372    }
373
374    @Override
375    public IOrg getOrg() {
376        return org;
377    }
378
379    @Override
380    public void setOrg(IOrg org) {
381        this.org = org;
382    }
383
384    @Override
385    public String getMethod() {
386        return this.request.getMethod();
387    }
388
389    @Override
390    public String getParameter(String paramName) {
391        return this.request.getParameter(paramName);
392    }
393
394    @Override
395    public Boolean getIsJsonRequest() {
396        return isJsonRequest;
397    }
398
399    @Override
400    public void setIsJsonRequest(Boolean isJsonRequest) {
401        this.isJsonRequest = isJsonRequest;
402    }
403
404    @Override
405    public void setQuery(String query) {
406        this.query = query;
407    }
408
409    @Override
410    public Map<String, Object> getItems() {
411        return items;
412    }
413
414    @Override
415    public void setItems(Map<String, Object> items) {
416        this.items = items;
417    }
418
419    @Override
420    public SandboxedRequest getSandboxedRequest(Sandbox box) {
421        if (sandboxedRequest == null) {
422            sandboxedRequest = new SandboxedRequest(box, this);
423        }
424        return sandboxedRequest;
425    }
426
427    @Override
428    public Set<String> getScopes() {
429        return scopes;
430    }
431
432    @Override
433    public StRequest setScopes(Set<String> scopes) {
434        this.scopes = scopes;
435        return this;
436    }
437
438    @Override
439    public boolean isScoped() {
440        return scoped;
441    }
442
443    @Override
444    public StRequest setScoped(boolean scoped) {
445        this.scoped = scoped;
446        return this;
447    }
448
449    @Column
450    public Long getValetUserId() {
451        return valetUserId;
452    }
453
454    public StRequest setValetUserId(Long valetUserId) {
455        this.valetUserId = valetUserId;
456        return this;
457    }
458
459    @Column
460    public String getValetEmail() {
461        return valetEmail;
462    }
463
464    public StRequest setValetEmail(String valetEmail) {
465        this.valetEmail = valetEmail;
466        return this;
467    }
468}