/* SPDX-FileCopyrightText: 2007 Jeff Cooper SPDX-FileCopyrightText: 2007 Thomas Georgiou SPDX-License-Identifier: LGPL-2.0-only */ #include "dictengine.h" #include #include #include #include #include #include #include DictEngine::DictEngine(QObject *parent, const QVariantList &args) : Plasma::DataEngine(parent, args) , m_tcpSocket(nullptr) { Q_UNUSED(args) m_serverName = QLatin1String("dict.org"); // In case we need to switch it later m_dictName = QLatin1String("wn"); // Default, good dictionary } DictEngine::~DictEngine() { } void DictEngine::setDict(const QString &dict) { m_dictName = dict; } void DictEngine::setServer(const QString &server) { m_serverName = server; } static QString wnToHtml(const QString &word, QByteArray &text) { QList splitText = text.split('\n'); QString def; def += QLatin1String("
\n"); static QRegularExpression linkRx(QStringLiteral("{(.*?)}")); bool isFirst = true; while (!splitText.empty()) { // 150 n definitions retrieved - definitions follow // 151 word database name - text follows // 250 ok (optional timing information here) // 552 No match QString currentLine = splitText.takeFirst(); if (currentLine.startsWith(QLatin1String("151"))) { isFirst = true; continue; } if (currentLine.startsWith('.')) { def += QLatin1String(""); continue; } if (currentLine.startsWith("552")) { return i18n("No match found for %1", word); } if (!(currentLine.startsWith(QLatin1String("150")) || currentLine.startsWith(QLatin1String("151")) || currentLine.startsWith(QLatin1String("250")))) { // Handle links int offset = 0; QRegularExpressionMatchIterator it = linkRx.globalMatch(currentLine); while (it.hasNext()) { QRegularExpressionMatch match = it.next(); QUrl url; url.setScheme("dict"); url.setPath(match.captured(1)); const QString linkText = QStringLiteral("%2").arg(url.toString(), match.captured(1)); currentLine.replace(match.capturedStart(0) + offset, match.capturedLength(0), linkText); offset += linkText.length() - match.capturedLength(0); } if (isFirst) { def += "
" + currentLine + "
\n
"; isFirst = false; continue; } else { static QRegularExpression newLineRx(QStringLiteral("([1-9]{1,2}:)")); if (currentLine.contains(newLineRx)) { def += QLatin1String("\n
\n"); } static QRegularExpression makeMeBoldRx(QStringLiteral("^([\\s\\S]*[1-9]{1,2}:)")); currentLine.replace(makeMeBoldRx, QLatin1String("\\1")); def += currentLine; continue; } } } def += QLatin1String("
"); return def; } void DictEngine::getDefinition() { m_tcpSocket->readAll(); QByteArray ret; const QByteArray command = QByteArray("DEFINE ") + m_dictName.toLatin1() + " \"" + m_currentWord.toUtf8() + "\"\n"; // qDebug() << command; m_tcpSocket->write(command); m_tcpSocket->flush(); while (!ret.contains("250") && !ret.contains("552") && !ret.contains("550")) { m_tcpSocket->waitForReadyRead(); ret += m_tcpSocket->readAll(); } m_tcpSocket->disconnectFromHost(); const QString html = wnToHtml(m_currentWord, ret); // setData(m_currentQuery, m_dictName, html); setData(m_currentQuery, QStringLiteral("text"), html); } void DictEngine::getDicts() { m_tcpSocket->readAll(); QByteArray ret; m_tcpSocket->write(QByteArray("SHOW DB\n")); m_tcpSocket->flush(); m_tcpSocket->waitForReadyRead(); while (!ret.contains("250")) { m_tcpSocket->waitForReadyRead(); ret += m_tcpSocket->readAll(); } QVariantMap *availableDicts = new QVariantMap; const QList retLines = ret.split('\n'); for (const QByteArray &curr : retLines) { if (curr.startsWith("554")) { // TODO: What happens if no DB available? // TODO: Eventually there will be functionality to change the server... break; } // ignore status code and empty lines if (curr.startsWith("250") || curr.startsWith("110") || curr.isEmpty()) { continue; } if (!curr.startsWith('-') && !curr.startsWith('.')) { const QString line = QString::fromUtf8(curr).trimmed(); const QString id = line.section(' ', 0, 0); QString description = line.section(' ', 1); if (description.startsWith('"') && description.endsWith('"')) { description.remove(0, 1); description.chop(1); } setData(QStringLiteral("list-dictionaries"), id, description); // this is additive availableDicts->insert(id, description); } } m_availableDictsCache.insert(m_serverName, availableDicts); m_tcpSocket->disconnectFromHost(); } void DictEngine::socketClosed() { if (m_tcpSocket) { m_tcpSocket->deleteLater(); } m_tcpSocket = nullptr; } bool DictEngine::sourceRequestEvent(const QString &query) { // FIXME: this is COMPLETELY broken .. it can only look up one query at a time! // a DataContainer subclass that does the look up should probably be made if (m_tcpSocket) { m_tcpSocket->abort(); // stop if lookup is in progress and new query is requested m_tcpSocket->deleteLater(); m_tcpSocket = nullptr; } QStringList queryParts = query.split(':', Qt::SkipEmptyParts); if (queryParts.isEmpty()) { return false; } m_currentWord = queryParts.last(); m_currentQuery = query; // asked for a dictionary? if (queryParts.count() > 1) { setDict(queryParts[queryParts.count() - 2]); // default to wordnet } else { setDict(QStringLiteral("wn")); } // asked for a server? if (queryParts.count() > 2) { setServer(queryParts[queryParts.count() - 3]); // default to wordnet } else { setServer(QStringLiteral("dict.org")); } if (m_currentWord.simplified().isEmpty()) { setData(m_currentQuery, m_dictName, QString()); } else { if (m_currentWord == QLatin1String("list-dictionaries")) { // Use cache if available QVariantMap *dicts = m_availableDictsCache.object(m_serverName); if (dicts) { for (auto it = dicts->constBegin(); it != dicts->constEnd(); ++it) { setData(m_currentQuery, it.key(), it.value()); } return true; } } // We need to do this in order to create the DataContainer immediately in DataEngine // so it can connect to updates. Not sure why DataEnginePrivate::requestSource // doesn't create the DataContainer when sourceRequestEvent returns true, by doing // source(sourceName) instead of source(sourceName, false), but well, I'm too scared to change that. setData(m_currentQuery, QVariant()); m_tcpSocket = new QTcpSocket(this); connect(m_tcpSocket, &QTcpSocket::disconnected, this, &DictEngine::socketClosed); if (m_currentWord == QLatin1String("list-dictionaries")) { connect(m_tcpSocket, &QTcpSocket::readyRead, this, &DictEngine::getDicts); } else { connect(m_tcpSocket, &QTcpSocket::readyRead, this, &DictEngine::getDefinition); } m_tcpSocket->connectToHost(m_serverName, 2628); } return true; } K_PLUGIN_CLASS_WITH_JSON(DictEngine, "plasma-dataengine-dict.json") #include "dictengine.moc"