@@ -23,6 +23,7 @@ import HostKeyVerifiers._
2323import java .security .PublicKey
2424import net .schmizz .sshj .common .SecurityUtils
2525import net .schmizz .sshj .transport .verification .{OpenSSHKnownHosts , HostKeyVerifier }
26+ import annotation .tailrec
2627
2728trait HostConfigProvider extends (String => Validated [HostConfig ])
2829
@@ -156,34 +157,49 @@ object HostFileConfig {
156157 def apply (): HostConfigProvider = apply(DefaultHostFileDir )
157158 def apply (hostFilesDir : String ): HostConfigProvider = new FromStringsHostConfigProvider {
158159 def rawLines (host : String ) = {
159- val hostFile = new File (hostFilesDir + File .separator + host)
160- try {
161- Either .cond(
162- hostFile.exists,
163- hostFile.getAbsolutePath -> Source .fromFile(hostFile, " utf8" ).getLines(),
164- " Host file '%s' not found, either provide one or use a concrete HostConfig, PasswordLogin or PublicKeyLogin" .format(hostFile)
165- )
166- } catch {
167- case e : IOException => Left (" Could not read host file '%' due to %s" .format(hostFile, e))
160+ val locations = searchLocations(host).map(name => new File (hostFilesDir + File .separator + name))
161+ locations.find(_.exists) match {
162+ case Some (file) =>
163+ try Right (file.getAbsolutePath -> Source .fromFile(file, " utf8" ).getLines())
164+ catch { case e : IOException => Left (" Could not read host file '%' due to %s" .format(file, e)) }
165+ case None =>
166+ Left ((" Host files '%s' not found, either provide one or use a concrete HostConfig, PasswordLogin or " +
167+ " PublicKeyLogin" ).format(locations.mkString(" ', '" )))
168168 }
169169 }
170170 }
171+
172+ def searchLocations (name : String ): Stream [String ] = {
173+ if (name.isEmpty) Stream .empty
174+ else name #:: {
175+ val dotIx = name.indexOf('.' )
176+ @ tailrec def findDigit (i : Int ): Int = if (i < 0 || name.charAt(i).isDigit) i else findDigit(i - 1 )
177+ val digitIx = findDigit(if (dotIx > 0 ) dotIx - 1 else name.length - 1 )
178+ if (digitIx >= 0 && digitIx < dotIx)
179+ searchLocations(name.updated(digitIx, 'X' ))
180+ else if (dotIx > 0 )
181+ searchLocations(name.substring(dotIx + 1 ))
182+ else Stream .empty
183+ }
184+ }
171185}
172186
173187object HostResourceConfig {
174188 def apply (): HostConfigProvider = apply(" " )
175189 def apply (resourceBase : String ): HostConfigProvider = new FromStringsHostConfigProvider {
176190 def rawLines (host : String ) = {
177- val hostResource = resourceBase + host
178- try {
179- val resourceStream = getClass.getClassLoader.getResourceAsStream(hostResource)
180- Either .cond(
181- resourceStream != null ,
182- hostResource -> Source .fromInputStream(resourceStream, " utf8" ).getLines(),
183- " Host resource '%s' not found" .format(hostResource)
184- )
185- } catch {
186- case e : IOException => Left (" Could not read host resource '%' due to %s" .format(hostResource, e))
191+ val locations = HostFileConfig .searchLocations(host).map(resourceBase + _)
192+ locations.map { r =>
193+ r -> {
194+ val inputStream = getClass.getClassLoader.getResourceAsStream(r)
195+ try new StreamCopier ().emptyToString(inputStream).split(" \n " ).toList
196+ catch { case _ : Exception => null }
197+ }
198+ }.find(_._2 != null ) match {
199+ case Some (result) => Right (result)
200+ case None =>
201+ Left ((" Host resources '%s' not found, either provide one or use a concrete HostConfig, PasswordLogin or " +
202+ " PublicKeyLogin" ).format(locations.mkString(" ', '" )))
187203 }
188204 }
189205 }
0 commit comments