2626import re
2727import shlex
2828import socket
29+ import glob
2930
3031SSH_PORT = 22
3132
@@ -49,7 +50,7 @@ def __init__(self):
4950 """
5051 self ._config = []
5152
52- def parse (self , file_obj ):
53+ def parse (self , file_obj , parsed_files = None ):
5354 """
5455 Read an OpenSSH config from the given file object.
5556
@@ -68,7 +69,7 @@ def parse(self, file_obj):
6869 raise Exception ("Unparsable line %s" % line )
6970 key = match .group (1 ).lower ()
7071 value = match .group (2 )
71-
72+
7273 if key == 'host' :
7374 self ._config .append (host )
7475 host = {
@@ -80,6 +81,27 @@ def parse(self, file_obj):
8081 # at the end (for compatibility with issue #415). After 3.x, it
8182 # will simply not get stripped, leaving a nice explicit marker.
8283 host ['config' ][key ] = None
84+ elif key == 'include' :
85+ # support for Include directive in ssh_config
86+
87+ path = value
88+ # according to SSH documentation if the path is relative, we look forward in ~/.ssh
89+ if path [0 ] != '/' and path [0 ]!= '~' :
90+ path = '~/.ssh/' + path
91+ #expand the user home path
92+ path = os .path .expanduser (path )
93+
94+ if parsed_files == None :
95+ parsed_files = []
96+
97+ #parse every included file
98+ for filename in glob .iglob (path ):
99+ if os .path .isfile (filename ):
100+ if path in parsed_files :
101+ raise Exception ("Include loop detected in ssh config file: %s" % path )
102+ with open (filename ) as fd :
103+ parsed_files .append (path );
104+ self .parse (fd ,parsed_files )
83105 else :
84106 if value .startswith ('"' ) and value .endswith ('"' ):
85107 value = value [1 :- 1 ]
0 commit comments