blob: ce139cb16d9ed8016d5c44c271103cff6a3180da [file] [log] [blame]
// Copyright 2008-2010 Victor Iacoban
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software distributed under
// the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
// either express or implied. See the License for the specific language governing permissions and
// limitations under the License.
package org.zmlx.hg4idea.execution;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.zmlx.hg4idea.action.HgCommandResultNotifier;
import org.zmlx.hg4idea.util.HgErrorUtil;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.List;
public class HgRemoteCommandExecutor extends HgCommandExecutor {
@Nullable private ModalityState myState;
final boolean myIgnoreAuthorizationRequest;
public HgRemoteCommandExecutor(@NotNull Project project, @Nullable String destination) {
this(project, destination, null, false);
}
public HgRemoteCommandExecutor(@NotNull Project project, @Nullable String destination, boolean ignoreAuthorizationRequest) {
this(project, destination, null, ignoreAuthorizationRequest);
}
public HgRemoteCommandExecutor(@NotNull Project project,
@Nullable String destination,
@Nullable ModalityState state, boolean ignoreAuthorizationRequest) {
super(project, destination);
myState = state;
myIgnoreAuthorizationRequest = ignoreAuthorizationRequest;
}
@Nullable
public HgCommandResult executeInCurrentThread(@Nullable final VirtualFile repo, @NotNull final String operation,
@Nullable final List<String> arguments) {
HgCommandResult result = executeInCurrentThread(repo, operation, arguments, false);
if (!myIgnoreAuthorizationRequest && HgErrorUtil.isAuthorizationError(result)) {
if (HgErrorUtil.hasAuthorizationInDestinationPath(myDestination)) {
new HgCommandResultNotifier(myProject)
.notifyError(result, "Authorization failed", "Your hgrc file settings have wrong username or password in [paths].\n" +
"Please, update your .hg/hgrc file.");
return null;
}
result = executeInCurrentThread(repo, operation, arguments, true);
}
return result;
}
@Nullable
private HgCommandResult executeInCurrentThread(@Nullable final VirtualFile repo,
@NotNull final String operation,
@Nullable final List<String> arguments,
boolean forceAuthorization) {
PassReceiver passReceiver = new PassReceiver(myProject, forceAuthorization, myIgnoreAuthorizationRequest, myState);
SocketServer passServer = new SocketServer(passReceiver);
try {
int passPort = passServer.start();
HgCommandResult result = super.executeInCurrentThread(repo, operation, prepareArguments(arguments, passPort));
if (!HgErrorUtil.isAuthorizationError(result)) {
passReceiver.saveCredentials();
}
return result;
}
catch (IOException e) {
showError(e);
LOG.info("IOException during preparing command", e);
return null;
}
finally {
passServer.stop();
}
}
private List<String> prepareArguments(List<String> arguments, int port) {
List<String> cmdArguments = ContainerUtil.newArrayList();
cmdArguments.add("--config");
cmdArguments.add("extensions.hg4ideapromptextension=" + myVcs.getPromptHooksExtensionFile().getAbsolutePath());
cmdArguments.add("--config");
cmdArguments.add("hg4ideapass.port=" + port);
if (arguments != null && arguments.size() != 0) {
cmdArguments.addAll(arguments);
}
return cmdArguments;
}
@Override
protected void logCommand(@NotNull String operation, @Nullable List<String> arguments) {
//do not log arguments for remote command because of internal password port info etc
super.logCommand(operation, null);
}
private static class PassReceiver extends SocketServer.Protocol {
private final Project myProject;
private HgCommandAuthenticator myAuthenticator;
private boolean myForceAuthorization;
private boolean mySilentMode;
@Nullable private ModalityState myState;
private PassReceiver(Project project, boolean forceAuthorization, boolean silent, @Nullable ModalityState state) {
myProject = project;
myForceAuthorization = forceAuthorization;
mySilentMode = silent;
myState = state;
}
@Override
public boolean handleConnection(Socket socket) throws IOException {
//noinspection IOResourceOpenedButNotSafelyClosed
DataInputStream dataInputStream = new DataInputStream(socket.getInputStream());
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
String command = new String(readDataBlock(dataInputStream));
assert "getpass".equals(command) : "Invalid command: " + command;
String uri = new String(readDataBlock(dataInputStream));
String path = new String(readDataBlock(dataInputStream));
String proposedLogin = new String(readDataBlock(dataInputStream));
HgCommandAuthenticator authenticator = new HgCommandAuthenticator(myProject, myForceAuthorization, mySilentMode);
boolean ok = authenticator.promptForAuthentication(myProject, proposedLogin, uri, path, myState);
if (ok) {
myAuthenticator = authenticator;
sendDataBlock(out, authenticator.getUserName().getBytes());
sendDataBlock(out, authenticator.getPassword().getBytes());
}
return true;
}
public void saveCredentials() {
if (myAuthenticator == null) return;
myAuthenticator.saveCredentials();
}
}
}