/*
* Copyright 2012, CMM, University of Queensland.
*
* This file is part of Paul.
*
* Paul is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Paul is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Paul. If not, see <http://www.gnu.org/licenses/>.
*/
package au.edu.uq.cmm.paul.watcher;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.ini4j.Ini;
import org.ini4j.InvalidFileFormatException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* An UNC path mapper for use on a Samba fileserver. This reads the Samba
* configuration file to figure out what the locally hosted shares are.
*
* @author scrawley
*/
public class SambaUncPathnameMapper implements UncPathnameMapper {
public static final String DEFAULT_SMB_CONF_PATHNAME = "/etc/samba/smb.conf";
private static final Logger LOG =
LoggerFactory.getLogger(SambaUncPathnameMapper.class);
private static final Pattern UNC_PATTERN =
Pattern.compile("//([^/]+)/([^/]+)(?:/+(.*))?");
private Map<String, File> shareMap = new HashMap<String, File>();
private Set<String> hostNames;
public SambaUncPathnameMapper()
throws InvalidFileFormatException, IOException {
this(DEFAULT_SMB_CONF_PATHNAME);
}
public SambaUncPathnameMapper(String smbConfFileName)
throws InvalidFileFormatException, IOException {
initShareMap(smbConfFileName);
initHostnames();
}
/**
* Configure the set of Strings that we'll recognize as our hostname
* an UNC pathname.
*
* @throws UnknownHostException
*/
private void initHostnames() throws UnknownHostException {
// Note: this will only give us the primary hostname & IP address.
// DNS aliases and secondary IP addresses won't show up. (If
// this is a problem we may need to configure the 'hostnames'
// set manually.)
LOG.debug("Figuring out our hostnames");
hostNames = new HashSet<String>();
InetAddress host = InetAddress.getLocalHost();
String hostAddr = host.getHostAddress();
String hostName = host.getHostName();
String canonicalHostName = host.getCanonicalHostName();
hostNames.add(hostAddr);
if (hostNames.add(hostName)) {
int firstDot = hostName.indexOf(".");
if (firstDot > 0) {
hostNames.add(hostName.substring(0, firstDot));
}
}
if (hostNames.add(canonicalHostName)) {
int firstDot = canonicalHostName.indexOf(".");
if (firstDot > 0) {
hostNames.add(canonicalHostName.substring(0, firstDot));
}
}
LOG.info("The following hostnames will be recognized " +
"as 'us' by the share mapper: " + hostNames);
}
/**
* Load the SMB share mapping from the Samba config file.
*
* @param smbConfFileName the pathname of the config file.
* @throws FileNotFoundException
* @throws IOException
* @throws InvalidFileFormatException
*/
private void initShareMap(String smbConfFileName)
throws FileNotFoundException, IOException,
InvalidFileFormatException {
FileInputStream is = new FileInputStream(smbConfFileName);
try {
LOG.debug("Loading share map from Samba config file - " + smbConfFileName);
Ini ini = new Ini();
ini.load(is);
for (String section : ini.keySet()) {
if (section.equals("global")) {
continue;
}
String path = ini.get(section, "path");
if (path == null) {
continue;
}
File dir = new File(path);
if (!dir.exists() || !dir.isDirectory()) {
LOG.info("Ignoring share '" + section +
"' because the mapped object '" + dir +
"' is not an existing directory");
continue;
}
LOG.debug("Share '" + section + "' maps to directory " + dir);
shareMap.put(section, dir);
}
} finally {
is.close();
}
}
@Override
public File mapUncPathname(String uncPathname) {
String canonicalUncPathname = uncPathname.replace('\\', '/');
Matcher matcher = UNC_PATTERN.matcher(canonicalUncPathname);
if (!matcher.matches()) {
LOG.info("Invalid UNC path: '" + canonicalUncPathname + "'");
return null;
}
if (!hostNames.contains(matcher.group(1))) {
LOG.info("UNC path '" + canonicalUncPathname + "'s hostname is not us");
return null;
}
File sharePath = shareMap.get(matcher.group(2));
if (sharePath == null) {
LOG.info("UNC path '" + canonicalUncPathname + "'s share is not known");
return null;
}
if (matcher.group(3) == null) {
return sharePath;
} else {
return new File(sharePath, matcher.group(3));
}
}
}