@@ -97,10 +97,19 @@ def help_text(x: str) -> str:
9797 return {'e' : 'edit' , 'o' : 'open' , 's' : 'save' }.get (response , 'cancel' )
9898
9999
100+ def hostname_matches (from_hyperlink : str , actual : str ) -> bool :
101+ if from_hyperlink == actual :
102+ return True
103+ if from_hyperlink .partition ('.' )[0 ] == actual .partition ('.' )[0 ]:
104+ return True
105+ return False
106+
107+
100108class ControlMaster :
101109
102- def __init__ (self , conn_data : SSHConnectionData , remote_path : str , dest : str = '' ):
110+ def __init__ (self , conn_data : SSHConnectionData , remote_path : str , cli_opts : RemoteFileCLIOptions , dest : str = '' ):
103111 self .conn_data = conn_data
112+ self .cli_opts = cli_opts
104113 self .remote_path = remote_path
105114 self .dest = dest
106115 self .tdir = ''
@@ -138,6 +147,33 @@ def is_alive(self) -> bool:
138147 stdout = subprocess .DEVNULL , stderr = subprocess .DEVNULL , stdin = subprocess .DEVNULL
139148 ).wait () == 0
140149
150+ def check_hostname_matches (self ) -> None :
151+ cp = subprocess .run (self .batch_cmd_prefix + [self .conn_data .hostname , 'hostname' , '-f' ], stdout = subprocess .PIPE ,
152+ stderr = subprocess .DEVNULL , stdin = subprocess .DEVNULL )
153+ if cp .returncode == 0 :
154+ q = cp .stdout .decode ('utf-8' ).strip ()
155+ if not hostname_matches (self .cli_opts .hostname or '' , q ):
156+ print (reset_terminal (), end = '' )
157+ print (f'The remote hostname { styled (q , fg = "green" )} does not match the' )
158+ print (f'hostname in the hyperlink { styled (self .cli_opts .hostname or "" , fg = "red" )} ' )
159+ print ('This indicates that kitty has not connected to the correct remote machine.' )
160+ print ('This can happen, for example, when using nested SSH sessions.' )
161+ print (f'The hostname kitty used to connect was: { styled (self .conn_data .hostname , fg = "yellow" )} ' , end = '' )
162+ if self .conn_data .port is not None :
163+ print (f' with port: { self .conn_data .port } ' )
164+ print ()
165+ print ()
166+ print ('Do you want to continue anyway?' )
167+ print (
168+ f'{ styled ("Y" , fg = "green" )} es' ,
169+ f'{ styled ("N" , fg = "red" )} o' , sep = '\t '
170+ )
171+ sys .stdout .flush ()
172+ response = get_key_press ('yn' , 'n' )
173+ if response != 'y' :
174+ raise SystemExit (1 )
175+ print (reset_terminal (), end = '' )
176+
141177 def download (self ) -> bool :
142178 with open (self .dest , 'wb' ) as f :
143179 return subprocess .run (
@@ -181,7 +217,7 @@ def main(args: List[str]) -> Result:
181217 show_error ('Failed with unhandled exception' )
182218
183219
184- def save_as (conn_data : SSHConnectionData , remote_path : str ) -> None :
220+ def save_as (conn_data : SSHConnectionData , remote_path : str , cli_opts : RemoteFileCLIOptions ) -> None :
185221 ddir = cache_dir ()
186222 os .makedirs (ddir , exist_ok = True )
187223 last_used_store_path = os .path .join (ddir , 'remote-file-last-used.txt' )
@@ -219,7 +255,7 @@ def save_as(conn_data: SSHConnectionData, remote_path: str) -> None:
219255 return
220256 if response == 'n' :
221257 print (reset_terminal (), end = '' )
222- return save_as (conn_data , remote_path )
258+ return save_as (conn_data , remote_path , cli_opts )
223259
224260 if response == 'r' :
225261 q = dest
@@ -231,7 +267,8 @@ def save_as(conn_data: SSHConnectionData, remote_path: str) -> None:
231267 dest = q
232268 if os .path .dirname (dest ):
233269 os .makedirs (os .path .dirname (dest ), exist_ok = True )
234- with ControlMaster (conn_data , remote_path , dest = dest ) as master :
270+ with ControlMaster (conn_data , remote_path , cli_opts , dest = dest ) as master :
271+ master .check_hostname_matches ()
235272 if not master .download ():
236273 show_error ('Failed to copy file from remote machine' )
237274
@@ -242,13 +279,15 @@ def handle_action(action: str, cli_opts: RemoteFileCLIOptions) -> Result:
242279 if action == 'open' :
243280 print ('Opening' , cli_opts .path , 'from' , cli_opts .hostname )
244281 dest = os .path .join (tempfile .mkdtemp (), os .path .basename (remote_path ))
245- with ControlMaster (conn_data , remote_path , dest = dest ) as master :
282+ with ControlMaster (conn_data , remote_path , cli_opts , dest = dest ) as master :
283+ master .check_hostname_matches ()
246284 if master .download ():
247285 return dest
248286 show_error ('Failed to copy file from remote machine' )
249287 elif action == 'edit' :
250288 print ('Editing' , cli_opts .path , 'from' , cli_opts .hostname )
251- with ControlMaster (conn_data , remote_path ) as master :
289+ with ControlMaster (conn_data , remote_path , cli_opts ) as master :
290+ master .check_hostname_matches ()
252291 if not master .download ():
253292 show_error (f'Failed to download { remote_path } ' )
254293 return None
@@ -271,7 +310,7 @@ def handle_action(action: str, cli_opts: RemoteFileCLIOptions) -> Result:
271310 show_error (f'Failed to upload { remote_path } , SSH master process died' )
272311 elif action == 'save' :
273312 print ('Saving' , cli_opts .path , 'from' , cli_opts .hostname )
274- save_as (conn_data , remote_path )
313+ save_as (conn_data , remote_path , cli_opts )
275314
276315
277316@result_handler ()
0 commit comments