1- use std:: process:: Stdio ;
1+ use std:: {
2+ ffi:: OsStr ,
3+ process:: { Command , Stdio } ,
4+ } ;
25
3- use gix_url:: ArgumentSafety :: * ;
6+ use gix_url:: { ArgumentSafety :: * , Url } ;
47
5- use crate :: { client:: blocking_io, Protocol } ;
8+ use crate :: { client:: blocking_io:: file :: SpawnProcessOnDemand , Protocol } ;
69
710/// The error used in [`connect()`].
811#[ derive( Debug , thiserror:: Error ) ]
@@ -100,40 +103,18 @@ pub mod connect {
100103/// If `trace` is `true`, all packetlines received or sent will be passed to the facilities of the `gix-trace` crate.
101104#[ allow( clippy:: result_large_err) ]
102105pub fn connect (
103- url : gix_url :: Url ,
106+ url : Url ,
104107 desired_version : Protocol ,
105108 options : connect:: Options ,
106109 trace : bool ,
107- ) -> Result < blocking_io :: file :: SpawnProcessOnDemand , Error > {
110+ ) -> Result < SpawnProcessOnDemand , Error > {
108111 if url. scheme != gix_url:: Scheme :: Ssh || url. host ( ) . is_none ( ) {
109112 return Err ( Error :: UnsupportedScheme ( url) ) ;
110113 }
111114 let ssh_cmd = options. ssh_command ( ) ;
112- let mut kind = options. kind . unwrap_or_else ( || ProgramKind :: from ( ssh_cmd) ) ;
113- if options. kind . is_none ( ) && kind == ProgramKind :: Simple {
114- let mut cmd = std:: process:: Command :: from (
115- gix_command:: prepare ( ssh_cmd)
116- . stderr ( Stdio :: null ( ) )
117- . stdout ( Stdio :: null ( ) )
118- . stdin ( Stdio :: null ( ) )
119- . command_may_be_shell_script ( )
120- . arg ( "-G" )
121- . arg ( match url. host_as_argument ( ) {
122- Usable ( host) => host,
123- Dangerous ( host) => Err ( Error :: AmbiguousHostName { host : host. into ( ) } ) ?,
124- Absent => panic ! ( "BUG: host should always be present in SSH URLs" ) ,
125- } ) ,
126- ) ;
127- gix_features:: trace:: debug!( cmd = ?cmd, "invoking `ssh` for feature check" ) ;
128- kind = if cmd. status ( ) . ok ( ) . is_some_and ( |status| status. success ( ) ) {
129- ProgramKind :: Ssh
130- } else {
131- ProgramKind :: Simple
132- } ;
133- }
134-
115+ let kind = determine_client_kind ( options. kind , ssh_cmd, & url, options. disallow_shell ) ?;
135116 let path = gix_url:: expand_path:: for_shell ( url. path . clone ( ) ) ;
136- Ok ( blocking_io :: file :: SpawnProcessOnDemand :: new_ssh (
117+ Ok ( SpawnProcessOnDemand :: new_ssh (
137118 url,
138119 ssh_cmd,
139120 path,
@@ -144,5 +125,44 @@ pub fn connect(
144125 ) )
145126}
146127
128+ #[ allow( clippy:: result_large_err) ]
129+ fn determine_client_kind (
130+ known_kind : Option < ProgramKind > ,
131+ ssh_cmd : & OsStr ,
132+ url : & Url ,
133+ disallow_shell : bool ,
134+ ) -> Result < ProgramKind , Error > {
135+ let mut kind = known_kind. unwrap_or_else ( || ProgramKind :: from ( ssh_cmd) ) ;
136+ if known_kind. is_none ( ) && kind == ProgramKind :: Simple {
137+ let mut cmd = build_client_feature_check_command ( ssh_cmd, url, disallow_shell) ?;
138+ gix_features:: trace:: debug!( cmd = ?cmd, "invoking `ssh` for feature check" ) ;
139+ kind = if cmd. status ( ) . ok ( ) . is_some_and ( |status| status. success ( ) ) {
140+ ProgramKind :: Ssh
141+ } else {
142+ ProgramKind :: Simple
143+ } ;
144+ }
145+ Ok ( kind)
146+ }
147+
148+ #[ allow( clippy:: result_large_err) ]
149+ fn build_client_feature_check_command ( ssh_cmd : & OsStr , url : & Url , disallow_shell : bool ) -> Result < Command , Error > {
150+ let mut prepare = gix_command:: prepare ( ssh_cmd)
151+ . stderr ( Stdio :: null ( ) )
152+ . stdout ( Stdio :: null ( ) )
153+ . stdin ( Stdio :: null ( ) )
154+ . command_may_be_shell_script ( )
155+ . arg ( "-G" )
156+ . arg ( match url. host_as_argument ( ) {
157+ Usable ( host) => host,
158+ Dangerous ( host) => Err ( Error :: AmbiguousHostName { host : host. into ( ) } ) ?,
159+ Absent => panic ! ( "BUG: host should always be present in SSH URLs" ) ,
160+ } ) ;
161+ if disallow_shell {
162+ prepare. use_shell = false ;
163+ }
164+ Ok ( prepare. into ( ) )
165+ }
166+
147167#[ cfg( test) ]
148168mod tests;
0 commit comments