001/**
002 * Copyright 2010-2014 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.kuali.common.util.secure.channel;
017
018import java.io.BufferedOutputStream;
019import java.io.ByteArrayInputStream;
020import java.io.ByteArrayOutputStream;
021import java.io.File;
022import java.io.IOException;
023import java.io.InputStream;
024import java.io.OutputStream;
025import java.util.ArrayList;
026import java.util.List;
027import java.util.Properties;
028
029import org.apache.commons.io.FileUtils;
030import org.apache.commons.io.FilenameUtils;
031import org.apache.commons.io.IOUtils;
032import org.apache.commons.lang3.StringUtils;
033import org.kuali.common.util.Assert;
034import org.kuali.common.util.CollectionUtils;
035import org.kuali.common.util.Encodings;
036import org.kuali.common.util.LocationUtils;
037import org.kuali.common.util.PropertyUtils;
038import org.kuali.common.util.Str;
039import org.kuali.common.util.base.Threads;
040import org.kuali.common.util.property.ImmutableProperties;
041import org.slf4j.Logger;
042import org.slf4j.LoggerFactory;
043
044import com.google.common.base.Optional;
045import com.google.common.collect.ImmutableList;
046import com.jcraft.jsch.Channel;
047import com.jcraft.jsch.ChannelExec;
048import com.jcraft.jsch.ChannelSftp;
049import com.jcraft.jsch.JSch;
050import com.jcraft.jsch.JSchException;
051import com.jcraft.jsch.Session;
052import com.jcraft.jsch.SftpATTRS;
053import com.jcraft.jsch.SftpException;
054
055/**
056 * @deprecated
057 */
058@Deprecated
059public final class DefaultSecureChannel implements SecureChannel {
060
061        private static final Logger logger = LoggerFactory.getLogger(DefaultSecureChannel.class);
062
063        private static final String SFTP = "sftp";
064        private static final String EXEC = "exec";
065        private static final String FORWARDSLASH = "/";
066        private final File knownHosts;
067        private final File config;
068        private final boolean useConfigFile;
069        private final boolean includeDefaultPrivateKeyLocations;
070        private final boolean strictHostKeyChecking;
071        private final int port;
072        private final int waitForClosedSleepMillis;
073        private final String encoding;
074        private final String username;
075        private final String hostname;
076        private final Optional<Integer> connectTimeout;
077        private final List<File> privateKeys;
078        private final List<String> privateKeyStrings;
079        private final Properties options;
080
081        private Session session;
082        private ChannelSftp sftp;
083        private boolean open = false;
084
085        @Override
086        public synchronized void open() throws IOException {
087                Assert.isFalse(open, "Already open");
088                logOpen();
089                try {
090                        JSch jsch = getJSch();
091                        this.session = openSession(jsch);
092                        this.sftp = openSftpChannel(session, connectTimeout);
093                        this.open = true;
094                } catch (JSchException e) {
095                        throw new IOException("Unexpected error opening secure channel", e);
096                }
097        }
098
099        @Override
100        public synchronized void close() {
101                if (!open) {
102                        return;
103                }
104                logger.info("Closing secure channel [{}]", ChannelUtils.getLocation(username, hostname));
105                closeQuietly(sftp);
106                closeQuietly(session);
107                this.open = false;
108        }
109
110        @Override
111        public Result executeCommand(String command) {
112                return executeCommand(command, null);
113        }
114
115        @Override
116        public Result executeCommand(String command, String stdin) {
117                Assert.noBlanks(command);
118                ChannelExec exec = null;
119                InputStream stdoutStream = null;
120                ByteArrayOutputStream stderrStream = null;
121                InputStream stdinStream = null;
122                try {
123                        // Preserve start time
124                        long start = System.currentTimeMillis();
125                        // Open an exec channel
126                        exec = (ChannelExec) session.openChannel(EXEC);
127                        // Convert the command string to bytes
128                        byte[] commandBytes = Str.getBytes(command, encoding);
129                        // Store the command on the exec channel
130                        exec.setCommand(commandBytes);
131                        // Prepare the stdin stream
132                        stdinStream = getInputStream(stdin, encoding);
133                        // Prepare the stderr stream
134                        stderrStream = new ByteArrayOutputStream();
135                        // Get the stdout stream from the ChannelExec object
136                        stdoutStream = exec.getInputStream();
137                        // Update the ChannelExec object with the stdin stream
138                        exec.setInputStream(stdinStream);
139                        // Update the ChannelExec object with the stderr stream
140                        exec.setErrStream(stderrStream);
141                        // Execute the command.
142                        // This consumes anything from stdin and stores output in stdout/stderr
143                        connect(exec, Optional.<Integer> absent());
144                        // Convert stdout and stderr to String's
145                        String stdout = Str.getString(IOUtils.toByteArray(stdoutStream), encoding);
146                        String stderr = Str.getString(stderrStream.toByteArray(), encoding);
147                        // Make sure the channel is closed
148                        waitForClosed(exec, waitForClosedSleepMillis);
149                        // Return the result of executing the command
150                        return new Result(command, exec.getExitStatus(), stdin, stdout, stderr, encoding, start, System.currentTimeMillis());
151                } catch (Exception e) {
152                        throw new IllegalStateException(e);
153                } finally {
154                        // Cleanup
155                        IOUtils.closeQuietly(stdinStream);
156                        IOUtils.closeQuietly(stdoutStream);
157                        IOUtils.closeQuietly(stderrStream);
158                        closeQuietly(exec);
159                }
160        }
161
162        @Override
163        public void executeNoWait(String command) {
164                Assert.notBlank(command);
165                ChannelExec exec = null;
166                try {
167                        // Open an exec channel
168                        exec = (ChannelExec) session.openChannel(EXEC);
169                        // Convert the command string to bytes
170                        byte[] commandBytes = Str.getBytes(command, encoding);
171                        // Store the command on the exec channel
172                        exec.setCommand(commandBytes);
173                        // Execute the command.
174                        // This consumes anything from stdin and stores output in stdout/stderr
175                        connect(exec, Optional.<Integer> absent());
176                } catch (Exception e) {
177                        throw new IllegalStateException(e);
178                } finally {
179                        closeQuietly(exec);
180                }
181        }
182
183        protected InputStream getInputStream(String s, String encoding) {
184                if (s == null) {
185                        return null;
186                } else {
187                        return new ByteArrayInputStream(Str.getBytes(s, encoding));
188                }
189        }
190
191        protected void waitForClosed(ChannelExec exec, long millis) {
192                while (!exec.isClosed()) {
193                        Threads.sleep(millis);
194                }
195        }
196
197        @Override
198        public RemoteFile getWorkingDirectory() {
199                try {
200                        String workingDirectory = sftp.pwd();
201                        return getMetaData(workingDirectory);
202                } catch (SftpException e) {
203                        throw new IllegalStateException(e);
204                }
205        }
206
207        protected void logOpen() {
208                logger.info("Opening secure channel [{}] encoding={}", ChannelUtils.getLocation(username, hostname), encoding);
209                logger.debug("Private key files - {}", privateKeys.size());
210                logger.debug("Private key strings - {}", privateKeyStrings.size());
211                logger.debug("Private key config file - {}", config);
212                logger.debug("Private key config file use - {}", useConfigFile);
213                logger.debug("Include default private key locations - {}", includeDefaultPrivateKeyLocations);
214                logger.debug("Known hosts file - {}", knownHosts);
215                logger.debug("Port - {}", port);
216                logger.debug("Connect timeout - {}", connectTimeout);
217                logger.debug("Strict host key checking - {}", strictHostKeyChecking);
218                logger.debug("Configuring channel with {} custom options", options.size());
219                PropertyUtils.debug(options);
220        }
221
222        protected ChannelSftp openSftpChannel(Session session, Optional<Integer> timeout) throws JSchException {
223                ChannelSftp sftp = (ChannelSftp) session.openChannel(SFTP);
224                connect(sftp, timeout);
225                return sftp;
226        }
227
228        protected void connect(Channel channel, Optional<Integer> timeout) throws JSchException {
229                if (timeout.isPresent()) {
230                        channel.connect(timeout.get());
231                } else {
232                        channel.connect();
233                }
234        }
235
236        protected void closeQuietly(Session session) {
237                if (session != null) {
238                        session.disconnect();
239                }
240        }
241
242        protected void closeQuietly(Channel channel) {
243                if (channel != null) {
244                        channel.disconnect();
245                }
246        }
247
248        private static Properties getSessionProperties(Properties options, boolean strictHostKeyChecking) {
249                Properties properties = new Properties();
250                properties.putAll(options);
251                if (!strictHostKeyChecking) {
252                        properties.setProperty(SSHUtils.STRICT_HOST_KEY_CHECKING, SSHUtils.NO);
253                }
254                return properties;
255        }
256
257        protected Session openSession(JSch jsch) throws JSchException {
258                Session session = jsch.getSession(username, hostname, port);
259                session.setConfig(options);
260                if (connectTimeout.isPresent()) {
261                        session.connect(connectTimeout.get());
262                } else {
263                        session.connect();
264                }
265                return session;
266        }
267
268        protected JSch getJSch() throws JSchException {
269                JSch jsch = getJSch(privateKeys, privateKeyStrings);
270                if (strictHostKeyChecking && knownHosts.exists()) {
271                        String path = LocationUtils.getCanonicalPath(knownHosts);
272                        jsch.setKnownHosts(path);
273                }
274                return jsch;
275        }
276
277        protected JSch getJSch(List<File> privateKeys, List<String> privateKeyStrings) throws JSchException {
278                JSch jsch = new JSch();
279                for (File privateKey : privateKeys) {
280                        String path = LocationUtils.getCanonicalPath(privateKey);
281                        jsch.addIdentity(path);
282                }
283                int count = 0;
284                for (String privateKeyString : privateKeyStrings) {
285                        String name = "privateKeyString-" + Integer.toString(count++);
286                        byte[] bytes = Str.getBytes(privateKeyString, encoding);
287                        jsch.addIdentity(name, bytes, null, null);
288                }
289                return jsch;
290        }
291
292        protected static List<File> getUniquePrivateKeyFiles(List<File> privateKeys, boolean useConfigFile, File config, boolean includeDefaultPrivateKeyLocations) {
293                List<String> paths = new ArrayList<String>();
294                for (File privateKey : privateKeys) {
295                        paths.add(LocationUtils.getCanonicalPath(privateKey));
296                }
297                if (useConfigFile) {
298                        for (String path : SSHUtils.getFilenames(config)) {
299                                paths.add(path);
300                        }
301                }
302                if (includeDefaultPrivateKeyLocations) {
303                        for (String path : SSHUtils.PRIVATE_KEY_DEFAULTS) {
304                                paths.add(path);
305                        }
306                }
307                List<String> uniquePaths = CollectionUtils.getUniqueStrings(paths);
308                return SSHUtils.getExistingAndReadable(uniquePaths);
309        }
310
311        @Override
312        public RemoteFile getMetaData(String absolutePath) {
313                Assert.noBlanks(absolutePath);
314                return fillInAttributes(absolutePath);
315        }
316
317        @Override
318        public void deleteFile(String absolutePath) {
319                RemoteFile file = getMetaData(absolutePath);
320                if (isStatus(file, Status.MISSING)) {
321                        return;
322                }
323                if (file.isDirectory()) {
324                        throw new IllegalArgumentException("[" + ChannelUtils.getLocation(username, hostname, file) + "] is a directory.");
325                }
326                try {
327                        sftp.rm(absolutePath);
328                } catch (SftpException e) {
329                        throw new IllegalStateException(e);
330                }
331        }
332
333        @Override
334        public boolean exists(String absolutePath) {
335                RemoteFile file = getMetaData(absolutePath);
336                return isStatus(file, Status.EXISTS);
337        }
338
339        @Override
340        public boolean isDirectory(String absolutePath) {
341                RemoteFile file = getMetaData(absolutePath);
342                return isStatus(file, Status.EXISTS) && file.isDirectory();
343        }
344
345        protected RemoteFile fillInAttributes(String path) {
346                try {
347                        SftpATTRS attributes = sftp.stat(path);
348                        return fillInAttributes(path, attributes);
349                } catch (SftpException e) {
350                        return handleNoSuchFileException(path, e);
351                }
352        }
353
354        protected RemoteFile fillInAttributes(String path, SftpATTRS attributes) {
355                boolean directory = attributes.isDir();
356                int permissions = attributes.getPermissions();
357                int userId = attributes.getUId();
358                int groupId = attributes.getGId();
359                long size = attributes.getSize();
360                Status status = Status.EXISTS;
361                return new RemoteFile.Builder(path).directory(directory).permissions(permissions).userId(userId).groupId(groupId).size(size).status(status).build();
362        }
363
364        @Override
365        public void copyFile(File source, RemoteFile destination) {
366                Assert.notNull(source);
367                Assert.isTrue(source.exists());
368                Assert.isTrue(!source.isDirectory());
369                Assert.isTrue(source.canRead());
370                copyLocationToFile(LocationUtils.getCanonicalURLString(source), destination);
371        }
372
373        @Override
374        public void copyFileToDirectory(File source, RemoteFile directory) {
375                String filename = source.getName();
376                String absolutePath = getAbsolutePath(directory.getAbsolutePath(), filename);
377                RemoteFile file = new RemoteFile.Builder(absolutePath).clone(directory).build();
378                copyFile(source, file);
379        }
380
381        @Override
382        public void copyLocationToFile(String location, RemoteFile destination) {
383                Assert.notNull(location);
384                Assert.isTrue(LocationUtils.exists(location), location + " does not exist");
385                InputStream in = null;
386                try {
387                        in = LocationUtils.getInputStream(location);
388                        copyInputStreamToFile(in, destination);
389                } catch (Exception e) {
390                        throw new IllegalStateException(e);
391                } finally {
392                        IOUtils.closeQuietly(in);
393                }
394        }
395
396        @Override
397        public void copyStringToFile(String string, RemoteFile destination) {
398                Assert.notNull(string);
399                Assert.notBlank(encoding);
400                InputStream in = new ByteArrayInputStream(Str.getBytes(string, encoding));
401                copyInputStreamToFile(in, destination);
402                IOUtils.closeQuietly(in);
403        }
404
405        @Override
406        public String toString(RemoteFile source) {
407                Assert.notNull(source);
408                Assert.hasText(source.getAbsolutePath());
409                Assert.notBlank(encoding);
410                ByteArrayOutputStream out = new ByteArrayOutputStream();
411                try {
412                        copyFile(source, out);
413                        return out.toString(encoding);
414                } catch (IOException e) {
415                        throw new IllegalStateException("Unexpected IO error", e);
416                } finally {
417                        IOUtils.closeQuietly(out);
418                }
419        }
420
421        @Override
422        public void copyInputStreamToFile(InputStream source, RemoteFile destination) {
423                Assert.notNull(source);
424                try {
425                        createDirectories(destination);
426                        sftp.put(source, destination.getAbsolutePath());
427                } catch (SftpException e) {
428                        throw new IllegalStateException(e);
429                }
430        }
431
432        protected String getAbsolutePath(String absolutePath, String filename) {
433                if (StringUtils.endsWith(absolutePath, FORWARDSLASH)) {
434                        return absolutePath + filename;
435                } else {
436                        return absolutePath + FORWARDSLASH + filename;
437                }
438        }
439
440        @Override
441        public void copyLocationToDirectory(String location, RemoteFile directory) {
442                String filename = LocationUtils.getFilename(location);
443                String absolutePath = getAbsolutePath(directory.getAbsolutePath(), filename);
444                RemoteFile file = new RemoteFile.Builder(absolutePath).clone(directory).build();
445                copyLocationToFile(location, file);
446        }
447
448        @Override
449        public void copyFile(RemoteFile source, File destination) {
450                OutputStream out = null;
451                try {
452                        out = new BufferedOutputStream(FileUtils.openOutputStream(destination));
453                        copyFile(source, out);
454                } catch (Exception e) {
455                        throw new IllegalStateException(e);
456                } finally {
457                        IOUtils.closeQuietly(out);
458                }
459        }
460
461        @Override
462        public void copyRemoteFile(String absolutePath, OutputStream out) throws IOException {
463                try {
464                        sftp.get(absolutePath, out);
465                } catch (SftpException e) {
466                        throw new IOException("Unexpected IO error", e);
467                }
468        }
469
470        @Override
471        public void copyFile(RemoteFile source, OutputStream out) throws IOException {
472                copyRemoteFile(source.getAbsolutePath(), out);
473        }
474
475        @Override
476        public void copyFileToDirectory(RemoteFile source, File destination) {
477                String filename = FilenameUtils.getName(source.getAbsolutePath());
478                File newDestination = new File(destination, filename);
479                copyFile(source, newDestination);
480        }
481
482        @Override
483        public void createDirectory(RemoteFile dir) {
484                Assert.isTrue(dir.isDirectory());
485                try {
486                        createDirectories(dir);
487                } catch (SftpException e) {
488                        throw new IllegalStateException(e);
489                }
490        }
491
492        protected void createDirectories(RemoteFile file) throws SftpException {
493                boolean directoryIndicator = file.isDirectory();
494                RemoteFile remoteFile = fillInAttributes(file.getAbsolutePath());
495                validate(remoteFile, directoryIndicator);
496                List<String> directories = LocationUtils.getNormalizedPathFragments(file.getAbsolutePath(), file.isDirectory());
497                for (String directory : directories) {
498                        RemoteFile parentDir = fillInAttributes(directory);
499                        validate(parentDir, true);
500                        if (!isStatus(parentDir, Status.EXISTS)) {
501                                mkdir(parentDir);
502                        }
503                }
504        }
505
506        protected boolean isStatus(RemoteFile file, Status status) {
507                Optional<Status> remoteStatus = file.getStatus();
508                if (remoteStatus.isPresent()) {
509                        return remoteStatus.get().equals(status);
510                } else {
511                        return false;
512                }
513        }
514
515        protected void validate(RemoteFile file, boolean directoryIndicator) {
516                // Make sure status has been filled in
517                Assert.isTrue(file.getStatus().isPresent());
518
519                // Convenience flags
520                boolean missing = isStatus(file, Status.MISSING);
521                boolean exists = isStatus(file, Status.EXISTS);
522
523                // It it is supposed to be a directory, make sure it's a directory
524                // If it is supposed to be a regular file, make sure it's a regular file
525                boolean correctFileType = file.isDirectory() == directoryIndicator;
526
527                // Is everything as it should be?
528                boolean valid = missing || exists && correctFileType;
529
530                Assert.isTrue(valid, getInvalidExistingFileMessage(file));
531        }
532
533        protected String getInvalidExistingFileMessage(RemoteFile existing) {
534                if (existing.isDirectory()) {
535                        return "[" + ChannelUtils.getLocation(username, hostname, existing) + "] is an existing directory. Unable to create file.";
536                } else {
537                        return "[" + ChannelUtils.getLocation(username, hostname, existing) + "] is an existing file. Unable to create directory.";
538                }
539        }
540
541        protected void mkdir(RemoteFile dir) {
542                try {
543                        String path = dir.getAbsolutePath();
544                        logger.debug("Creating [{}]", path);
545                        sftp.mkdir(path);
546                        setAttributes(dir);
547                } catch (SftpException e) {
548                        throw new IllegalStateException(e);
549                }
550        }
551
552        protected void setAttributes(RemoteFile file) throws SftpException {
553                String path = file.getAbsolutePath();
554                if (file.getPermissions().isPresent()) {
555                        sftp.chmod(file.getPermissions().get(), path);
556                }
557                if (file.getGroupId().isPresent()) {
558                        sftp.chgrp(file.getGroupId().get(), path);
559                }
560                if (file.getUserId().isPresent()) {
561                        sftp.chown(file.getUserId().get(), path);
562                }
563        }
564
565        protected RemoteFile handleNoSuchFileException(String path, SftpException e) {
566                if (isNoSuchFileException(e)) {
567                        return new RemoteFile.Builder(path).status(Status.MISSING).build();
568                } else {
569                        throw new IllegalStateException(e);
570                }
571        }
572
573        protected boolean isNoSuchFileException(SftpException exception) {
574                return exception.id == ChannelSftp.SSH_FX_NO_SUCH_FILE;
575        }
576
577        public static class Builder {
578
579                // Required
580                private final String username;
581                private final String hostname;
582
583                // Optional
584                private File knownHosts = SSHUtils.DEFAULT_KNOWN_HOSTS;
585                private File config = SSHUtils.DEFAULT_CONFIG_FILE;
586                private boolean useConfigFile = true;
587                private boolean includeDefaultPrivateKeyLocations = true;
588                private boolean strictHostKeyChecking = true;
589                private int port = SSHUtils.DEFAULT_PORT;
590                private int waitForClosedSleepMillis = 10;
591                private String encoding = Encodings.UTF8;
592                private Optional<Integer> connectTimeout = Optional.absent();
593                private Properties options = ImmutableProperties.of();
594                private List<File> privateKeys = ImmutableList.of();
595                private List<String> privateKeyStrings = ImmutableList.of();
596
597                public DefaultSecureChannel build() {
598                        Assert.noBlanks(username, hostname, encoding);
599                        Assert.noNulls(knownHosts, config, connectTimeout, options, privateKeys, privateKeyStrings);
600                        Assert.isTrue(SSHUtils.isValidPort(port), port + " is invalid");
601                        Assert.isTrue(waitForClosedSleepMillis > 0, "waitForClosedSleepMillis must be a positive integer");
602                        if (connectTimeout.isPresent()) {
603                                Assert.isTrue(connectTimeout.get() > 0, "connectTimeout must be a positive integer");
604                        }
605                        if (useConfigFile) {
606                                Assert.exists(config);
607                                Assert.isTrue(config.canRead(), "[" + config + "] exists but is not readable");
608                        }
609                        this.options = ImmutableProperties.of(getSessionProperties(options, strictHostKeyChecking));
610                        this.privateKeys = ImmutableList.copyOf(getUniquePrivateKeyFiles(privateKeys, useConfigFile, config, includeDefaultPrivateKeyLocations));
611                        this.privateKeyStrings = ImmutableList.copyOf(privateKeyStrings);
612                        return new DefaultSecureChannel(this);
613                }
614
615                public Builder(String username, String hostname) {
616                        this.username = username;
617                        this.hostname = hostname;
618                }
619
620                public Builder knownHosts(File knownHosts) {
621                        this.knownHosts = knownHosts;
622                        return this;
623                }
624
625                public Builder config(File config) {
626                        this.config = config;
627                        return this;
628                }
629
630                public Builder useConfigFile(boolean useConfigFile) {
631                        this.useConfigFile = useConfigFile;
632                        return this;
633                }
634
635                public Builder includeDefaultPrivateKeyLocations(boolean includeDefaultPrivateKeyLocations) {
636                        this.includeDefaultPrivateKeyLocations = includeDefaultPrivateKeyLocations;
637                        return this;
638                }
639
640                public Builder strictHostKeyChecking(boolean strictHostKeyChecking) {
641                        this.strictHostKeyChecking = strictHostKeyChecking;
642                        return this;
643                }
644
645                public Builder port(int port) {
646                        this.port = port;
647                        return this;
648                }
649
650                public Builder waitForClosedSleepMillis(int waitForClosedSleepMillis) {
651                        this.waitForClosedSleepMillis = waitForClosedSleepMillis;
652                        return this;
653                }
654
655                public Builder encoding(String encoding) {
656                        this.encoding = encoding;
657                        return this;
658                }
659
660                public Builder connectTimeout(int connectTimeout) {
661                        this.connectTimeout = Optional.of(connectTimeout);
662                        return this;
663                }
664
665                public Builder options(Properties options) {
666                        this.options = options;
667                        return this;
668                }
669
670                public Builder privateKeys(List<File> privateKeys) {
671                        this.privateKeys = privateKeys;
672                        return this;
673                }
674
675                public Builder privateKeyStrings(List<String> privateKeyStrings) {
676                        this.privateKeyStrings = privateKeyStrings;
677                        return this;
678                }
679
680        }
681
682        private DefaultSecureChannel(Builder builder) {
683                this.username = builder.username;
684                this.hostname = builder.hostname;
685                this.knownHosts = builder.knownHosts;
686                this.config = builder.config;
687                this.useConfigFile = builder.useConfigFile;
688                this.includeDefaultPrivateKeyLocations = builder.includeDefaultPrivateKeyLocations;
689                this.strictHostKeyChecking = builder.strictHostKeyChecking;
690                this.port = builder.port;
691                this.waitForClosedSleepMillis = builder.waitForClosedSleepMillis;
692                this.encoding = builder.encoding;
693                this.connectTimeout = builder.connectTimeout;
694                this.options = builder.options;
695                this.privateKeys = builder.privateKeys;
696                this.privateKeyStrings = builder.privateKeyStrings;
697        }
698
699        public Session getSession() {
700                return session;
701        }
702
703        public void setSession(Session session) {
704                this.session = session;
705        }
706
707        public ChannelSftp getSftp() {
708                return sftp;
709        }
710
711        public void setSftp(ChannelSftp sftp) {
712                this.sftp = sftp;
713        }
714
715        public File getKnownHosts() {
716                return knownHosts;
717        }
718
719        public File getConfig() {
720                return config;
721        }
722
723        public boolean isUseConfigFile() {
724                return useConfigFile;
725        }
726
727        public boolean isIncludeDefaultPrivateKeyLocations() {
728                return includeDefaultPrivateKeyLocations;
729        }
730
731        public boolean isStrictHostKeyChecking() {
732                return strictHostKeyChecking;
733        }
734
735        @Override
736        public int getPort() {
737                return port;
738        }
739
740        public int getWaitForClosedSleepMillis() {
741                return waitForClosedSleepMillis;
742        }
743
744        public String getEncoding() {
745                return encoding;
746        }
747
748        @Override
749        public String getUsername() {
750                return username;
751        }
752
753        @Override
754        public String getHostname() {
755                return hostname;
756        }
757
758        public Optional<Integer> getConnectTimeout() {
759                return connectTimeout;
760        }
761
762        public List<File> getPrivateKeys() {
763                return privateKeys;
764        }
765
766        public List<String> getPrivateKeyStrings() {
767                return privateKeyStrings;
768        }
769
770        public Properties getOptions() {
771                return options;
772        }
773
774}