diff --git a/plugins/hg4idea/resources/org/zmlx/hg4idea/HgVcsMessages.properties b/plugins/hg4idea/resources/org/zmlx/hg4idea/HgVcsMessages.properties index e7051d7106a5..b2f5d4db3c7c 100644 --- a/plugins/hg4idea/resources/org/zmlx/hg4idea/HgVcsMessages.properties +++ b/plugins/hg4idea/resources/org/zmlx/hg4idea/HgVcsMessages.properties @@ -116,5 +116,6 @@ hg4idea.merge.please-commit=Merged heads, please commit repository \"{0}\" hg4idea.error.invalidExecutable=\"{0}\" is not a valid mercurial executable hg4idea.integrate.other.head=Other head: {0} -hgidea.dialog.login.password.required=Login and password required +hg4idea.dialog.login.password.required=Login and password required +hg4idea.dialog.login.description=Login to {0} hg4idea.exception.file.not.under.hg=The file {0} is not under Mercurial. \ No newline at end of file diff --git a/plugins/hg4idea/resources/python/LICENSE.txt b/plugins/hg4idea/resources/python/LICENSE.txt new file mode 100644 index 000000000000..d159169d1050 --- /dev/null +++ b/plugins/hg4idea/resources/python/LICENSE.txt @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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; either version 2 of the License, or + (at your option) any later version. + + 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 Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/plugins/hg4idea/resources/python/README.txt b/plugins/hg4idea/resources/python/README.txt new file mode 100644 index 000000000000..7f27d21d4950 --- /dev/null +++ b/plugins/hg4idea/resources/python/README.txt @@ -0,0 +1,8 @@ +This Mercurial extension is meant to provide robust integration with +external tools. + +Mercurial sometimes asks vital questions during several operations, for +instance during merging. Automatically choosing the default answer +for these questions is typically not what you want when working from, +for instance, an IDE. This extension forwards the prompts to another +process (i.e. the IDE) so that it can be answered by the user. \ No newline at end of file diff --git a/plugins/hg4idea/resources/python/prompthooks.py b/plugins/hg4idea/resources/python/prompthooks.py index 9641d0cb6fdb..2507a9daac14 100644 --- a/plugins/hg4idea/resources/python/prompthooks.py +++ b/plugins/hg4idea/resources/python/prompthooks.py @@ -1,24 +1,50 @@ #!/usr/bin/env python -from mercurial import filemerge, ui, util, dispatch -from mercurial.node import short -import sys, struct, socket +#Mercurial extension to robustly integrate prompts with other processes +#Copyright (C) 2010-2011 Willem Verstraeten +# +#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; either version 2 +#of the License, or (at your option) any later version. +# +#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 Street, Fifth Floor, Boston, MA 02110-1301, USA. + +import urllib2 +from mercurial import ui, util +import struct, socket +from mercurial.i18n import _ + +try: + from mercurial.url import passwordmgr +except: + from mercurial.httprepo import passwordmgr def sendInt( client, number): length = struct.pack('>L', number) client.sendall( length ) def send( client, data ): - sendInt(client, len(data)) - client.sendall( data ) + if data is None: + sendInt(client, 0) + else: + sendInt(client, len(data)) + client.sendall( data ) -def receiveInt(client): +def receiveIntWithMessage(client, message): requiredLength = struct.calcsize('>L') buffer = '' while len(buffer) list of logins for this URL. Passwords are remembered in the PasswordSafe. - private Map> myRememberedUrls = new HashMap>(); + // visited URL -> login for this URL. Passwords are remembered in the PasswordSafe. + private Map myRememberedUserNames = new HashMap(); private boolean myRunViaBash; public static class State { @@ -57,12 +55,13 @@ public class HgGlobalSettings implements PersistentStateComponent> getRememberedUrls() { - return myRememberedUrls; + @Nullable + public String getRememberedUserName(@NotNull String stringUrl) { + return myRememberedUserNames.get(stringUrl); } /** @@ -77,12 +76,7 @@ public class HgGlobalSettings implements PersistentStateComponent list = myRememberedUrls.get(stringUrl); - if (list == null) { - list = new LinkedList(); - myRememberedUrls.put(stringUrl, list); - } - list.add(username); + myRememberedUserNames.put(stringUrl, username); } public static String getDefaultExecutable() { diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/command/HgCloneCommand.java b/plugins/hg4idea/src/org/zmlx/hg4idea/command/HgCloneCommand.java index 1384ddc2f528..8380ba1baa0a 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/command/HgCloneCommand.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/command/HgCloneCommand.java @@ -3,10 +3,7 @@ package org.zmlx.hg4idea.command; import com.intellij.openapi.project.Project; import org.jetbrains.annotations.Nullable; -import java.nio.charset.Charset; import java.util.ArrayList; -import java.util.Collections; -import java.util.LinkedList; import java.util.List; public class HgCloneCommand { @@ -14,7 +11,6 @@ public class HgCloneCommand { private String repositoryURL; private String directory; - private final HgCommandAuthenticator authenticator = new HgCommandAuthenticator(); public HgCloneCommand(Project project) { this.project = project; @@ -33,6 +29,6 @@ public class HgCloneCommand { final List arguments = new ArrayList(2); arguments.add(repositoryURL); arguments.add(directory); - return authenticator.executeCommandAndAuthenticateIfNecessary(project, null, repositoryURL, "clone", arguments); + return HgCommandService.getInstance(project).execute(null, "clone", arguments); } } diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/command/HgCommandAuthenticator.java b/plugins/hg4idea/src/org/zmlx/hg4idea/command/HgCommandAuthenticator.java index 5007815066f6..b0560e9f85d9 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/command/HgCommandAuthenticator.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/command/HgCommandAuthenticator.java @@ -19,22 +19,14 @@ import com.intellij.ide.passwordSafe.impl.PasswordSafeImpl; import com.intellij.ide.passwordSafe.impl.PasswordSafeProvider; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.util.ui.UIUtil; -import com.intellij.vcsUtil.VcsUtil; import org.apache.commons.lang.StringUtils; import org.jetbrains.annotations.Nullable; import org.zmlx.hg4idea.HgGlobalSettings; -import org.zmlx.hg4idea.HgUtil; import org.zmlx.hg4idea.HgVcs; import org.zmlx.hg4idea.ui.HgUsernamePasswordDialog; -import java.net.URISyntaxException; -import java.util.List; -import java.util.Map; - -import static org.zmlx.hg4idea.command.HgErrorUtil.isAbort; -import static org.zmlx.hg4idea.command.HgErrorUtil.isAuthorizationError; +import java.awt.*; +import java.lang.reflect.InvocationTargetException; /** * Base class for any command interacting with a remote repository and which needs authentication. @@ -42,192 +34,132 @@ import static org.zmlx.hg4idea.command.HgErrorUtil.isAuthorizationError; class HgCommandAuthenticator { private static final Logger LOG = Logger.getInstance(HgCommandAuthenticator.class.getName()); + + private GetPasswordRunnable myRunnable; + private final Project myProject; - @Nullable - protected HgCommandResult executeCommandAndAuthenticateIfNecessary(Project project, VirtualFile localRepository, String remoteRepository, String command, List arguments) { - return executeCommandAndAuthenticateIfNecessary(project, localRepository, remoteRepository, command, arguments, 0); - } - /** - * Tries to execute an hg command and analyzes the output. - * If authentication is needed, watches to the PasswordSafe for the saved password. Otherwise asks the user. - * Repeats it 3 times to give a chance to retry. - * @param project - * @param localRepository - * @param remoteRepository - * @param command - * @param arguments - * @param urlPosition - * @return - */ - @Nullable - protected HgCommandResult executeCommandAndAuthenticateIfNecessary(Project project, VirtualFile localRepository, String remoteRepository, String command, List arguments, int urlArgumentPosition) { - HgCommandResult result = HgCommandService.getInstance(project).execute(localRepository, command, arguments); // try to execute without authentication data - if (isAuthorizationError(result)) { - try { - // get auth data from password safe of from user and inject to the url - HgUrl hgUrl = new HgUrl(remoteRepository); - if (hgUrl.supportsAuthentication()) { - final GetPasswordRunnable runnable = new GetPasswordRunnable(project, hgUrl); - for (int i = 0; i < 3; i++) { - if (i == 1) { - runnable.setForceShowDialog(true); // first time try to get info from password safe if it's there, next time don't even try, - // because it means that the saved data didn't pass the authentication - } - result = tryToAuthenticate(project, localRepository, hgUrl, runnable, command, arguments, urlArgumentPosition); - if (result == HgCommandResult.CANCELLED) { - return result; - } - if (isAbort(result)) { - if (isAuthorizationError(result)) { - continue; - } else { - return result; - } - } - saveCredentials(project, runnable); - return result; - } - HgUtil.notifyError(project, "Authentication failed", "Authentication to " + remoteRepository + " failed"); - return result; - } else { - HgUtil.notifyError(project, "Authentication error", "Authentication was requested, but " + hgUrl.getScheme() + " doesn't support it."); - } - } catch (URISyntaxException e) { - VcsUtil.showErrorMessage(project, "Invalid repository: " + remoteRepository, "Error"); - } - } - return result; + public HgCommandAuthenticator(Project project) { + myProject = project; } - /** - * Shows the auth dialog if needed, fills authentication data to the given hgurl and returns the result of command execution. - * NB: hgUrl is modified - */ - @Nullable - private static HgCommandResult tryToAuthenticate(Project project, VirtualFile localRepository, HgUrl hgUrl, GetPasswordRunnable runnable, String command, List arguments, int urlArgumentPosition) throws URISyntaxException { - HgCommandService service = HgCommandService.getInstance(project); + public void saveCredentials() { + if (myRunnable == null) return; - UIUtil.invokeAndWaitIfNeeded(runnable); - if (runnable.isOk()) { - hgUrl.setUsername(runnable.getUserName()); - hgUrl.setPassword(String.valueOf(runnable.getPassword())); - } else { - return HgCommandResult.CANCELLED; - } - - arguments.set(urlArgumentPosition, hgUrl.asString()); - return service.execute(localRepository, command, arguments); - } - - private static void saveCredentials(Project project, GetPasswordRunnable runnable) { final PasswordSafeImpl passwordSafe = (PasswordSafeImpl)PasswordSafe.getInstance(); if (passwordSafe.getSettings().getProviderType().equals(PasswordSafeSettings.ProviderType.DO_NOT_STORE)) { return; } - final String key = keyForUrlAndLogin(runnable.getURL(), runnable.getUserName()); + final String key = keyForUrlAndLogin(myRunnable.getURL(), myRunnable.getUserName()); - final PasswordSafeProvider provider = runnable.isRememberPassword() ? passwordSafe.getMasterKeyProvider() : passwordSafe.getMemoryProvider(); + final PasswordSafeProvider provider = + myRunnable.isRememberPassword() ? passwordSafe.getMasterKeyProvider() : passwordSafe.getMemoryProvider(); try { - provider.storePassword(project, HgCommandAuthenticator.class, key, runnable.getPassword()); - final HgVcs vcs = HgVcs.getInstance(project); + provider.storePassword(myProject, HgCommandAuthenticator.class, key, myRunnable.getPassword()); + final HgVcs vcs = HgVcs.getInstance(myProject); if (vcs != null) { - vcs.getGlobalSettings().addRememberedUrl(runnable.getURL(), runnable.getUserName()); + vcs.getGlobalSettings().addRememberedUrl(myRunnable.getURL(), myRunnable.getUserName()); } - } catch (PasswordSafeException e) { + } + catch (PasswordSafeException e) { LOG.info("Couldn't store the password for key [" + key + "]", e); } } - private static class GetPasswordRunnable implements Runnable { + public boolean promptForAuthentication(Project project, String proposedLogin, String uri, String path) { + GetPasswordRunnable runnable = new GetPasswordRunnable(project, proposedLogin, uri, path); + // Don't use Application#invokeAndWait here, as IntelliJ + // may already be showing a dialog (such as the clone dialog) + try { + EventQueue.invokeAndWait(runnable); // TODO: use ApplicationManager.getApplication() with correct modality state. + myRunnable = runnable; + return runnable.isOk(); + } + catch (InterruptedException e) { + return false; + } + catch (InvocationTargetException e) { + throw new RuntimeException(e); + } + } - private final HgUrl hgUrl; - private String userName; - private String myPassword; - private Project project; - private boolean myForceShowDialog; - private boolean ok = false; + public String getUserName() { + return myRunnable.getUserName(); + } + + public String getPassword() { + return myRunnable.getPassword(); + } + + private static class GetPasswordRunnable implements Runnable { private static final Logger LOG = Logger.getInstance(GetPasswordRunnable.class.getName()); - private String myURL; + + private String myUserName; + private String myPassword; + private Project myProject; + private final String myProposedLogin; + private boolean ok = false; + @Nullable private String myURL; private boolean myRememberPassword; - public GetPasswordRunnable(Project project, HgUrl hgUrl) { - this.hgUrl = hgUrl; - this.project = project; + public GetPasswordRunnable(Project project, String proposedLogin, String uri, String path) { + this.myProject = project; + this.myProposedLogin = proposedLogin; + this.myURL = uri + path; } - + public void run() { - // get the string representation of the url - @Nullable String stringUrl = null; - try { - stringUrl = hgUrl.asString(); - } - catch (URISyntaxException e) { - LOG.warn("Couldn't parse hgUrl: [" + hgUrl + "]", e); - } - // find if we've already been here - final HgVcs vcs = HgVcs.getInstance(project); + final HgVcs vcs = HgVcs.getInstance(myProject); if (vcs == null) { return; } final HgGlobalSettings hgGlobalSettings = vcs.getGlobalSettings(); - final Map> urls = hgGlobalSettings.getRememberedUrls(); - @Nullable List rememberedLoginsForUrl = urls.get(stringUrl); - - String login = hgUrl.getUsername(); - if (StringUtils.isBlank(login)) { - // find the last used login - if (rememberedLoginsForUrl != null && !rememberedLoginsForUrl.isEmpty()) { - login = rememberedLoginsForUrl.get(0); - } + @Nullable String rememberedLoginsForUrl = null; + if (!StringUtils.isBlank(myURL)) { + rememberedLoginsForUrl = hgGlobalSettings.getRememberedUserName(myURL); } - String password = hgUrl.getPassword(); - if (StringUtils.isBlank(password) && stringUrl != null) { + String login = myProposedLogin; + if (StringUtils.isBlank(login)) { + // find the last used login + login = rememberedLoginsForUrl; + } + + String password = null; + if (!StringUtils.isBlank(login) && myURL != null) { // if we've logged in with this login, search for password - final String key = keyForUrlAndLogin(stringUrl, login); + final String key = keyForUrlAndLogin(myURL, login); try { final PasswordSafeImpl passwordSafe = (PasswordSafeImpl)PasswordSafe.getInstance(); - password = passwordSafe.getMemoryProvider().getPassword(project, HgCommandAuthenticator.class, key); + password = passwordSafe.getMemoryProvider().getPassword(myProject, HgCommandAuthenticator.class, key); if (password == null && passwordSafe.getSettings().getProviderType().equals(PasswordSafeSettings.ProviderType.MASTER_PASSWORD)) { - password = passwordSafe.getMasterKeyProvider().getPassword(project, HgCommandAuthenticator.class, key); + password = passwordSafe.getMasterKeyProvider().getPassword(myProject, HgCommandAuthenticator.class, key); } } catch (PasswordSafeException e) { LOG.info("Couldn't get password for key [" + key + "]", e); } } - // don't show dialog if we can (not forced + both fields are known) - if (!myForceShowDialog && !StringUtils.isBlank(password) && !StringUtils.isBlank(login)) { - userName = login; + // don't show dialog if we don't have to (both fields are known) + if (!StringUtils.isBlank(password) && !StringUtils.isBlank(login)) { + myUserName = login; myPassword = password; ok = true; return; } - String url; - try { - url = hgUrl.asString(false); - } - catch (URISyntaxException e) { - url = null; - } - final HgUsernamePasswordDialog dialog = new HgUsernamePasswordDialog(project, url, login, password); + final HgUsernamePasswordDialog dialog = new HgUsernamePasswordDialog(myProject, myURL, login, password); dialog.show(); if (dialog.isOK()) { - userName = dialog.getUsername(); + myUserName = dialog.getUsername(); myPassword = dialog.getPassword(); - ok = true; - myRememberPassword = dialog.isRememberPassword(); - if (stringUrl != null) { - myURL = stringUrl; - } + ok = true; } } public String getUserName() { - return userName; + return myUserName; } public String getPassword() { @@ -242,17 +174,13 @@ class HgCommandAuthenticator { return myURL; } - public void setForceShowDialog(boolean forceShowDialog) { - myForceShowDialog = forceShowDialog; - } - public boolean isRememberPassword() { return myRememberPassword; } } private static String keyForUrlAndLogin(String stringUrl, String login) { - return stringUrl + login; + return login + ":" + stringUrl; } } diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/command/HgCommandService.java b/plugins/hg4idea/src/org/zmlx/hg4idea/command/HgCommandService.java index e5ad4ec72fa2..532f50e31027 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/command/HgCommandService.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/command/HgCommandService.java @@ -14,7 +14,6 @@ package org.zmlx.hg4idea.command; import com.intellij.execution.ui.ConsoleViewContentType; import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; @@ -22,11 +21,7 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.vcsUtil.VcsUtil; import org.apache.commons.lang.StringUtils; import org.jetbrains.annotations.Nullable; -import org.zmlx.hg4idea.HgExecutableValidator; -import org.zmlx.hg4idea.HgGlobalSettings; -import org.zmlx.hg4idea.HgUtil; -import org.zmlx.hg4idea.HgVcs; -import org.zmlx.hg4idea.HgVcsMessages; +import org.zmlx.hg4idea.*; import javax.swing.*; import java.awt.*; @@ -34,6 +29,7 @@ import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.IOException; +import java.lang.reflect.InvocationTargetException; import java.net.Socket; import java.net.URI; import java.nio.charset.Charset; @@ -44,7 +40,7 @@ import java.util.List; public final class HgCommandService { - private static File PROMPT_HOOKS_PLUGIN; + private File myPromptHooksExtensionFile; static final Logger LOG = Logger.getInstance(HgCommandService.class.getName()); @@ -60,8 +56,8 @@ public final class HgCommandService { public HgCommandService(Project project, HgGlobalSettings settings) { myProject = project; mySettings = settings; - if (PROMPT_HOOKS_PLUGIN == null) { - PROMPT_HOOKS_PLUGIN = HgUtil.getTemporaryPythonFile("prompthooks"); + if (myPromptHooksExtensionFile == null) { + myPromptHooksExtensionFile = HgUtil.getTemporaryPythonFile("prompthooks"); } myVcs = HgVcs.getInstance(myProject); LOG.assertTrue(myVcs != null); @@ -92,7 +88,17 @@ public final class HgCommandService { } @Nullable - HgCommandResult execute(VirtualFile repo, List hgOptions, String operation, List arguments, Charset charset, boolean silent) { + HgCommandResult execute(final VirtualFile repo, final List hgOptions, final String operation, final List arguments, final Charset charset, final boolean silent) { + return executeOffOfEDT(repo, hgOptions, operation, arguments, charset, silent); + } + + private HgCommandResult executeOffOfEDT(VirtualFile repo, + List hgOptions, + String operation, + List arguments, + Charset charset, + boolean silent) { +// assert !EventQueue.isDispatchThread(); if (myProject.isDisposed()) { return null; } @@ -104,21 +110,28 @@ public final class HgCommandService { cmdLine.add(repo.getPath()); } - SocketServer promptServer = new SocketServer(new PromptReceiver()); WarningReceiver warningReceiver = new WarningReceiver(); + PassReceiver passReceiver = new PassReceiver(myProject); + + SocketServer promptServer = new SocketServer(new PromptReceiver()); SocketServer warningServer = new SocketServer(warningReceiver); - if (PROMPT_HOOKS_PLUGIN == null) { + SocketServer passServer = new SocketServer(passReceiver); + + if (myPromptHooksExtensionFile == null) { throw new RuntimeException("Could not hook into the prompt mechanism of Mercurial"); } try { int promptPort = promptServer.start(); int warningPort = warningServer.start(); + int passPort = passServer.start(); cmdLine.add("--config"); - cmdLine.add("extensions.hg4ideapromptextension=" + PROMPT_HOOKS_PLUGIN.getAbsolutePath()); + cmdLine.add("extensions.hg4ideapromptextension=" + myPromptHooksExtensionFile.getAbsolutePath()); cmdLine.add("--config"); cmdLine.add("hg4ideaprompt.port=" + promptPort); cmdLine.add("--config"); cmdLine.add("hg4ideawarn.port=" + warningPort); + cmdLine.add("--config"); + cmdLine.add("hg4ideapass.port=" + passPort); // Other parts of the plugin count on the availability of the MQ extension, so make sure it is enabled cmdLine.add("--config"); @@ -138,6 +151,9 @@ public final class HgCommandService { try { String workingDir = repo != null ? repo.getPath() : null; result = shellCommand.execute(cmdLine, workingDir, charset); + if (!HgErrorUtil.isAuthorizationError(result)) { + passReceiver.saveCredentials(); + } } catch (ShellCommandException e) { if (!silent) { if (myValidator.checkExecutableAndNotifyIfNeeded()) { @@ -155,6 +171,7 @@ public final class HgCommandService { } finally { promptServer.stop(); warningServer.stop(); + passServer.stop(); } String warnings = warningReceiver.getWarnings(); result.setWarnings(warnings); @@ -241,6 +258,7 @@ public final class HgCommandService { public boolean handleConnection(Socket socket) throws IOException { DataInputStream dataInput = new DataInputStream(socket.getInputStream()); + DataOutputStream out = new DataOutputStream(socket.getOutputStream()); final String message = new String(readDataBlock(dataInput)); int numOfChoices = dataInput.readInt(); final Choice[] choices = new Choice[numOfChoices]; @@ -252,29 +270,32 @@ public final class HgCommandService { final Choice defaultChoice = choices[defaultChoiceInt]; final int[] index = new int[]{-1}; - ApplicationManager.getApplication().invokeAndWait(new Runnable() { - public void run() { - Window parent = ApplicationManager.getApplication().getComponent(Window.class); - index[0] = JOptionPane.showOptionDialog( - parent, - message, - "hg4idea", - JOptionPane.OK_CANCEL_OPTION, - JOptionPane.QUESTION_MESSAGE, - null, - choices, - defaultChoice); + try { + EventQueue.invokeAndWait(new Runnable() { + public void run() { + Window parent = ApplicationManager.getApplication().getComponent(Window.class); + index[0] = JOptionPane + .showOptionDialog(parent, message, "hg4idea", JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, choices, + defaultChoice); + } + }); + + int chosen = index[0]; + if (chosen == JOptionPane.CLOSED_OPTION) { + out.writeInt(-1); + } else { + out.writeInt(chosen); } - }, ModalityState.defaultModalityState()); - - int chosen = index[0]; - DataOutputStream out = new DataOutputStream(socket.getOutputStream()); - if (chosen == JOptionPane.CLOSED_OPTION) { - out.writeInt(-1); - } else { - out.writeInt(chosen); + return true; + } + catch (InterruptedException e) { + //do nothing + return true; + } + catch (InvocationTargetException e) { + //shouldn't happen + throw new RuntimeException(e); } - return true; } private static class Choice{ @@ -313,6 +334,39 @@ public final class HgCommandService { } } } + + private static class PassReceiver extends SocketServer.Protocol{ + private final Project myProject; + private HgCommandAuthenticator myAuthenticator; + private PassReceiver(Project project) { + myProject = project; + } + @Override + public boolean handleConnection(Socket socket) throws IOException { + DataInputStream dataInputStream = new DataInputStream(socket.getInputStream()); + DataOutputStream out = new DataOutputStream(socket.getOutputStream()); + + String command = new String(readDataBlock(dataInputStream)); + assert "getpass".equals(command); + String uri = new String(readDataBlock(dataInputStream)); + String path = new String(readDataBlock(dataInputStream)); + String proposedLogin = new String(readDataBlock(dataInputStream)); + + HgCommandAuthenticator authenticator = new HgCommandAuthenticator(myProject); + boolean ok = authenticator.promptForAuthentication(myProject, proposedLogin, uri, path); + 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(); + } + } } diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/command/HgIdentifyCommand.java b/plugins/hg4idea/src/org/zmlx/hg4idea/command/HgIdentifyCommand.java index dbc3d899cf79..cdc6cef72530 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/command/HgIdentifyCommand.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/command/HgIdentifyCommand.java @@ -3,16 +3,12 @@ package org.zmlx.hg4idea.command; import com.intellij.openapi.project.Project; import org.jetbrains.annotations.Nullable; -import java.nio.charset.Charset; -import java.util.Collection; -import java.util.Collections; import java.util.LinkedList; import java.util.List; public class HgIdentifyCommand { private final Project project; - private final HgCommandAuthenticator authenticator = new HgCommandAuthenticator(); private String source; public HgIdentifyCommand(Project project) { @@ -31,6 +27,6 @@ public class HgIdentifyCommand { public HgCommandResult execute() { final List arguments = new LinkedList(); arguments.add(source); - return authenticator.executeCommandAndAuthenticateIfNecessary(project, null, source, "identify", arguments); + return HgCommandService.getInstance(project).execute(null, "identify", arguments); } } diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/command/HgPullCommand.java b/plugins/hg4idea/src/org/zmlx/hg4idea/command/HgPullCommand.java index c5d1a3fb1287..097f35ca7e6a 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/command/HgPullCommand.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/command/HgPullCommand.java @@ -31,8 +31,6 @@ public class HgPullCommand { private boolean update = true; private boolean rebase = !update; - private final HgCommandAuthenticator authenticator = new HgCommandAuthenticator(); - public HgPullCommand(Project project, @NotNull VirtualFile repo) { this.project = project; this.repo = repo; @@ -69,7 +67,7 @@ public class HgPullCommand { arguments.add(source); - HgCommandResult result = authenticator.executeCommandAndAuthenticateIfNecessary(project, repo, source, "pull", arguments, arguments.size()-1); + HgCommandResult result = HgCommandService.getInstance(project).execute(repo, "pull", arguments); project.getMessageBus().syncPublisher(HgVcs.REMOTE_TOPIC).update(project); diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/command/HgPushCommand.java b/plugins/hg4idea/src/org/zmlx/hg4idea/command/HgPushCommand.java index 8a80c2cbc925..4ff847d10b27 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/command/HgPushCommand.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/command/HgPushCommand.java @@ -26,7 +26,6 @@ public class HgPushCommand { private final Project myProject; private final VirtualFile myRepo; private final String myDestination; - private final HgCommandAuthenticator authenticator = new HgCommandAuthenticator(); private String myRevision; private boolean myForce; @@ -65,7 +64,7 @@ public class HgPushCommand { } arguments.add(myDestination); - final HgCommandResult result = authenticator.executeCommandAndAuthenticateIfNecessary(myProject, myRepo, myDestination, "push", arguments, arguments.size()-1); + final HgCommandResult result = HgCommandService.getInstance(myProject).execute(myRepo, "push", arguments); myProject.getMessageBus().syncPublisher(HgVcs.REMOTE_TOPIC).update(myProject); return result; } diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/command/HgRemoteChangesetsCommand.java b/plugins/hg4idea/src/org/zmlx/hg4idea/command/HgRemoteChangesetsCommand.java index 5166952b3166..e08e68e4c7ec 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/command/HgRemoteChangesetsCommand.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/command/HgRemoteChangesetsCommand.java @@ -51,7 +51,7 @@ public abstract class HgRemoteChangesetsCommand extends HgChangesetsCommand { @Override protected HgCommandResult executeCommand(VirtualFile repo, List args) { String repositoryURL = new HgShowConfigCommand(project).getDefaultPath(repo); - HgCommandResult result = new HgCommandAuthenticator().executeCommandAndAuthenticateIfNecessary(project, repo, repositoryURL, command, args, args.size()-1); + HgCommandResult result = HgCommandService.getInstance(project).execute(repo, command, args); if (result == HgCommandResult.CANCELLED) { final HgVcs vcs = HgVcs.getInstance(project); Notifications.Bus.notify(new Notification(HgVcs.NOTIFICATION_GROUP_ID, "Checking for incoming/outgoing changes disabled", diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/command/SocketServer.java b/plugins/hg4idea/src/org/zmlx/hg4idea/command/SocketServer.java index 51bcf40eda9b..d76180b59b59 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/command/SocketServer.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/command/SocketServer.java @@ -36,15 +36,19 @@ public class SocketServer { serverSocket = new ServerSocket(0); int port = serverSocket.getLocalPort(); - String name = "hg4idea prompt server"; - serverThread = new Thread(name) { + serverThread = new Thread("hg4idea prompt server") { @Override public void run() { try { boolean _continue = true; while (_continue) { Socket socket = serverSocket.accept(); - _continue = protocol.handleConnection(socket); + try { + _continue = protocol.handleConnection(socket); + } + finally { + socket.close(); + } } } catch (SocketException e) { //socket was closed, that's OK diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/provider/HgCheckoutProvider.java b/plugins/hg4idea/src/org/zmlx/hg4idea/provider/HgCheckoutProvider.java index 2b3207948431..b8690e89d8a1 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/provider/HgCheckoutProvider.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/provider/HgCheckoutProvider.java @@ -78,107 +78,27 @@ public class HgCheckoutProvider implements CheckoutProvider { clone.setDirectory(targetDir); // handle result - try { - final HgCommandResult myCloneResult = clone.execute(); - if (myCloneResult == null) { - notifyError("Clone failed", "Clone failed due to unknown error", project); - } else if (myCloneResult.getExitValue() != 0) { - notifyError("Clone failed", "Clone from " + sourceRepositoryURL + " failed.

" + myCloneResult.getRawError(), project); - } else { - ApplicationManager.getApplication().invokeLater(new Runnable() { - @Override - public void run() { - if (listener != null) { - listener.directoryCheckedOut(new File(dialog.getParentDirectory(), dialog.getDirectoryName())); - listener.checkoutCompleted(); - } + final HgCommandResult myCloneResult = clone.execute(); + if (myCloneResult == null) { + notifyError("Clone failed", "Clone failed due to unknown error", project); + } else if (myCloneResult.getExitValue() != 0) { + notifyError("Clone failed", "Clone from " + sourceRepositoryURL + " failed.

" + myCloneResult.getRawError(), project); + } else { + ApplicationManager.getApplication().invokeLater(new Runnable() { + @Override + public void run() { + if (listener != null) { + listener.directoryCheckedOut(new File(dialog.getParentDirectory(), dialog.getDirectoryName())); + listener.checkoutCompleted(); } - }); - } - } finally { - cleanupAuthDataFromHgrc(targetDir); + } + }); } } }.queue(); } - /** - * Removes authentication data from the parent URL of a just cloned repository. - * @param targetDir directory where the hg project was cloned into. - */ - private static void cleanupAuthDataFromHgrc(String targetDir) { - File hgrc = new File(new File(targetDir, ".hg"), "hgrc"); - if (!hgrc.exists()) { - return; - } - - for (int i = 0; i < 3; i++) { // 3 attempts in case of an IOException - BufferedReader reader = null; - PrintWriter writer = null; - try { - // writing correct info into a temporary file - final File tempFile = FileUtil.createTempFile("hgrc", "temp"); - tempFile.deleteOnExit(); - reader = new BufferedReader(new FileReader(hgrc)); - writer = new PrintWriter(new FileWriter(tempFile)); - String line; - while ((line = reader.readLine()) != null) { - String parseLine = line.trim(); - if (parseLine.startsWith("default") && parseLine.contains("@")) { // looking for paths.default - int eqIdx = parseLine.indexOf('='); - parseLine = parseLine.substring(eqIdx+1).trim(); // getting value of paths.default - try { - final URI uri = new URI(parseLine); - final String userInfo = uri.getUserInfo(); - if (userInfo == null) { // no user info => OK - writer.println(line); - continue; - } - int colonIdx = userInfo.indexOf(':'); - if (colonIdx == -1) { // no password given => OK - writer.println(line); - continue; - } - final String urlWithoutAuthData = uri.toString().replace(userInfo, userInfo.substring(0, colonIdx)); // remove password, leave username - writer.println("default = " + urlWithoutAuthData); - } catch (Throwable t) { // not URI => no sensitive data - writer.println(line); - } - } else { - writer.println(line); - } - } - reader.close(); - writer.close(); - - // substituting files - try { - if (!tempFile.renameTo(hgrc)) { // this may fail in case of different FSs - FileUtil.copy(tempFile, hgrc); - FileUtil.delete(tempFile); - } - } catch (Throwable e) { - FileUtil.copy(tempFile, hgrc); - FileUtil.delete(tempFile); - } - return; - } catch (IOException e) { - LOG.info(e); - } finally { - if (reader != null) { - try { - reader.close(); - } catch (IOException e) { - } - } - if (writer != null) { - writer.close(); - } - } - } - } - private static void notifyError(String title, String description, Project project) { Notifications.Bus.notify(new Notification(HgVcs.NOTIFICATION_GROUP_ID, title, description, NotificationType.ERROR), project); } diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgCloneDialog.java b/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgCloneDialog.java index 2986e714f03a..1237e5bd2c3d 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgCloneDialog.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgCloneDialog.java @@ -15,11 +15,15 @@ */ package org.zmlx.hg4idea.ui; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.fileChooser.FileChooserDescriptor; +import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.*; import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.annotations.NotNull; import org.zmlx.hg4idea.HgVcsMessages; import org.zmlx.hg4idea.command.HgCommandResult; import org.zmlx.hg4idea.command.HgIdentifyCommand; @@ -180,21 +184,22 @@ public class HgCloneDialog extends DialogWrapper { testButton.addActionListener(new ActionListener() { public void actionPerformed(final ActionEvent e) { testURL = repositoryURL.getText(); - final boolean finished = ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable() { - public void run() { + ProgressManager.getInstance().run(new Task.Backgroundable(project,HgVcsMessages.message("hg4idea.clone.test.progress", testURL), true) { + @Override + public void run(@NotNull ProgressIndicator indicator) { testResult = testRepository(project, testURL); + ApplicationManager.getApplication().invokeLater(new Runnable() { + @Override + public void run() { + if (testResult) { + Messages.showInfoMessage(testButton, HgVcsMessages.message("hg4idea.clone.test.success.message", testURL), + HgVcsMessages.message("hg4idea.clone.test.success")); + } + updateCloneButton(); + } + }); } - }, HgVcsMessages.message("hg4idea.clone.test.progress", testURL), true, project, clonePanel); - - if (!finished) { - return; - } - - if (testResult) { - Messages.showInfoMessage(testButton, HgVcsMessages.message("hg4idea.clone.test.success.message", testURL), - HgVcsMessages.message("hg4idea.clone.test.success")); - } - updateCloneButton(); + }); } }); diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgUsernamePasswordDialog.java b/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgUsernamePasswordDialog.java index 8980b31f7a3c..9ba12fd92715 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgUsernamePasswordDialog.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/ui/HgUsernamePasswordDialog.java @@ -25,9 +25,9 @@ public class HgUsernamePasswordDialog extends DialogWrapper { public HgUsernamePasswordDialog(Project project, String url, String login, String password) { super(project, false); - setTitle(HgVcsMessages.message("hgidea.dialog.login.password.required")); - final String desc = (url == null ? null : "Login to " + url); - authPanel = new AuthenticationPanel(desc, login, password, false); + setTitle(HgVcsMessages.message("hg4idea.dialog.login.password.required")); + authPanel = new AuthenticationPanel(HgVcsMessages.message("hg4idea.dialog.login.description", url), login, password, + !StringUtils.isBlank(password)); init(); } @@ -52,4 +52,4 @@ public class HgUsernamePasswordDialog extends DialogWrapper { return authPanel.isRememberPassword(); } -} \ No newline at end of file +}