com.trsst.ui.AppServlet.java Source code

Java tutorial

Introduction

Here is the source code for com.trsst.ui.AppServlet.java

Source

/*
 * Copyright 2013 mpowers
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.trsst.ui;

import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.nio.charset.Charset;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.tika.Tika;

import com.trsst.Command;
import com.trsst.Common;

/**
 * Private servlet for our private client. Exposes command-line client as a
 * servlet for "post" command only. Parameters "attach" and "home" are rejected.
 * Non-local clients are rejected by default.
 */
public class AppServlet extends HttpServlet {

    private static final long serialVersionUID = -8082699767921771750L;
    private static final String[] ALLOWED_FILES = { "boiler.css", "index.html", "favicon.ico", "jquery-1.10.1.js",
            "model.js", "composer.js", "pollster.js", "renderer.js", "controller.js", "ui.css", "icon-256.png",
            "icon-back.png", "note.svg", "note.png", "loading-on-white.gif", "loading-on-gray.gif",
            "loading-on-orange.gif", "icon-rss.png" };

    private final Map<String, byte[]> resources;
    private boolean restricted;

    public AppServlet() {
        this(true);
    }

    public AppServlet(boolean restricted) {

        this.restricted = restricted;

        // load static resources
        resources = new HashMap<String, byte[]>();
        for (String s : ALLOWED_FILES) {
            try {
                resources.put(s, Common.readFully(this.getClass().getResourceAsStream("site/" + s)));
            } catch (Exception e) {
                log.error("Could not load static http resource: " + s, e);
            }
        }
    }

    @Override
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        doPost(request, response);
    }

    @Override
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // FLAG: limit access only to local clients
        if (restricted && !request.getRemoteAddr().equals(request.getLocalAddr())) {
            response.sendError(HttpServletResponse.SC_FORBIDDEN, "Non-local clients are not allowed.");
            return;
        }

        // in case of any posted files
        InputStream inStream = null;

        // determine if supported command: pull, push, post
        String path = request.getPathInfo();
        System.err.println(new Date().toString() + " " + path);
        if (path != null) {
            // FLAG: limit only to pull and post
            if (path.startsWith("/pull/") || path.startsWith("/post")) {
                // FLAG: we're sending the user's keystore
                // password over the wire (over SSL)
                List<String> args = new LinkedList<String>();
                if (path.startsWith("/pull/")) {
                    path = path.substring("/pull/".length());
                    response.setContentType("application/atom+xml; type=feed; charset=utf-8");
                    // System.out.println("doPull: " +
                    // request.getParameterMap());
                    args.add("pull");
                    if (request.getParameterMap().size() > 0) {
                        boolean first = true;
                        for (Object name : request.getParameterMap().keySet()) {
                            // FLAG: don't allow "home" (server-abuse)
                            // FLAG: don't allow "attach" (file-system access)
                            if ("decrypt".equals(name) || "pass".equals(name)) {
                                for (String value : request.getParameterValues(name.toString())) {
                                    args.add("--" + name.toString());
                                    args.add(value);
                                }
                            } else {
                                for (String value : request.getParameterValues(name.toString())) {
                                    if (first) {
                                        path = path + '?';
                                        first = false;
                                    } else {
                                        path = path + '&';
                                    }
                                    path = path + name + '=' + value;
                                }
                            }
                        }
                    }
                    args.add(path);

                } else if (path.startsWith("/post")) {
                    // System.out.println("doPost: " +
                    // request.getParameterMap());
                    args.add("post");

                    try { // h/t http://stackoverflow.com/questions/2422468
                        List<FileItem> items = new ServletFileUpload(new DiskFileItemFactory())
                                .parseRequest(request);
                        for (FileItem item : items) {
                            if (item.isFormField()) {
                                // process regular form field
                                String name = item.getFieldName();
                                String value = item.getString("UTF-8").trim();
                                // System.out.println("AppServlet: " + name
                                // + " : " + value);
                                if (value.length() > 0) {
                                    // FLAG: don't allow "home" (server-abuse)
                                    // FLAG: don't allow "attach" (file-system
                                    // access)
                                    if ("id".equals(name)) {
                                        if (value.startsWith("urn:feed:")) {
                                            value = value.substring("urn:feed:".length());
                                        }
                                        args.add(value);
                                    } else if (!"home".equals(name) && !"attach".equals(name)) {
                                        args.add("--" + name);
                                        args.add(value);
                                    }
                                } else {
                                    log.debug("Empty form value for name: " + name);
                                }
                            } else if (item.getSize() > 0) {
                                // process form file field (input type="file").
                                // String filename = FilenameUtils.getName(item
                                // .getName());
                                if (item.getSize() > 1024 * 1024 * 10) {
                                    throw new FileUploadException("Current maximum upload size is 10MB");
                                }
                                String name = item.getFieldName();
                                if ("icon".equals(name) || "logo".equals(name)) {
                                    args.add("--" + name);
                                    args.add("-");
                                }
                                inStream = item.getInputStream();
                                // NOTE: only handles one file!
                            } else {
                                log.debug("Ignored form field: " + item.getFieldName());
                            }
                        }
                    } catch (FileUploadException e) {
                        response.sendError(HttpServletResponse.SC_BAD_REQUEST,
                                "Could not parse multipart request: " + e);
                        return;
                    }
                }

                // send post data if any to command input stream
                if (inStream != null) {
                    args.add("--attach");
                }
                //System.out.println(args);

                // make sure we don't create another local server
                args.add("--host");
                args.add(request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort()
                        + "/feed");

                PrintStream outStream = new PrintStream(response.getOutputStream(), false, "UTF-8");
                int result = new Command().doBegin(args.toArray(new String[0]), outStream, inStream);
                if (result != 0) {
                    response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
                            "Internal error code: " + result);
                } else {
                    outStream.flush();
                }
                return;
            }

            // otherwise: determine if static resource request
            if (path.startsWith("/")) {
                path = path.substring(1);
            }

            byte[] result = resources.get(path);
            String mimetype = null;
            if (result == null) {
                // if ("".equals(path) || path.endsWith(".html")) {
                // treat all html requests with index doc
                result = resources.get("index.html");
                mimetype = "text/html";
                // }
            }
            if (result != null) {
                if (mimetype == null) {
                    if (path.endsWith(".html")) {
                        mimetype = "text/html";
                    } else if (path.endsWith(".css")) {
                        mimetype = "text/css";
                    } else if (path.endsWith(".js")) {
                        mimetype = "application/javascript";
                    } else if (path.endsWith(".png")) {
                        mimetype = "image/png";
                    } else if (path.endsWith(".jpg")) {
                        mimetype = "image/jpeg";
                    } else if (path.endsWith(".jpeg")) {
                        mimetype = "image/jpeg";
                    } else if (path.endsWith(".gif")) {
                        mimetype = "image/gif";
                    } else {
                        mimetype = new Tika().detect(result);
                    }
                }
                if (request.getHeader("If-None-Match:") != null) {
                    // client should always use cached version
                    log.info("sending 304");
                    response.setStatus(304); // Not Modified
                    return;
                }
                // otherwise allow ETag/If-None-Match
                response.setHeader("ETag", Long.toHexString(path.hashCode()));
                if (mimetype != null) {
                    response.setContentType(mimetype);
                }
                response.setContentLength(result.length);
                response.getOutputStream().write(result);
                return;
            }

        }

        // // otherwise: 404 Not Found
        // response.sendError(HttpServletResponse.SC_NOT_FOUND);
    }

    private final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(this.getClass());

}