/// <summary> /// Connect to the remote servers, with the details from Profile /// </summary> /// <param name="reconnecting">True if this is an attempt to re-establish a closed connection</param> public void Connect(bool reconnecting = false) { Notifications.ChangeTrayText(reconnecting ? MessageType.Reconnecting : MessageType.Connecting); Log.Write(l.Debug, "{0} client...", reconnecting ? "Reconnecting" : "Connecting"); if (FTP) { _ftpc = new FtpClient { Host = controller.Account.Host, Port = controller.Account.Port }; // Add accepted certificates _ftpc.ClientCertificates.AddRange(Certificates); if (controller.Account.Protocol == FtpProtocol.FTPS) { _ftpc.ValidateCertificate += (sender, x) => { var fingerPrint = new X509Certificate2(x.Certificate).Thumbprint; if (_ftpc.ClientCertificates.Count <= 0 && x.PolicyErrors != SslPolicyErrors.None) { Certificates.Add(x.Certificate); x.Accept = false; return; } // if ValidateCertificate handler isn't set, accept the certificate and move on if (ValidateCertificate == null || Settings.TrustedCertificates.Contains(fingerPrint)) { Log.Write(l.Client, "Trusted: {0}", fingerPrint); x.Accept = true; return; } var e = new ValidateCertificateEventArgs { Fingerprint = fingerPrint, SerialNumber = x.Certificate.GetSerialNumberString(), Algorithm = x.Certificate.GetKeyAlgorithmParametersString(), ValidFrom = x.Certificate.GetEffectiveDateString(), ValidTo = x.Certificate.GetExpirationDateString(), Issuer = x.Certificate.Issuer }; // Prompt user to validate ValidateCertificate(null, e); x.Accept = e.IsTrusted; }; // Change Security Protocol if (controller.Account.FtpsMethod == FtpsMethod.Explicit) { _ftpc.EncryptionMode = FtpEncryptionMode.Explicit; } else if (controller.Account.FtpsMethod == FtpsMethod.Implicit) { _ftpc.EncryptionMode = FtpEncryptionMode.Implicit; } } _ftpc.Credentials = new NetworkCredential(controller.Account.Username, controller.Account.Password); try { _ftpc.Connect(); } catch (Exception exc) { // Since the ClientCertificates are added when accepted in ValidateCertificate, the first // attempt to connect will fail with an AuthenticationException. If this is the case, a // re-connect is attempted, this time with the certificates properly set. // This is a workaround to avoid storing Certificate files locally... if (exc is System.Security.Authentication.AuthenticationException && _ftpc.ClientCertificates.Count <= 0) { Connect(); } else { throw; } } } else // SFTP { ConnectionInfo connectionInfo; if (controller.isPrivateKeyValid) { connectionInfo = new PrivateKeyConnectionInfo(controller.Account.Host, controller.Account.Port, controller.Account.Username, new PrivateKeyFile(controller.Account.PrivateKeyFile, controller.Account.Password)); } else { connectionInfo = new PasswordConnectionInfo(controller.Account.Host, controller.Account.Port, controller.Account.Username, controller.Account.Password); } _sftpc = new SftpClient(connectionInfo); _sftpc.ConnectionInfo.AuthenticationBanner += (o, x) => Log.Write(l.Warning, x.BannerMessage); _sftpc.HostKeyReceived += (o, x) => { var fingerPrint = x.FingerPrint.GetCertificateData(); // if ValidateCertificate handler isn't set, accept the certificate and move on if (ValidateCertificate == null || Settings.TrustedCertificates.Contains(fingerPrint)) { Log.Write(l.Client, "Trusted: {0}", fingerPrint); x.CanTrust = true; return; } var e = new ValidateCertificateEventArgs { Fingerprint = fingerPrint, Key = x.HostKeyName, KeySize = x.KeyLength.ToString() }; // Prompt user to validate ValidateCertificate(null, e); x.CanTrust = e.IsTrusted; }; _sftpc.Connect(); _sftpc.ErrorOccurred += (o, e) => { if (!isConnected) { Notifications.ChangeTrayText(MessageType.Nothing); } if (ConnectionClosed != null) { ConnectionClosed(null, new ConnectionClosedEventArgs { Text = e.Exception.Message }); } if (e.Exception is Renci.SshNet.Common.SftpPermissionDeniedException) { Log.Write(l.Warning, "Permission denied error occured"); } if (e.Exception is Renci.SshNet.Common.SshConnectionException) { Reconnect(); } }; } controller.HomePath = WorkingDirectory; if (isConnected) { if (!string.IsNullOrWhiteSpace(controller.Paths.Remote) && !controller.Paths.Remote.Equals("/")) { WorkingDirectory = controller.Paths.Remote; } } Log.Write(l.Debug, "Client connected sucessfully"); Notifications.ChangeTrayText(MessageType.Ready); if (Settings.IsDebugMode) { LogServerInfo(); } // Periodically send NOOP (KeepAlive) to server if a non-zero interval is set SetKeepAlive(); }