1 | // Copyright (C) 2009 Google Inc. |
2 | // |
3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
4 | // you may not use this file except in compliance with the License. |
5 | // You may obtain a copy of the License at |
6 | // |
7 | // http://www.apache.org/licenses/LICENSE-2.0 |
8 | // |
9 | // Unless required by applicable law or agreed to in writing, software |
10 | // distributed under the License is distributed on an "AS IS" BASIS, |
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | // See the License for the specific language governing permissions and |
13 | // limitations under the License. |
14 | |
15 | package com.google.caja.ancillary.servlet; |
16 | |
17 | import com.google.caja.lexer.FilePosition; |
18 | import com.google.caja.parser.ParseTreeNodeContainer; |
19 | import com.google.caja.parser.html.DomParser; |
20 | import com.google.caja.parser.html.HtmlQuasiBuilder; |
21 | import com.google.caja.parser.html.Nodes; |
22 | import com.google.caja.parser.js.Expression; |
23 | import com.google.caja.parser.js.ObjectConstructor; |
24 | import com.google.caja.parser.js.StringLiteral; |
25 | import com.google.caja.parser.quasiliteral.QuasiBuilder; |
26 | import com.google.caja.render.Concatenator; |
27 | import com.google.caja.render.JsMinimalPrinter; |
28 | import com.google.caja.reporting.RenderContext; |
29 | import com.google.caja.util.Lists; |
30 | |
31 | import java.io.File; |
32 | import java.io.IOException; |
33 | import java.io.Writer; |
34 | import java.net.URI; |
35 | import java.net.URISyntaxException; |
36 | import java.util.List; |
37 | import javax.servlet.http.HttpServletRequest; |
38 | import javax.servlet.http.HttpServletResponse; |
39 | |
40 | import org.apache.commons.fileupload.FileItem; |
41 | import org.apache.commons.fileupload.FileUploadException; |
42 | import org.apache.commons.fileupload.disk.DiskFileItemFactory; |
43 | import org.apache.commons.fileupload.servlet.ServletFileUpload; |
44 | |
45 | /** |
46 | * Invoked from a file upload form in <tt>files/upload.html</tt> to upload |
47 | * a source code file. This calls back into {@code uploaded} defined in |
48 | * <tt>files/index.js</tt> with a JSON object containing the uploaded files |
49 | * so that they can be folded into the parent frame's form. |
50 | * |
51 | * @author mikesamuel@gmail.com |
52 | */ |
53 | final class UploadPage { |
54 | static void doUpload(HttpServletRequest req, HttpServletResponse resp) |
55 | throws IOException { |
56 | // Process the uploaded items |
57 | List<ObjectConstructor> uploads = Lists.newArrayList(); |
58 | |
59 | if (ServletFileUpload.isMultipartContent(req)) { |
60 | // Create a factory for disk-based file items |
61 | DiskFileItemFactory factory = new DiskFileItemFactory(); |
62 | |
63 | int maxUploadSizeBytes = 1 << 18; |
64 | factory.setSizeThreshold(maxUploadSizeBytes); // In-memory threshold |
65 | factory.setRepository(new File("/dev/null")); // Do not store on disk |
66 | ServletFileUpload upload = new ServletFileUpload(factory); |
67 | upload.setSizeMax(maxUploadSizeBytes); |
68 | |
69 | writeHeader(resp); |
70 | |
71 | // Parse the request |
72 | List<?> items; |
73 | try { |
74 | items = upload.parseRequest(req); |
75 | } catch (FileUploadException ex) { |
76 | ex.printStackTrace(); |
77 | resp.getWriter().write(Nodes.encode(ex.getMessage())); |
78 | return; |
79 | } |
80 | |
81 | for (Object fileItemObj : items) { |
82 | FileItem item = (FileItem) fileItemObj; // Written for pre-generic java. |
83 | if (!item.isFormField()) { // Then is a file |
84 | FilePosition unk = FilePosition.UNKNOWN; |
85 | String ct = item.getContentType(); |
86 | uploads.add((ObjectConstructor) QuasiBuilder.substV( |
87 | "({ i: @i, ip: @ip, it: @it? })", |
88 | "i", StringLiteral.valueOf(unk, item.getString()), |
89 | "ip", StringLiteral.valueOf(unk, item.getName()), |
90 | "it", ct != null ? StringLiteral.valueOf(unk, ct) : null)); |
91 | } |
92 | } |
93 | } else if (req.getParameter("url") != null) { |
94 | List<URI> toFetch = Lists.newArrayList(); |
95 | boolean failed = false; |
96 | for (String value : req.getParameterValues("url")) { |
97 | try { |
98 | toFetch.add(new URI(value)); |
99 | } catch (URISyntaxException ex) { |
100 | if (!failed) { |
101 | failed = true; |
102 | resp.setStatus(500); |
103 | resp.setContentType("text/html;charset=UTF-8"); |
104 | } |
105 | resp.getWriter().write( |
106 | "<p>Bad URI: " + Nodes.encode(ex.getMessage())); |
107 | } |
108 | } |
109 | if (failed) { return; } |
110 | writeHeader(resp); |
111 | FilePosition unk = FilePosition.UNKNOWN; |
112 | for (URI uri : toFetch) { |
113 | try { |
114 | Content c = UriFetcher.fetch(uri); |
115 | if (c.isText()) { |
116 | uploads.add((ObjectConstructor) QuasiBuilder.substV( |
117 | "({ i: @i, ip: @ip, it: @it? })", |
118 | "i", StringLiteral.valueOf(unk, c.getText()), |
119 | "ip", StringLiteral.valueOf(unk, uri.toString()), |
120 | "it", StringLiteral.valueOf(unk, c.type.mimeType))); |
121 | } |
122 | } catch (IOException ex) { |
123 | resp.getWriter().write( |
124 | "<p>" + Nodes.encode( |
125 | "Failed to fetch URI: " + uri + " : " + ex.getMessage())); |
126 | } |
127 | } |
128 | } else { |
129 | resp.setStatus(HttpServletResponse.SC_BAD_REQUEST); |
130 | resp.getWriter().write("Content not multipart"); |
131 | return; |
132 | } |
133 | |
134 | Expression notifyParent = (Expression) QuasiBuilder.substV( |
135 | "window.parent.uploaded([@uploads*], window.name)", |
136 | "uploads", new ParseTreeNodeContainer(uploads)); |
137 | StringBuilder jsBuf = new StringBuilder(); |
138 | RenderContext rc = new RenderContext( |
139 | new JsMinimalPrinter(new Concatenator(jsBuf))).withEmbeddable(true); |
140 | notifyParent.render(rc); |
141 | rc.getOut().noMoreTokens(); |
142 | |
143 | HtmlQuasiBuilder b = HtmlQuasiBuilder.getBuilder( |
144 | DomParser.makeDocument(null, null)); |
145 | |
146 | Writer out = resp.getWriter(); |
147 | out.write(Nodes.render(b.substV( |
148 | "<script>@js</script>", "js", jsBuf.toString()))); |
149 | } |
150 | |
151 | private static void writeHeader(HttpServletResponse resp) throws IOException { |
152 | resp.setStatus(200); |
153 | resp.setContentType("text/html;charset=UTF-8"); |
154 | Writer out = resp.getWriter(); |
155 | out.write("<h1>Loading…</h1>"); |
156 | out.flush(); |
157 | } |
158 | } |