mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[PATCH] Hg: use prompthook to deal with authentication
- added extra hook in prompthooks.py to retrieve username and password from IDE. - reworked command authenticator to no longer try to parse URL logic. Comment from Kirill Likhodedov: removed foxtrot library from the patch. Currently still executing hg commands in EDT. Fix will follow in the next commit.
This commit is contained in:
committed by
Kirill Likhodedov
parent
4a515dcda0
commit
246f584ac3
@@ -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.
|
||||
@@ -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.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
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.
|
||||
|
||||
<signature of Ty Coon>, 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.
|
||||
@@ -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.
|
||||
@@ -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)<requiredLength:
|
||||
chunk = client.recv(requiredLength-len(buffer))
|
||||
if chunk == '':
|
||||
raise RuntimeError, "socket connection broken"
|
||||
raise util.Abort( message )
|
||||
buffer = buffer + chunk
|
||||
|
||||
# struct.unpack always returns a tuple, even if that tuple only contains a single
|
||||
@@ -27,13 +53,20 @@ def receiveInt(client):
|
||||
|
||||
return intToReturn
|
||||
|
||||
|
||||
def receiveInt(client):
|
||||
return receiveIntWithMessage(client, "could not get information from server")
|
||||
|
||||
def receive( client ):
|
||||
length = receiveInt(client)
|
||||
receiveWithMessage(client, "could not get information from server")
|
||||
|
||||
def receiveWithMessage( client, message ):
|
||||
length = receiveIntWithMessage(client, message)
|
||||
buffer = ''
|
||||
while len(buffer) < length :
|
||||
chunk = client.recv(length - len(buffer))
|
||||
if chunk == '':
|
||||
raise RuntimeError, "socket connection broken"
|
||||
raise util.Abort( message)
|
||||
buffer = buffer+chunk
|
||||
|
||||
return buffer
|
||||
@@ -45,14 +78,14 @@ def monkeypatch_method(cls):
|
||||
return func
|
||||
return decorator
|
||||
|
||||
def sendchangestoidea(ui, msg, choices, default):
|
||||
def sendchoicestoidea(ui, msg, choices, default):
|
||||
port = int(ui.config( 'hg4ideaprompt', 'port', None, True))
|
||||
|
||||
if not port:
|
||||
raise util.Abort("No port was specified")
|
||||
|
||||
numOfChoices = len(choices)
|
||||
if numOfChoices == 0:
|
||||
if not numOfChoices:
|
||||
return default
|
||||
|
||||
client = socket.socket( socket.AF_INET, socket.SOCK_STREAM )
|
||||
@@ -67,7 +100,6 @@ def sendchangestoidea(ui, msg, choices, default):
|
||||
sendInt( client, default )
|
||||
|
||||
answer = receiveInt( client )
|
||||
print "Received answer: %s" % answer
|
||||
if answer == -1:
|
||||
raise util.Abort("User cancelled")
|
||||
else:
|
||||
@@ -80,13 +112,13 @@ def sendchangestoidea(ui, msg, choices, default):
|
||||
if getattr(ui.ui, 'promptchoice', None):
|
||||
@monkeypatch_method(ui.ui)
|
||||
def promptchoice(self, msg, choices=None, default=0):
|
||||
return sendchangestoidea(self, msg, choices, default)
|
||||
return sendchoicestoidea(self, msg, choices, default)
|
||||
else:
|
||||
@monkeypatch_method(ui.ui)
|
||||
def prompt(self, msg, choices=None, default="y"):
|
||||
resps = [s[s.index('&')+1].lower() for s in choices]
|
||||
defaultIndex = resps.index( default )
|
||||
responseIndex = sendchangestoidea( self, msg, choices, defaultIndex)
|
||||
responseIndex = sendchoicestoidea( self, msg, choices, defaultIndex)
|
||||
return resps[responseIndex]
|
||||
|
||||
original_warn = ui.ui.warn
|
||||
@@ -110,3 +142,59 @@ def warn(self, *msg):
|
||||
sendInt( client, len(msg) )
|
||||
for message in msg:
|
||||
send( client, message )
|
||||
|
||||
|
||||
def retrieve_pass_from_server(ui, uri,path, proposed_user):
|
||||
port = int(ui.config('hg4ideapass', 'port', None, True))
|
||||
if port is None:
|
||||
raise util.Abort("No port was specified")
|
||||
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
ui.debug("connecting ...")
|
||||
client.connect(('127.0.0.1', port))
|
||||
ui.debug("connected, sending data ...")
|
||||
|
||||
send(client, "getpass")
|
||||
send(client, uri)
|
||||
send(client, path)
|
||||
send(client, proposed_user)
|
||||
user = receiveWithMessage(client, "http authorization required")
|
||||
password = receiveWithMessage(client, "http authorization required")
|
||||
return user, password
|
||||
|
||||
|
||||
original_retrievepass=passwordmgr.find_user_password
|
||||
@monkeypatch_method(passwordmgr)
|
||||
def find_user_password(self, realm, authuri):
|
||||
try:
|
||||
return original_retrievepass(self, realm, authuri)
|
||||
except util.Abort:
|
||||
|
||||
# In mercurial 1.8 the readauthtoken method was replaced with
|
||||
# the readauthforuri method, which has different semantics
|
||||
if getattr(self, 'readauthtoken', None):
|
||||
def read_hgrc_authtoken(ui, authuri):
|
||||
return self.readauthtoken(authuri)
|
||||
else:
|
||||
def read_hgrc_authtoken(ui, authuri):
|
||||
# hg 1.8
|
||||
from mercurial.url import readauthforuri
|
||||
res = readauthforuri(self.ui, authuri)
|
||||
if res:
|
||||
group, auth = res
|
||||
return auth
|
||||
else:
|
||||
return None
|
||||
|
||||
user, password = urllib2.HTTPPasswordMgrWithDefaultRealm.find_user_password(self, realm, authuri)
|
||||
if user is None:
|
||||
auth = read_hgrc_authtoken(self.ui, authuri)
|
||||
if auth:
|
||||
user = auth.get("username")
|
||||
|
||||
reduced_uri, path= self.reduce_uri(authuri, False)
|
||||
retrievedPass = retrieve_pass_from_server(self.ui, reduced_uri, path, user)
|
||||
if retrievedPass is None:
|
||||
raise util.Abort(_('http authorization required'))
|
||||
user, passwd = retrievedPass
|
||||
self.add_password(realm, authuri, user, passwd)
|
||||
return retrievedPass
|
||||
@@ -20,9 +20,7 @@ import org.apache.commons.lang.StringUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
@State(
|
||||
name = "HgGlobalSettings",
|
||||
@@ -35,8 +33,8 @@ public class HgGlobalSettings implements PersistentStateComponent<HgGlobalSettin
|
||||
|
||||
private String myHgExecutable = HG;
|
||||
|
||||
// visited URL -> list of logins for this URL. Passwords are remembered in the PasswordSafe.
|
||||
private Map<String, List<String>> myRememberedUrls = new HashMap<String, List<String>>();
|
||||
// visited URL -> login for this URL. Passwords are remembered in the PasswordSafe.
|
||||
private Map<String, String> myRememberedUserNames = new HashMap<String, String>();
|
||||
private boolean myRunViaBash;
|
||||
|
||||
public static class State {
|
||||
@@ -57,12 +55,13 @@ public class HgGlobalSettings implements PersistentStateComponent<HgGlobalSettin
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the rememebered urls which were accessed while working in the plugin.
|
||||
* @return key is a String representation of a URL, value is the list (probably empty) of logins remembered for this URL.
|
||||
* Returns the remembered username for the specified URL which were accessed while working in the plugin.
|
||||
* @param stringUrl the url for which to retrieve the last used username;
|
||||
* @return the (probably empty) login remembered for this URL.
|
||||
*/
|
||||
@NotNull
|
||||
public Map<String, List<String>> getRememberedUrls() {
|
||||
return myRememberedUrls;
|
||||
@Nullable
|
||||
public String getRememberedUserName(@NotNull String stringUrl) {
|
||||
return myRememberedUserNames.get(stringUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -77,12 +76,7 @@ public class HgGlobalSettings implements PersistentStateComponent<HgGlobalSettin
|
||||
if (username == null) {
|
||||
username = "";
|
||||
}
|
||||
List<String> list = myRememberedUrls.get(stringUrl);
|
||||
if (list == null) {
|
||||
list = new LinkedList<String>();
|
||||
myRememberedUrls.put(stringUrl, list);
|
||||
}
|
||||
list.add(username);
|
||||
myRememberedUserNames.put(stringUrl, username);
|
||||
}
|
||||
|
||||
public static String getDefaultExecutable() {
|
||||
|
||||
@@ -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<String> arguments = new ArrayList<String>(2);
|
||||
arguments.add(repositoryURL);
|
||||
arguments.add(directory);
|
||||
return authenticator.executeCommandAndAuthenticateIfNecessary(project, null, repositoryURL, "clone", arguments);
|
||||
return HgCommandService.getInstance(project).execute(null, "clone", arguments);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<String> 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<String> 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<String> 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<String, List<String>> urls = hgGlobalSettings.getRememberedUrls();
|
||||
@Nullable List<String> 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<String> hgOptions, String operation, List<String> arguments, Charset charset, boolean silent) {
|
||||
HgCommandResult execute(final VirtualFile repo, final List<String> hgOptions, final String operation, final List<String> arguments, final Charset charset, final boolean silent) {
|
||||
return executeOffOfEDT(repo, hgOptions, operation, arguments, charset, silent);
|
||||
}
|
||||
|
||||
private HgCommandResult executeOffOfEDT(VirtualFile repo,
|
||||
List<String> hgOptions,
|
||||
String operation,
|
||||
List<String> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<String> arguments = new LinkedList<String>();
|
||||
arguments.add(source);
|
||||
return authenticator.executeCommandAndAuthenticateIfNecessary(project, null, source, "identify", arguments);
|
||||
return HgCommandService.getInstance(project).execute(null, "identify", arguments);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ public abstract class HgRemoteChangesetsCommand extends HgChangesetsCommand {
|
||||
@Override
|
||||
protected HgCommandResult executeCommand(VirtualFile repo, List<String> 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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.<br/><br/>" + 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.<br/><br/>" + 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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user