blob: 8e3b04b190d60376031d69c6dd36e760069f7ad5 [file] [log] [blame]
// Copyright 2011 Google Inc. All Rights Reserved.
//
// 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.google.enterprise.adaptor.prebuilt;
import com.google.enterprise.adaptor.AbstractAdaptor;
import com.google.enterprise.adaptor.AdaptorContext;
import com.google.enterprise.adaptor.Config;
import com.google.enterprise.adaptor.DocId;
import com.google.enterprise.adaptor.DocIdPusher;
import com.google.enterprise.adaptor.IOHelper;
import com.google.enterprise.adaptor.Request;
import com.google.enterprise.adaptor.Response;
import java.io.*;
import java.util.*;
import java.util.logging.Logger;
import java.util.regex.*;
/**
* Adaptor serving files from current directory
*/
public class FileSystemAdaptor extends AbstractAdaptor {
private static final String CONFIG_SRC = "filesystemadaptor.src";
private static final String CONFIG_INCLUDE = "filesystemadaptor.include";
private static final String CONFIG_EXCLUDE = "filesystemadaptor.exclude";
private static Logger log
= Logger.getLogger(FileSystemAdaptor.class.getName());
private File serveDir;
private Pattern include;
private Pattern exclude;
@Override
public void initConfig(Config config) {
config.addKey(CONFIG_SRC, ".");
// White list of files to serve. Regular expression is matched against
// full path name, case sensitively. Partial matches are allowed, so you
// should use ^ and $ to mark beginning and end of file name if that is what
// you expect. Understand that '.' (period) means any character in regular
// expressions, so you should use '\.' for a literal period. When
// configuring via a properties file, backslash is a special character, so
// you need to use two backslashes instead of one.
// This default matches everything.
config.addKey(CONFIG_INCLUDE, "");
// Black list (overrides white list) of files to not serve. See include.
// This default matches nothing (meaning nothing is excluded).
config.addKey(CONFIG_EXCLUDE, "$^");
}
@Override
public void init(AdaptorContext context) throws Exception {
Config config = context.getConfig();
String source = config.getValue(CONFIG_SRC);
this.serveDir = new File(source).getCanonicalFile();
// Use DOTALL to prevent accidental newline concerns. Since files are not
// line-based, it seems unlikely this would be the wrong setting.
String strInclude = config.getValue(CONFIG_INCLUDE);
include = Pattern.compile(strInclude, Pattern.DOTALL);
String strExclude = config.getValue(CONFIG_EXCLUDE);
exclude = Pattern.compile(strExclude, Pattern.DOTALL);
}
@Override
public void getDocIds(DocIdPusher pusher) throws IOException,
InterruptedException {
ArrayList<DocId> mockDocIds = new ArrayList<DocId>();
String parent = serveDir.toString();
try {
for (File file : new RecursiveFileIterator(serveDir)) {
String name = file.toString();
if (!name.startsWith(parent)) {
throw new IllegalStateException(
"Internal problem: the file's path does not begin with parent.");
}
if (!isFileAllowed(file)) {
continue;
}
// +1 for slash
name = name.substring(parent.length() + 1);
mockDocIds.add(new DocId(name));
}
} catch (RecursiveFileIterator.WrappedIOException ex) {
throw ex.getCause();
}
pusher.pushDocIds(mockDocIds);
}
@Override
public void getDocContent(Request req, Response resp) throws IOException {
DocId id = req.getDocId();
File file = new File(serveDir, id.getUniqueId()).getCanonicalFile();
if (!file.exists() || !isFileDescendantOfServeDir(file)
|| !isFileAllowed(file)) {
resp.respondNotFound();
return;
}
if (!req.hasChangedSinceLastAccess(new Date(file.lastModified()))) {
resp.respondNotModified();
return;
}
InputStream input;
try {
input = new FileInputStream(file);
} catch (FileNotFoundException ex) {
resp.respondNotFound();
return;
}
try {
IOHelper.copyStream(input, resp.getOutputStream());
} finally {
input.close();
}
}
private boolean isFileDescendantOfServeDir(File file) {
while (file != null) {
if (file.equals(serveDir)) {
return true;
}
file = file.getParentFile();
}
return false;
}
private boolean isFileAllowed(File file) {
return include.matcher(file.getPath()).find()
&& !exclude.matcher(file.getPath()).find();
}
/** Call default main for adaptors. */
public static void main(String[] args) {
AbstractAdaptor.main(new FileSystemAdaptor(), args);
}
}