Fix transcript in transfer window.
[cyberduck.git] / source / ch / cyberduck / ui / cocoa / PromptLoginController.java
blob1da43941aebb3e877b9159d59177ee23dae3d5ed
1 package ch.cyberduck.ui.cocoa;
3 /*
4 * Copyright (c) 2005 David Kocher. All rights reserved.
5 * http://cyberduck.ch/
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * Bug fixes, suggestions and comments should be sent to:
18 * dkocher@cyberduck.ch
21 import ch.cyberduck.binding.application.NSAlert;
22 import ch.cyberduck.binding.application.NSButton;
23 import ch.cyberduck.binding.application.NSCell;
24 import ch.cyberduck.binding.application.NSColor;
25 import ch.cyberduck.binding.application.NSControl;
26 import ch.cyberduck.binding.application.NSImage;
27 import ch.cyberduck.binding.application.NSImageView;
28 import ch.cyberduck.binding.application.NSOpenPanel;
29 import ch.cyberduck.binding.application.NSSecureTextField;
30 import ch.cyberduck.binding.application.NSTextField;
31 import ch.cyberduck.binding.application.NSWindow;
32 import ch.cyberduck.binding.application.SheetCallback;
33 import ch.cyberduck.binding.foundation.NSAttributedString;
34 import ch.cyberduck.binding.foundation.NSNotification;
35 import ch.cyberduck.binding.foundation.NSNotificationCenter;
36 import ch.cyberduck.binding.foundation.NSObject;
37 import ch.cyberduck.core.Credentials;
38 import ch.cyberduck.core.DefaultProviderHelpService;
39 import ch.cyberduck.core.Host;
40 import ch.cyberduck.core.HostPasswordStore;
41 import ch.cyberduck.core.Local;
42 import ch.cyberduck.core.LocalFactory;
43 import ch.cyberduck.core.LocaleFactory;
44 import ch.cyberduck.core.LoginCallback;
45 import ch.cyberduck.core.LoginOptions;
46 import ch.cyberduck.core.PasswordStoreFactory;
47 import ch.cyberduck.core.Protocol;
48 import ch.cyberduck.core.Scheme;
49 import ch.cyberduck.core.StringAppender;
50 import ch.cyberduck.core.exception.LoginCanceledException;
51 import ch.cyberduck.core.preferences.Preferences;
52 import ch.cyberduck.core.preferences.PreferencesFactory;
53 import ch.cyberduck.core.resources.IconCacheFactory;
55 import org.apache.commons.lang3.StringUtils;
56 import org.apache.log4j.Logger;
57 import org.rococoa.Foundation;
59 /**
60 * @version $Id$
62 public final class PromptLoginController implements LoginCallback {
63 private static final Logger log = Logger.getLogger(PromptLoginController.class);
65 private HostPasswordStore keychain
66 = PasswordStoreFactory.get();
68 private Preferences preferences
69 = PreferencesFactory.get();
71 private WindowController parent;
73 public PromptLoginController(final WindowController parent) {
74 this.parent = parent;
77 @Override
78 public void warn(final Protocol protocol, final String title, final String message,
79 final String continueButton, final String disconnectButton, final String preference)
80 throws LoginCanceledException {
81 if(log.isDebugEnabled()) {
82 log.debug(String.format("Display insecure connection alert for %s", protocol));
84 final NSAlert alert = NSAlert.alert(title, message,
85 continueButton, // Default Button
86 null, // Alternate button
87 disconnectButton // Other
89 alert.setShowsHelp(true);
90 alert.setShowsSuppressionButton(true);
91 alert.suppressionButton().setTitle(LocaleFactory.localizedString("Don't show again", "Credentials"));
92 alert.setAlertStyle(NSAlert.NSWarningAlertStyle);
93 final StringBuilder site = new StringBuilder(preferences.getProperty("website.help"));
94 site.append("/").append(protocol.getScheme().name());
95 int option = parent.alert(alert, site.toString());
96 if(alert.suppressionButton().state() == NSCell.NSOnState) {
97 // Never show again.
98 preferences.setProperty(preference, true);
100 switch(option) {
101 case SheetCallback.CANCEL_OPTION:
102 throw new LoginCanceledException();
104 //Proceed nevertheless.
107 @Override
108 public void prompt(final Host bookmark, final Credentials credentials,
109 final String title, final String reason,
110 final LoginOptions options) throws LoginCanceledException {
111 if(log.isDebugEnabled()) {
112 log.debug(String.format("Prompt for credentials for %s", bookmark));
114 final SheetController sheet = new SheetController(parent) {
115 @Override
116 protected String getBundleName() {
117 return "Login";
120 @Override
121 public void awakeFromNib() {
122 this.update();
123 this.window().makeFirstResponder(usernameField);
124 super.awakeFromNib();
127 @Override
128 public void helpButtonClicked(NSButton sender) {
129 new DefaultProviderHelpService().help(bookmark.getProtocol());
132 @Outlet
133 protected NSImageView iconView;
135 public void setIconView(NSImageView iconView) {
136 this.iconView = iconView;
137 this.iconView.setImage(IconCacheFactory.<NSImage>get().iconNamed(bookmark.getProtocol().disk()));
140 @Outlet
141 private NSTextField usernameLabel;
143 public void setUsernameLabel(NSTextField usernameLabel) {
144 this.usernameLabel = usernameLabel;
145 this.usernameLabel.setAttributedStringValue(NSAttributedString.attributedStringWithAttributes(
146 StringUtils.isNotBlank(credentials.getUsernamePlaceholder()) ? String.format("%s:",
147 credentials.getUsernamePlaceholder()) : StringUtils.EMPTY,
148 TRUNCATE_MIDDLE_ATTRIBUTES));
151 @Outlet
152 private NSTextField passwordLabel;
154 public void setPasswordLabel(NSTextField passwordLabel) {
155 this.passwordLabel = passwordLabel;
158 @Outlet
159 private NSTextField titleField;
161 public void setTitleField(NSTextField titleField) {
162 this.titleField = titleField;
163 this.updateField(this.titleField, LocaleFactory.localizedString(title, "Credentials"));
166 @Outlet
167 private NSTextField usernameField;
169 public void setUsernameField(NSTextField usernameField) {
170 this.usernameField = usernameField;
171 this.updateField(this.usernameField, credentials.getUsername());
172 NSNotificationCenter.defaultCenter().addObserver(this.id(),
173 Foundation.selector("userFieldTextDidChange:"),
174 NSControl.NSControlTextDidChangeNotification,
175 this.usernameField);
178 public void userFieldTextDidChange(NSNotification notification) {
179 credentials.setUsername(usernameField.stringValue());
180 if(StringUtils.isNotBlank(credentials.getUsername())) {
181 final String password = keychain.getPassword(bookmark.getProtocol().getScheme(), bookmark.getPort(),
182 bookmark.getHostname(), credentials.getUsername());
183 if(StringUtils.isNotBlank(password)) {
184 passwordField.setStringValue(password);
185 this.passFieldTextDidChange(notification);
188 this.update();
191 @Outlet
192 private NSTextField textField;
194 public void setTextField(NSTextField textField) {
195 this.textField = textField;
196 this.textField.setSelectable(true);
197 if(reason.startsWith(Scheme.http.name())) {
198 // For OAuth2
199 this.textField.setAttributedStringValue(HyperlinkAttributedStringFactory.create(reason));
200 this.textField.setAllowsEditingTextAttributes(true);
201 this.textField.setSelectable(true);
203 else {
204 this.updateField(this.textField, new StringAppender().append(reason).toString());
208 @Outlet
209 private NSSecureTextField passwordField;
211 public void setPasswordField(NSSecureTextField passwordField) {
212 this.passwordField = passwordField;
213 this.updateField(this.passwordField, credentials.getPassword());
214 NSNotificationCenter.defaultCenter().addObserver(this.id(),
215 Foundation.selector("passFieldTextDidChange:"),
216 NSControl.NSControlTextDidChangeNotification,
217 this.passwordField);
220 public void passFieldTextDidChange(NSNotification notification) {
221 credentials.setPassword(passwordField.stringValue());
224 @Outlet
225 private NSButton keychainCheckbox;
227 public void setKeychainCheckbox(NSButton keychainCheckbox) {
228 this.keychainCheckbox = keychainCheckbox;
229 this.keychainCheckbox.setTarget(this.id());
230 this.keychainCheckbox.setAction(Foundation.selector("keychainCheckboxClicked:"));
231 this.keychainCheckbox.setState(preferences.getBoolean("connection.login.useKeychain")
232 && preferences.getBoolean("connection.login.addKeychain") ? NSCell.NSOnState : NSCell.NSOffState);
235 public void keychainCheckboxClicked(final NSButton sender) {
236 final boolean enabled = sender.state() == NSCell.NSOnState;
237 preferences.setProperty("connection.login.addKeychain", enabled);
240 @Outlet
241 private NSButton anonymousCheckbox;
243 public void setAnonymousCheckbox(NSButton anonymousCheckbox) {
244 this.anonymousCheckbox = anonymousCheckbox;
245 this.anonymousCheckbox.setTarget(this.id());
246 this.anonymousCheckbox.setAction(Foundation.selector("anonymousCheckboxClicked:"));
249 @Action
250 public void anonymousCheckboxClicked(final NSButton sender) {
251 if(sender.state() == NSCell.NSOnState) {
252 credentials.setUsername(preferences.getProperty("connection.login.anon.name"));
253 credentials.setPassword(preferences.getProperty("connection.login.anon.pass"));
255 if(sender.state() == NSCell.NSOffState) {
256 credentials.setUsername(preferences.getProperty("connection.login.name"));
257 credentials.setPassword(null);
259 this.updateField(this.usernameField, credentials.getUsername());
260 this.updateField(this.passwordField, credentials.getPassword());
261 this.update();
264 @Outlet
265 private NSTextField pkLabel;
267 public void setPkLabel(NSTextField pkLabel) {
268 this.pkLabel = pkLabel;
271 @Outlet
272 private NSButton pkCheckbox;
274 public void setPkCheckbox(NSButton pkCheckbox) {
275 this.pkCheckbox = pkCheckbox;
276 this.pkCheckbox.setTarget(this.id());
277 this.pkCheckbox.setAction(Foundation.selector("pkCheckboxSelectionChanged:"));
280 @Action
281 public void pkCheckboxSelectionChanged(final NSButton sender) {
282 if(sender.state() == NSCell.NSOnState) {
283 try {
284 select(this, new SheetCallback() {
285 @Override
286 public void callback(final int returncode) {
287 if(returncode == SheetCallback.DEFAULT_OPTION) {
288 final NSObject selected = select.filenames().lastObject();
289 if(selected != null) {
290 credentials.setIdentity(LocalFactory.get(selected.toString()));
291 update();
297 catch(LoginCanceledException e) {
301 else {
302 credentials.setIdentity(null);
304 update();
307 private void update() {
308 this.usernameField.setEnabled(options.user && !credentials.isAnonymousLogin());
309 this.usernameField.cell().setPlaceholderString(credentials.getUsernamePlaceholder());
311 this.passwordField.setEnabled(options.password && !credentials.isAnonymousLogin());
312 this.passwordField.cell().setPlaceholderString(credentials.getPasswordPlaceholder());
314 this.passwordLabel.setAttributedStringValue(NSAttributedString.attributedStringWithAttributes(
315 StringUtils.isNotBlank(credentials.getPasswordPlaceholder()) ? String.format("%s:",
316 credentials.getPasswordPlaceholder()) : StringUtils.EMPTY,
317 TRUNCATE_MIDDLE_ATTRIBUTES));
319 boolean enable = options.keychain && !credentials.isAnonymousLogin();
320 this.keychainCheckbox.setEnabled(enable);
321 if(!enable) {
322 this.keychainCheckbox.setState(NSCell.NSOffState);
325 this.anonymousCheckbox.setEnabled(options.anonymous);
326 if(options.anonymous && credentials.isAnonymousLogin()) {
327 this.anonymousCheckbox.setState(NSCell.NSOnState);
329 else {
330 this.anonymousCheckbox.setState(NSCell.NSOffState);
332 this.pkCheckbox.setEnabled(options.publickey);
333 if(options.publickey && credentials.isPublicKeyAuthentication()) {
334 this.pkCheckbox.setState(NSCell.NSOnState);
335 this.updateField(this.pkLabel, credentials.getIdentity().getAbbreviatedPath(),
336 TRUNCATE_MIDDLE_ATTRIBUTES);
337 this.pkLabel.setTextColor(NSColor.textColor());
339 else {
340 this.pkCheckbox.setState(NSCell.NSOffState);
341 this.pkLabel.setStringValue(LocaleFactory.localizedString("No private key selected"));
342 this.pkLabel.setTextColor(NSColor.disabledControlTextColor());
346 @Override
347 protected boolean validateInput() {
348 credentials.setUsername(usernameField.stringValue());
349 credentials.setPassword(passwordField.stringValue());
350 return credentials.validate(bookmark.getProtocol(), options);
353 @Override
354 public void callback(final int returncode) {
355 if(returncode == SheetCallback.DEFAULT_OPTION) {
356 this.window().endEditingFor(null);
357 credentials.setSaved(keychainCheckbox.state() == NSCell.NSOnState);
358 credentials.setUsername(usernameField.stringValue());
359 credentials.setPassword(passwordField.stringValue());
363 sheet.beginSheet();
364 if(sheet.returnCode() == SheetCallback.CANCEL_OPTION) {
365 throw new LoginCanceledException();
369 private NSOpenPanel select;
371 public Local select(final Local identity) throws LoginCanceledException {
372 return this.select(parent, new SheetCallback() {
373 @Override
374 public void callback(final int returncode) {
380 protected Local select(final WindowController parent, final SheetCallback callback) throws LoginCanceledException {
381 final SheetController sheet = new SheetController(parent) {
382 @Override
383 public void callback(final int returncode) {
384 callback.callback(returncode);
387 @Override
388 protected void beginSheetImpl() {
389 select = NSOpenPanel.openPanel();
390 select.setCanChooseDirectories(false);
391 select.setCanChooseFiles(true);
392 select.setAllowsMultipleSelection(false);
393 select.setMessage(LocaleFactory.localizedString("Select the private key in PEM or PuTTY format", "Credentials"));
394 select.setPrompt(LocaleFactory.localizedString("Choose"));
395 select.beginSheetForDirectory(LocalFactory.get("~/.ssh").getAbsolute(),
396 null, parent.window(), this.id(), Foundation.selector("sheetDidClose:returnCode:contextInfo:"), null);
399 @Override
400 public NSWindow window() {
401 return select;
404 sheet.beginSheet();
405 if(sheet.returnCode() == SheetCallback.DEFAULT_OPTION) {
406 final NSObject selected = select.filenames().lastObject();
407 if(selected != null) {
408 return LocalFactory.get(selected.toString());
411 throw new LoginCanceledException();