/**
* Copyright (c) 2008-2016, XebiaLabs B.V., All rights reserved.
*
*
* Overthere is licensed under the terms of the GPLv2
* <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most XebiaLabs Libraries.
* There are special exceptions to the terms and conditions of the GPLv2 as it is applied to
* this software, see the FLOSS License Exception
* <http://github.com/xebialabs/overthere/blob/master/LICENSE>.
*
* This program 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; version 2
* of the License.
*
* This program 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 this
* program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth
* Floor, Boston, MA 02110-1301 USA
*/
package com.xebialabs.overthere.smb;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.security.Security;
import java.util.Map;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.hierynomus.mserref.NtStatus;
import com.hierynomus.smbj.DefaultConfig;
import com.hierynomus.smbj.SMBClient;
import com.hierynomus.smbj.auth.AuthenticationContext;
import com.hierynomus.smbj.common.SMBApiException;
import com.hierynomus.smbj.connection.Connection;
import com.hierynomus.smbj.session.Session;
import com.hierynomus.smbj.share.DiskShare;
import com.hierynomus.smbj.share.Share;
import com.xebialabs.overthere.ConnectionOptions;
import com.xebialabs.overthere.OverthereFile;
import com.xebialabs.overthere.RuntimeIOException;
import com.xebialabs.overthere.cifs.CifsConnectionType;
import com.xebialabs.overthere.proxy.ProxyConnection;
import com.xebialabs.overthere.spi.AddressPortMapper;
import com.xebialabs.overthere.spi.BaseOverthereConnection;
import static com.xebialabs.overthere.ConnectionOptions.ADDRESS;
import static com.xebialabs.overthere.ConnectionOptions.PASSWORD;
import static com.xebialabs.overthere.ConnectionOptions.PORT;
import static com.xebialabs.overthere.ConnectionOptions.USERNAME;
import static com.xebialabs.overthere.cifs.CifsConnectionBuilder.CONNECTION_TYPE;
import static com.xebialabs.overthere.smb.SmbConnectionBuilder.PATH_SHARE_MAPPINGS;
import static com.xebialabs.overthere.smb.SmbConnectionBuilder.PATH_SHARE_MAPPINGS_DEFAULT;
import static com.xebialabs.overthere.smb.SmbConnectionBuilder.PORT_DEFAULT_SMB;
import static com.xebialabs.overthere.smb.SmbConnectionBuilder.SMB_PORT;
import static java.net.InetSocketAddress.createUnresolved;
public class SmbConnection extends BaseOverthereConnection {
private final SMBClient client;
private final String hostname;
private final int smbPort;
private Connection connection;
private Session session;
private int port;
protected final String password;
protected CifsConnectionType cifsConnectionType;
protected final String username;
static {
if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
Security.addProvider(new BouncyCastleProvider());
}
}
protected SmbConnection(String protocol, ConnectionOptions options, AddressPortMapper mapper, boolean canStartProcess) {
super(protocol, options, mapper, canStartProcess);
this.cifsConnectionType = options.getEnum(CONNECTION_TYPE, CifsConnectionType.class);
if (mapper instanceof ProxyConnection) {
throw new IllegalArgumentException("Cannot open a smb:" + cifsConnectionType.toString().toLowerCase() + ": connection through an HTTP proxy");
}
String unmappedAddress = options.get(ADDRESS);
int unmappedPort = options.get(PORT, this.cifsConnectionType.getDefaultPort(options));
InetSocketAddress addressPort = mapper.map(createUnresolved(unmappedAddress, unmappedPort));
hostname = addressPort.getHostName();
port = addressPort.getPort();
int unmappedSmbPort = options.getInteger(SMB_PORT, PORT_DEFAULT_SMB);
InetSocketAddress smbAddressPort = mapper.map(createUnresolved(unmappedAddress, unmappedSmbPort));
smbPort = smbAddressPort.getPort();
username = options.get(USERNAME);
password = options.get(PASSWORD);
client = new SMBClient(new DefaultConfig());
}
public void connect() {
createConnection();
connected();
}
private void createConnection() {
try {
UserAndDomain ud = getUserNameAndDomain(username);
String user = ud.getUsername();
String domain = ud.getDomain();
connection = client.connect(hostname);
AuthenticationContext authContext = new AuthenticationContext(user, password.toCharArray(), domain);
session = connection.authenticate(authContext);
} catch (SMBApiException smbApi) {
if (smbApi.getStatus() == NtStatus.STATUS_LOGON_FAILURE) {
throw new RuntimeIOException(smbApi);
}
throw smbApi;
} catch (IOException e) {
throw new RuntimeIOException(e);
}
}
@Override
public OverthereFile getFile(String hostPath) {
Map<String, String> pathMappings = options.get(PATH_SHARE_MAPPINGS, PATH_SHARE_MAPPINGS_DEFAULT);
return new SmbFile(this, hostPath, pathMappings);
}
@Override
public OverthereFile getFile(OverthereFile parent, String child) {
return parent.getFile(child);
}
@Override
protected void doClose() {
try {
if (session != null) {
session.close();
}
} catch (IOException e) {
logger.warn("Exception while trying to close smb session", e);
} finally {
try {
if (connection != null) {
connection.close();
}
} catch (Exception e) {
logger.warn("Exception while trying to close smb connection", e);
}
}
}
@Override
protected OverthereFile getFileForTempFile(OverthereFile parent, String name) {
return getFile(parent, name);
}
@Override
public String toString() {
return "smb:" + cifsConnectionType.toString().toLowerCase() + "://" + username + "@" + hostname + ":" + smbPort + ":" + port;
}
protected DiskShare getShare(String shareName) {
Share share = session.connectShare(shareName);
if (!(share instanceof DiskShare)) {
close();
throw new RuntimeIOException("The share " + shareName + " is not a disk share");
}
return (DiskShare) share;
}
private UserAndDomain getUserNameAndDomain(String user) {
if (user.contains("\\")) {
String[] split = user.split("\\\\");
return new UserAndDomain(split[1], split[0]);
}
if (user.contains("@")) {
String[] split = user.split("@");
return new UserAndDomain(split[0], split[1]);
}
return new UserAndDomain(user, "");
}
private static class UserAndDomain {
String username;
String domain;
private UserAndDomain(String username, String domain) {
this.username = username;
this.domain = domain;
}
private String getUsername() {
return username;
}
private String getDomain() {
return domain;
}
}
private static Logger logger = LoggerFactory.getLogger(SmbConnection.class);
}