1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements.  See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License.  You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17 package org.apache.catalina.webresources;
18
19 import java.io.File;
20 import java.io.FileInputStream;
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.nio.file.Files;
24 import java.nio.file.StandardCopyOption;
25 import java.util.Set;
26 import java.util.jar.Manifest;
27
28 import org.apache.catalina.LifecycleException;
29 import org.apache.catalina.WebResource;
30 import org.apache.catalina.WebResourceRoot;
31 import org.apache.catalina.WebResourceRoot.ResourceSetType;
32 import org.apache.catalina.util.ResourceSet;
33 import org.apache.juli.logging.Log;
34 import org.apache.juli.logging.LogFactory;
35
36 /**
37  * Represents a {@link org.apache.catalina.WebResourceSet} based on a directory.
38  */

39 public class DirResourceSet extends AbstractFileResourceSet {
40
41     private static final Log log = LogFactory.getLog(DirResourceSet.class);
42
43     /**
44      * A no argument constructor is required for this to work with the digester.
45      */

46     public DirResourceSet() {
47         super("/");
48     }
49
50     /**
51      * Creates a new {@link org.apache.catalina.WebResourceSet} based on a
52      * directory.
53      *
54      * @param root          The {@link WebResourceRoot} this new
55      *                          {@link org.apache.catalina.WebResourceSet} will
56      *                          be added to.
57      * @param webAppMount   The path within the web application at which this
58      *                          {@link org.apache.catalina.WebResourceSet} will
59      *                          be mounted. For example, to add a directory of
60      *                          JARs to a web application, the directory would
61      *                          be mounted at "/WEB-INF/lib/"
62      * @param base          The absolute path to the directory on the file
63      *                          system from which the resources will be served.
64      * @param internalPath  The path within this new {@link
65      *                          org.apache.catalina.WebResourceSet} where
66      *                          resources will be served from.
67      */

68     public DirResourceSet(WebResourceRoot root, String webAppMount, String base,
69             String internalPath) {
70         super(internalPath);
71         setRoot(root);
72         setWebAppMount(webAppMount);
73         setBase(base);
74
75         if (root.getContext().getAddWebinfClassesResources()) {
76             File f = new File(base, internalPath);
77             f = new File(f, "/WEB-INF/classes/META-INF/resources");
78
79             if (f.isDirectory()) {
80                 root.createWebResourceSet(ResourceSetType.RESOURCE_JAR, "/",
81                          f.getAbsolutePath(), null"/");
82             }
83         }
84
85         if (getRoot().getState().isAvailable()) {
86             try {
87                 start();
88             } catch (LifecycleException e) {
89                 throw new IllegalStateException(e);
90             }
91         }
92     }
93
94
95     @Override
96     public WebResource getResource(String path) {
97         checkPath(path);
98         String webAppMount = getWebAppMount();
99         WebResourceRoot root = getRoot();
100         if (path.startsWith(webAppMount)) {
101             File f = file(path.substring(webAppMount.length()), false);
102             if (f == null) {
103                 return new EmptyResource(root, path);
104             }
105             if (!f.exists()) {
106                 return new EmptyResource(root, path, f);
107             }
108             if (f.isDirectory() && path.charAt(path.length() - 1) != '/') {
109                 path = path + '/';
110             }
111             return new FileResource(root, path, f, isReadOnly(), getManifest());
112         } else {
113             return new EmptyResource(root, path);
114         }
115     }
116
117     @Override
118     public String[] list(String path) {
119         checkPath(path);
120         String webAppMount = getWebAppMount();
121         if (path.startsWith(webAppMount)) {
122             File f = file(path.substring(webAppMount.length()), true);
123             if (f == null) {
124                 return EMPTY_STRING_ARRAY;
125             }
126             String[] result = f.list();
127             if (result == null) {
128                 return EMPTY_STRING_ARRAY;
129             } else {
130                 return result;
131             }
132         } else {
133             if (!path.endsWith("/")) {
134                 path = path + "/";
135             }
136             if (webAppMount.startsWith(path)) {
137                 int i = webAppMount.indexOf('/', path.length());
138                 if (i == -1) {
139                     return new String[] {webAppMount.substring(path.length())};
140                 } else {
141                     return new String[] {
142                             webAppMount.substring(path.length(), i)};
143                 }
144             }
145             return EMPTY_STRING_ARRAY;
146         }
147     }
148
149     @Override
150     public Set<String> listWebAppPaths(String path) {
151         checkPath(path);
152         String webAppMount = getWebAppMount();
153         ResourceSet<String> result = new ResourceSet<>();
154         if (path.startsWith(webAppMount)) {
155             File f = file(path.substring(webAppMount.length()), true);
156             if (f != null) {
157                 File[] list = f.listFiles();
158                 if (list != null) {
159                     for (File entry : list) {
160                         StringBuilder sb = new StringBuilder(path);
161                         if (path.charAt(path.length() - 1) != '/') {
162                             sb.append('/');
163                         }
164                         sb.append(entry.getName());
165                         if (entry.isDirectory()) {
166                             sb.append('/');
167                         }
168                         result.add(sb.toString());
169                     }
170                 }
171             }
172         } else {
173             if (!path.endsWith("/")) {
174                 path = path + "/";
175             }
176             if (webAppMount.startsWith(path)) {
177                 int i = webAppMount.indexOf('/', path.length());
178                 if (i == -1) {
179                     result.add(webAppMount + "/");
180                 } else {
181                     result.add(webAppMount.substring(0, i + 1));
182                 }
183             }
184         }
185         result.setLocked(true);
186         return result;
187     }
188
189     @Override
190     public boolean mkdir(String path) {
191         checkPath(path);
192         if (isReadOnly()) {
193             return false;
194         }
195         String webAppMount = getWebAppMount();
196         if (path.startsWith(webAppMount)) {
197             File f = file(path.substring(webAppMount.length()), false);
198             if (f == null) {
199                 return false;
200             }
201             return f.mkdir();
202         } else {
203             return false;
204         }
205     }
206
207     @Override
208     public boolean write(String path, InputStream is, boolean overwrite) {
209         checkPath(path);
210
211         if (is == null) {
212             throw new NullPointerException(
213                     sm.getString("dirResourceSet.writeNpe"));
214         }
215
216         if (isReadOnly()) {
217             return false;
218         }
219
220         // write() is meant to create a file so ensure that the path doesn't
221         // end in '/'
222         if (path.endsWith("/")) {
223             return false;
224         }
225
226         File dest = null;
227         String webAppMount = getWebAppMount();
228         if (path.startsWith(webAppMount)) {
229             dest = file(path.substring(webAppMount.length()), false);
230             if (dest == null) {
231                 return false;
232             }
233         } else {
234             return false;
235         }
236
237         if (dest.exists() && !overwrite) {
238             return false;
239         }
240
241         try {
242             if (overwrite) {
243                 Files.copy(is, dest.toPath(), StandardCopyOption.REPLACE_EXISTING);
244             } else {
245                 Files.copy(is, dest.toPath());
246             }
247         } catch (IOException ioe) {
248             return false;
249         }
250
251         return true;
252     }
253
254     @Override
255     protected void checkType(File file) {
256         if (file.isDirectory() == false) {
257             throw new IllegalArgumentException(sm.getString("dirResourceSet.notDirectory",
258                     getBase(), File.separator, getInternalPath()));
259         }
260     }
261
262     //-------------------------------------------------------- Lifecycle methods
263     @Override
264     protected void initInternal() throws LifecycleException {
265         super.initInternal();
266         // Is this an exploded web application?
267         if (getWebAppMount().equals("")) {
268             // Look for a manifest
269             File mf = file("META-INF/MANIFEST.MF"true);
270             if (mf != null && mf.isFile()) {
271                 try (FileInputStream fis = new FileInputStream(mf)) {
272                     setManifest(new Manifest(fis));
273                 } catch (IOException e) {
274                     log.warn(sm.getString("dirResourceSet.manifestFail", mf.getAbsolutePath()), e);
275                 }
276             }
277         }
278     }
279 }
280