mirror of
https://github.com/TermoraDev/termora.git
synced 2026-01-16 02:12:58 +08:00
feat: supports importing hosts from SecureCRT (#294)
This commit is contained in:
@@ -23,6 +23,8 @@ import org.jdesktop.swingx.JXTree
|
|||||||
import org.jdesktop.swingx.action.ActionManager
|
import org.jdesktop.swingx.action.ActionManager
|
||||||
import org.jdesktop.swingx.tree.DefaultXTreeCellRenderer
|
import org.jdesktop.swingx.tree.DefaultXTreeCellRenderer
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
|
import org.w3c.dom.Element
|
||||||
|
import org.w3c.dom.NodeList
|
||||||
import java.awt.Component
|
import java.awt.Component
|
||||||
import java.awt.Dimension
|
import java.awt.Dimension
|
||||||
import java.awt.datatransfer.DataFlavor
|
import java.awt.datatransfer.DataFlavor
|
||||||
@@ -43,6 +45,9 @@ import javax.swing.event.PopupMenuListener
|
|||||||
import javax.swing.filechooser.FileNameExtensionFilter
|
import javax.swing.filechooser.FileNameExtensionFilter
|
||||||
import javax.swing.tree.TreePath
|
import javax.swing.tree.TreePath
|
||||||
import javax.swing.tree.TreeSelectionModel
|
import javax.swing.tree.TreeSelectionModel
|
||||||
|
import javax.xml.parsers.DocumentBuilderFactory
|
||||||
|
import javax.xml.xpath.XPathConstants
|
||||||
|
import javax.xml.xpath.XPathFactory
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
|
|
||||||
class NewHostTree : JXTree() {
|
class NewHostTree : JXTree() {
|
||||||
@@ -361,6 +366,7 @@ class NewHostTree : JXTree() {
|
|||||||
val csvMenu = importMenu.add("CSV")
|
val csvMenu = importMenu.add("CSV")
|
||||||
val xShellMenu = importMenu.add("Xshell")
|
val xShellMenu = importMenu.add("Xshell")
|
||||||
val windTermMenu = importMenu.add("WindTerm")
|
val windTermMenu = importMenu.add("WindTerm")
|
||||||
|
val secureCRTMenu = importMenu.add("SecureCRT")
|
||||||
val mobaXtermMenu = importMenu.add("MobaXterm")
|
val mobaXtermMenu = importMenu.add("MobaXterm")
|
||||||
|
|
||||||
val open = popupMenu.add(I18n.getString("termora.welcome.contextmenu.connect"))
|
val open = popupMenu.add(I18n.getString("termora.welcome.contextmenu.connect"))
|
||||||
@@ -390,6 +396,7 @@ class NewHostTree : JXTree() {
|
|||||||
val property = popupMenu.add(I18n.getString("termora.welcome.contextmenu.property"))
|
val property = popupMenu.add(I18n.getString("termora.welcome.contextmenu.property"))
|
||||||
|
|
||||||
xShellMenu.addActionListener { importHosts(lastNode, ImportType.Xshell) }
|
xShellMenu.addActionListener { importHosts(lastNode, ImportType.Xshell) }
|
||||||
|
secureCRTMenu.addActionListener { importHosts(lastNode, ImportType.SecureCRT) }
|
||||||
mobaXtermMenu.addActionListener { importHosts(lastNode, ImportType.MobaXterm) }
|
mobaXtermMenu.addActionListener { importHosts(lastNode, ImportType.MobaXterm) }
|
||||||
csvMenu.addActionListener { importHosts(lastNode, ImportType.CSV) }
|
csvMenu.addActionListener { importHosts(lastNode, ImportType.CSV) }
|
||||||
windTermMenu.addActionListener { importHosts(lastNode, ImportType.WindTerm) }
|
windTermMenu.addActionListener { importHosts(lastNode, ImportType.WindTerm) }
|
||||||
@@ -640,6 +647,7 @@ class NewHostTree : JXTree() {
|
|||||||
when (type) {
|
when (type) {
|
||||||
ImportType.WindTerm -> chooser.fileFilter = FileNameExtensionFilter("WindTerm (*.sessions)", "sessions")
|
ImportType.WindTerm -> chooser.fileFilter = FileNameExtensionFilter("WindTerm (*.sessions)", "sessions")
|
||||||
ImportType.CSV -> chooser.fileFilter = FileNameExtensionFilter("CSV (*.csv)", "csv")
|
ImportType.CSV -> chooser.fileFilter = FileNameExtensionFilter("CSV (*.csv)", "csv")
|
||||||
|
ImportType.SecureCRT -> chooser.fileFilter = FileNameExtensionFilter("SecureCRT (*.xml)", "xml")
|
||||||
ImportType.MobaXterm -> chooser.fileFilter =
|
ImportType.MobaXterm -> chooser.fileFilter =
|
||||||
FileNameExtensionFilter("MobaXterm (*.mobaconf,*.ini)", "ini", "mobaconf")
|
FileNameExtensionFilter("MobaXterm (*.mobaconf,*.ini)", "ini", "mobaconf")
|
||||||
|
|
||||||
@@ -709,11 +717,14 @@ class NewHostTree : JXTree() {
|
|||||||
|
|
||||||
val nodes = when (type) {
|
val nodes = when (type) {
|
||||||
ImportType.WindTerm -> parseFromWindTerm(folder, file)
|
ImportType.WindTerm -> parseFromWindTerm(folder, file)
|
||||||
|
ImportType.SecureCRT -> parseFromSecureCRT(folder, file)
|
||||||
ImportType.MobaXterm -> parseFromMobaXterm(folder, file)
|
ImportType.MobaXterm -> parseFromMobaXterm(folder, file)
|
||||||
ImportType.Xshell -> parseFromXshell(folder, file)
|
ImportType.Xshell -> parseFromXshell(folder, file)
|
||||||
ImportType.CSV -> file.bufferedReader().use { parseFromCSV(folder, it) }
|
ImportType.CSV -> file.bufferedReader().use { parseFromCSV(folder, it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (nodes.isEmpty()) return
|
||||||
|
|
||||||
for (node in nodes) {
|
for (node in nodes) {
|
||||||
node.host = node.host.copy(parentId = folder.host.id, updateDate = System.currentTimeMillis())
|
node.host = node.host.copy(parentId = folder.host.id, updateDate = System.currentTimeMillis())
|
||||||
if (folder.getIndex(node) != -1) {
|
if (folder.getIndex(node) != -1) {
|
||||||
@@ -758,6 +769,43 @@ class NewHostTree : JXTree() {
|
|||||||
return parseFromCSV(folder, StringReader(sw.toString()))
|
return parseFromCSV(folder, StringReader(sw.toString()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun parseFromSecureCRT(folder: HostTreeNode, file: File): List<HostTreeNode> {
|
||||||
|
val xPath = XPathFactory.newInstance().newXPath()
|
||||||
|
val db = DocumentBuilderFactory.newInstance().newDocumentBuilder()
|
||||||
|
val doc = db.parse(file)
|
||||||
|
val sessionElement = xPath.compile("/VanDyke/key[@name='Sessions']")
|
||||||
|
.evaluate(doc, XPathConstants.NODE) as Element? ?: return emptyList()
|
||||||
|
val nodeList = xPath.compile(".//key[not(key)]").evaluate(sessionElement, XPathConstants.NODESET) as NodeList
|
||||||
|
if (nodeList.length == 0) return emptyList()
|
||||||
|
|
||||||
|
val sw = StringWriter()
|
||||||
|
CSVPrinter(sw, CSVFormat.EXCEL.builder().setHeader(*CSV_HEADERS).get()).use { printer ->
|
||||||
|
for (i in 0 until nodeList.length) {
|
||||||
|
val ele = nodeList.item(i) as Element
|
||||||
|
val protocol = xPath.compile("./string[@name='Protocol Name']/text()").evaluate(ele)
|
||||||
|
if (!StringUtils.equalsIgnoreCase(protocol, "SSH2")) continue
|
||||||
|
val label = ele.getAttribute("name")
|
||||||
|
if (StringUtils.isBlank(label)) continue
|
||||||
|
val hostname = xPath.compile("./string[@name='Hostname']/text()").evaluate(ele)
|
||||||
|
if (StringUtils.isBlank(hostname)) continue
|
||||||
|
val username = xPath.compile("./string[@name='Username']/text()").evaluate(ele)
|
||||||
|
val port = xPath.compile("./dword[@name='[SSH2] Port']/text()").evaluate(ele)?.toIntOrNull() ?: 22
|
||||||
|
|
||||||
|
|
||||||
|
val folders = mutableListOf<String>()
|
||||||
|
var p = ele.parentNode as Element
|
||||||
|
while (p != sessionElement) {
|
||||||
|
folders.addFirst(p.getAttribute("name"))
|
||||||
|
p = p.parentNode as Element
|
||||||
|
}
|
||||||
|
printer.printRecord(folders.joinToString("/"), label, hostname, port.toString(), username, "SSH")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return parseFromCSV(folder, StringReader(sw.toString()))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun parseFromMobaXterm(folder: HostTreeNode, file: File): List<HostTreeNode> {
|
private fun parseFromMobaXterm(folder: HostTreeNode, file: File): List<HostTreeNode> {
|
||||||
val ini = Ini()
|
val ini = Ini()
|
||||||
ini.config.isEscapeKeyOnly = true
|
ini.config.isEscapeKeyOnly = true
|
||||||
@@ -905,6 +953,7 @@ class NewHostTree : JXTree() {
|
|||||||
WindTerm,
|
WindTerm,
|
||||||
CSV,
|
CSV,
|
||||||
Xshell,
|
Xshell,
|
||||||
|
SecureCRT,
|
||||||
MobaXterm
|
MobaXterm
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user