#include "mremoteng_importer.h" #include #include namespace { QString joinFolderPath(const QStringList& parts) { QStringList trimmed; for (const QString& part : parts) { const QString t = part.trimmed(); if (!t.isEmpty()) { trimmed.push_back(t); } } return trimmed.join(QStringLiteral("/")); } // Maps mRemoteNG's Protocol attribute (an enum: RDP, VNC, SSH1, SSH2, // Telnet, Rlogin, RAW, HTTP, HTTPS, PowerShell, IntApp, Winbox) onto what // OrbitHub actually supports. *supported is set to false for anything // OrbitHub can't yet connect to (VNC included -- see issue #3). QString mappedProtocol(const QString& mRemoteNGProtocol, bool* supported) { const QString p = mRemoteNGProtocol.trimmed(); if (p.compare(QStringLiteral("RDP"), Qt::CaseInsensitive) == 0) { *supported = true; return QStringLiteral("RDP"); } if (p.compare(QStringLiteral("SSH1"), Qt::CaseInsensitive) == 0 || p.compare(QStringLiteral("SSH2"), Qt::CaseInsensitive) == 0) { *supported = true; return QStringLiteral("SSH"); } *supported = false; return p; } int defaultPortFor(const QString& orbitHubProtocol) { return orbitHubProtocol == QStringLiteral("RDP") ? 3389 : 22; } } MRemoteNGImportResult parseMRemoteNGConnections(const QByteArray& xmlData) { MRemoteNGImportResult result; QXmlStreamReader reader(xmlData); bool foundRoot = false; while (!reader.atEnd() && !foundRoot) { if (reader.readNext() == QXmlStreamReader::StartElement) { if (reader.name().compare(QStringLiteral("Connections"), Qt::CaseInsensitive) != 0) { result.errorMessage = QStringLiteral( "This doesn't look like an mRemoteNG connections file (expected a " " root element)."); return result; } foundRoot = true; const auto attrs = reader.attributes(); if (attrs.value(QStringLiteral("FullFileEncryption")) .compare(QStringLiteral("true"), Qt::CaseInsensitive) == 0) { result.errorMessage = QStringLiteral( "This file has \"full file encryption\" enabled in mRemoteNG, which " "encrypts the entire connection list with your master password. " "OrbitHub has no way to read that. In mRemoteNG, re-export with full " "file encryption turned off (only the saved passwords need stay " "encrypted -- OrbitHub never imports those anyway)."); return result; } } } if (!foundRoot) { result.errorMessage = QStringLiteral("Not a valid XML file, or the file is empty."); return result; } QStringList folderStack; std::vector openNodeIsContainer; while (!reader.atEnd()) { const QXmlStreamReader::TokenType token = reader.readNext(); if (token == QXmlStreamReader::StartElement && reader.name().compare(QStringLiteral("Node"), Qt::CaseInsensitive) == 0) { const auto attrs = reader.attributes(); const QString type = attrs.value(QStringLiteral("Type")).toString(); const bool isContainer = type.compare(QStringLiteral("Container"), Qt::CaseInsensitive) == 0; const QString name = attrs.value(QStringLiteral("Name")).toString().trimmed(); if (isContainer) { folderStack.push_back(name); const QString path = joinFolderPath(folderStack); if (!path.isEmpty()) { result.folders.push_back(path); } } else { bool supported = false; const QString protocolLabel = attrs.value(QStringLiteral("Protocol")).toString(); const QString mappedProto = mappedProtocol(protocolLabel, &supported); if (!supported) { result.skippedUnsupportedProtocol.push_back(QStringLiteral("%1 (%2)").arg( name.isEmpty() ? QStringLiteral("(unnamed)") : name, protocolLabel.isEmpty() ? QStringLiteral("unknown") : protocolLabel)); } else { MRemoteNGImportedProfile imported; imported.profile.name = name; imported.profile.host = attrs.value(QStringLiteral("Hostname")).toString().trimmed(); const QString portText = attrs.value(QStringLiteral("Port")).toString(); imported.profile.port = portText.isEmpty() ? defaultPortFor(mappedProto) : portText.toInt(); if (imported.profile.port <= 0) { imported.profile.port = defaultPortFor(mappedProto); } imported.profile.username = attrs.value(QStringLiteral("Username")).toString(); imported.profile.protocol = mappedProto; imported.profile.authMode = QStringLiteral("Password"); if (mappedProto == QStringLiteral("RDP")) { imported.profile.domain = attrs.value(QStringLiteral("Domain")).toString(); } imported.folderPath = joinFolderPath(folderStack); result.profiles.push_back(imported); } } openNodeIsContainer.push_back(isContainer); } else if (token == QXmlStreamReader::EndElement && reader.name().compare(QStringLiteral("Node"), Qt::CaseInsensitive) == 0) { if (!openNodeIsContainer.empty()) { if (openNodeIsContainer.back()) { folderStack.removeLast(); } openNodeIsContainer.pop_back(); } } } if (reader.hasError()) { result.errorMessage = QStringLiteral("Failed to parse XML: %1").arg(reader.errorString()); result.folders.clear(); result.profiles.clear(); result.skippedUnsupportedProtocol.clear(); } return result; }