diff --git a/CMakeLists.txt b/CMakeLists.txt index 656d6581..42b7a290 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,38 +1,45 @@ project(Ruqola) cmake_minimum_required(VERSION 3.0) find_package(ECM REQUIRED NO_MODULE) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH}) cmake_policy(SET CMP0063 NEW) include(KDECompilerSettings) include(KDEInstallDirs) include(KDECMakeSettings) include(ECMInstallIcons) +include(ECMQtDeclareLoggingCategory) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOUIC ON) set(CMAKE_INCLUDE_CURRENT_DIR ON) if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUXX) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 ") endif() find_package(Qt5 REQUIRED COMPONENTS Core Gui Widgets Qml Quick WebSockets Network NetworkAuth) find_package(KF5 REQUIRED COMPONENTS Kirigami2 ) include(ECMPoQmTools) include_directories(${QT_INCLUDES} ${CMAKE_CURRENT_BINARY_DIR}) +#add_definitions("-DQT_NO_CAST_FROM_ASCII -DQT_NO_CAST_TO_ASCII") +add_definitions(-DQT_NO_NARROWING_CONVERSIONS_IN_CONNECT) +#add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x060000) +add_definitions(-DQT_NO_URL_CAST_FROM_STRING) + add_subdirectory(src) if (IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/po") ecm_install_po_files_as_qm(po) endif() +install(FILES ruqola.categories DESTINATION ${KDE_INSTALL_CONFDIR}) feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/ruqola.categories b/ruqola.categories new file mode 100644 index 00000000..9b6bbd3b --- /dev/null +++ b/ruqola.categories @@ -0,0 +1 @@ +org.kde.ruqola ruqola (ruqola) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 26971f83..de0dbd83 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,31 +1,33 @@ set (Ruqola_core_srcs messagemodel.cpp roommodel.cpp ddpclient.cpp ruqola.cpp rocketchatbackend.cpp notification.cpp messagequeue.cpp authentication.cpp ) +ecm_qt_declare_logging_category(Ruqola_core_srcs HEADER ruqola_debug.h IDENTIFIER RUQOLA_LOG CATEGORY_NAME org.kde.ruqola) + add_library(RuqolaCore STATIC ${Ruqola_core_srcs}) target_link_libraries(RuqolaCore Qt5::Core Qt5::Gui Qt5::Widgets Qt5::Qml Qt5::Quick Qt5::WebSockets Qt5::Network Qt5::NetworkAuth ) qt5_add_resources(RuqolaResources qml/qml.qrc) add_executable(ruqola main.cpp ${RuqolaResources}) target_link_libraries(ruqola RuqolaCore) install(TARGETS ruqola RUNTIME DESTINATION bin) diff --git a/src/authentication.cpp b/src/authentication.cpp index 14b5614e..4456dab0 100644 --- a/src/authentication.cpp +++ b/src/authentication.cpp @@ -1,117 +1,118 @@ /* * * Copyright 2016 Riccardo Iaconelli * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "ruqola.h" #include "authentication.h" #include "ddpclient.h" +#include "ruqola_debug.h" #include #include Authentication::Authentication() : m_google(nullptr) { getDataFromJson(); } void Authentication::getDataFromJson(){ QDir cacheDir(":/src"); if (!cacheDir.exists(cacheDir.path())) { cacheDir.mkpath(cacheDir.path()); } QFile f(cacheDir.absoluteFilePath("client_secret.json")); QString val; if (f.open(QIODevice::ReadOnly | QIODevice::Text)) { val = f.readAll(); } QJsonDocument document = QJsonDocument::fromJson(val.toUtf8()); QJsonObject object = document.object(); const auto settingsObject = object["web"].toObject(); const QUrl authUri(settingsObject["auth_uri"].toString()); const QUrl tokenUri(settingsObject["token_uri"].toString()); const auto clientID = settingsObject["client_id"].toString(); const auto clientSecret(settingsObject["client_secret"].toString()); const auto redirectUrls = settingsObject["redirect_uris"].toArray(); const QUrl redirectUrl(redirectUrls[0].toString()); /* QString clientID = QString("143580046552-s4rmnq5mg008u76id0d3rl63od985hc6.apps.googleusercontent.com"); QString clientSecret = QString("nyVm19iOjjtldcCZJ-7003xg"); QString redirectUrl = QString("http://localhost:8080/cb/_oauth/google?close"); */ QSettings s; s.setValue("clientID", clientID); m_clientID = clientID; s.setValue("clientSecret", clientSecret); m_clientSecret = clientSecret; s.setValue("redirectUrl", redirectUrl); } void Authentication::OAuthLogin() { QJsonObject auth; QJsonObject authKeys; authKeys["credentialToken"] = m_clientID; authKeys["credentialSecret"] = m_clientSecret; auth["oauth"] = authKeys; - qDebug() << "-------------------------"; - qDebug() << "-------------------------"; - qDebug() << "OAuth Json" << auth; + qCDebug(RUQOLA_LOG) << "-------------------------"; + qCDebug(RUQOLA_LOG) << "-------------------------"; + qCDebug(RUQOLA_LOG) << "OAuth Json" << auth; Ruqola::self()->ddp()->method("login", QJsonDocument(auth)); QJsonArray requestPermissions; requestPermissions.append("email"); QUuid state; state = state.createUuid(); QSettings s; s.setValue("stateRandomNumber", state); QJsonObject loginUrlParameters; loginUrlParameters["client_id"] = m_clientID; loginUrlParameters["response_type"] = QString("code"); loginUrlParameters["scope"] = QString("openID profile email"); loginUrlParameters["state"] = state.toString(); QJsonObject json; json["requestPermissions"] = requestPermissions; json["requestOfflineToken"] = true; json["loginUrlParameters"] = loginUrlParameters; json["loginHint"] = s.value("username").toString(); json["loginStyle"] = QString("redirect"); json["redirectUrl"] = s.value("redirectUrl").toString(); -// qDebug() << "OAuth Json" << json; +// qCDebug(RUQOLA_LOG) << "OAuth Json" << json; // Ruqola::self()->ddp()->method("login", QJsonDocument(json)); } //#include "authentication.moc" diff --git a/src/ddpclient.cpp b/src/ddpclient.cpp index 7e76857b..23f42647 100644 --- a/src/ddpclient.cpp +++ b/src/ddpclient.cpp @@ -1,317 +1,318 @@ /* * * Copyright 2016 Riccardo Iaconelli * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "ddpclient.h" #include "ruqola.h" +#include "ruqola_debug.h" #include #include #include void process_test(QJsonDocument doc) { - qDebug() << "Callback test:" << doc; - qDebug() << "End callback"; + qCDebug(RUQOLA_LOG) << "Callback test:" << doc; + qCDebug(RUQOLA_LOG) << "End callback"; } void login_callback(QJsonDocument doc) { - qDebug() << "LOGIN:" << doc; - Ruqola::self()->setAuthToken(doc.object().value("token").toString()); - qDebug() << "End callback"; + qCDebug(RUQOLA_LOG) << "LOGIN:" << doc; + Ruqola::self()->setAuthToken(doc.object().value(QStringLiteral("token")).toString()); + qCDebug(RUQOLA_LOG) << "End callback"; } void DDPClient::resume_login_callback(const QJsonDocument &doc) { - qDebug() << "LOGIN:" << doc; - Ruqola::self()->setAuthToken(doc.object().value("token").toString()); - qDebug() << "End callback"; + qCDebug(RUQOLA_LOG) << "LOGIN:" << doc; + Ruqola::self()->setAuthToken(doc.object().value(QStringLiteral("token")).toString()); + qCDebug(RUQOLA_LOG) << "End callback"; } void empty_callback(QJsonDocument doc) { Q_UNUSED(doc); } DDPClient::DDPClient(const QString& url, QObject* parent) : QObject(parent), m_url(url), m_uid(1), m_loginJob(0), m_loginStatus(NotConnected), m_loginType(Password), m_connected(false), m_attemptedPasswordLogin(false), m_attemptedTokenLogin(false) { m_webSocket.ignoreSslErrors(); connect(&m_webSocket, &QWebSocket::connected, this, &DDPClient::onWSConnected); connect(&m_webSocket, &QWebSocket::textMessageReceived, this, &DDPClient::onTextMessageReceived); connect(&m_webSocket, &QWebSocket::disconnected, this, &DDPClient::onWSclosed); connect(Ruqola::self(), &Ruqola::serverURLChanged, this, &DDPClient::onServerURLChange); if (!url.isEmpty()) { - m_webSocket.open(QUrl("wss://"+url+"/websocket")); + m_webSocket.open(QUrl(QStringLiteral("wss://")+url+QStringLiteral("/websocket"))); } - qDebug() << "Trying to connect to URL" << url; + qCDebug(RUQOLA_LOG) << "Trying to connect to URL" << url; } DDPClient::~DDPClient() { m_webSocket.close(); } void DDPClient::onServerURLChange() { if (Ruqola::self()->serverURL() != m_url || !m_webSocket.isValid()) { if (m_webSocket.isValid()) { m_webSocket.flush(); m_webSocket.close(); } m_url = Ruqola::self()->serverURL(); - m_webSocket.open(QUrl("wss://"+m_url+"/websocket")); + m_webSocket.open(QUrl(QStringLiteral("wss://")+m_url+QStringLiteral("/websocket"))); connect(&m_webSocket, &QWebSocket::connected, this, &DDPClient::onWSConnected); - qDebug() << "Reconnecting" << m_url; + qCDebug(RUQOLA_LOG) << "Reconnecting" << m_url; } } DDPClient::LoginStatus DDPClient::loginStatus() const { return m_loginStatus; } void DDPClient::setLoginStatus(DDPClient::LoginStatus l) { - qDebug() << "Setting login status to" << l; + qCDebug(RUQOLA_LOG) << "Setting login status to" << l; m_loginStatus = l; emit loginStatusChanged(); // reset flags if (l == LoginFailed) { m_attemptedPasswordLogin = false; m_attemptedTokenLogin = false; } } DDPClient::LoginType DDPClient::loginType() const { return m_loginType; } void DDPClient::setLoginType(DDPClient::LoginType t) { - qDebug() << "Setting login type to" << t; + qCDebug(RUQOLA_LOG) << "Setting login type to" << t; m_loginType = t; emit loginTypeChanged(); } bool DDPClient::isConnected() const { return m_connected; } bool DDPClient::isLoggedIn() const { return m_loginStatus == LoggedIn; } QString DDPClient::cachePath() const { return QStandardPaths::writableLocation(QStandardPaths::CacheLocation); } QQueue> DDPClient::messageQueue() { return m_messageQueue; } unsigned int DDPClient::method(const QString& m, const QJsonDocument& params, DDPClient::MessageType messageType) { return method(m, params, empty_callback, messageType); } unsigned int DDPClient::method(const QString& method, const QJsonDocument& params, std::function callback, DDPClient::MessageType messageType) { QJsonObject json; - json["msg"] = "method"; - json["method"] = method; - json["id"] = QString::number(m_uid); + json[QStringLiteral("msg")] = "method"; + json[QStringLiteral("method")] = method; + json[QStringLiteral("id")] = QString::number(m_uid); if (params.isArray()){ - json["params"] = params.array(); + json[QStringLiteral("params")] = params.array(); } else if (params.isObject()) { QJsonArray arr; arr.append(params.object()); - json["params"] = arr; + json[QStringLiteral("params")] = arr; } qint64 bytes = m_webSocket.sendTextMessage(QJsonDocument(json).toJson(QJsonDocument::Compact)); if (bytes < json.length()) { - qDebug() << "ERROR! I couldn't send all of my message. This is a bug! (try again)"; - qDebug() << m_webSocket.isValid() << m_webSocket.error() << m_webSocket.requestUrl(); + qCDebug(RUQOLA_LOG) << "ERROR! I couldn't send all of my message. This is a bug! (try again)"; + qCDebug(RUQOLA_LOG) << m_webSocket.isValid() << m_webSocket.error() << m_webSocket.requestUrl(); if (messageType==DDPClient::Persistent) { m_messageQueue.enqueue(qMakePair(method,params)); Ruqola::self()->messageQueue()->processQueue(); } } else { - qDebug() << "Successfully sent " << json; + qCDebug(RUQOLA_LOG) << "Successfully sent " << json; } m_callbackHash[m_uid] = callback; m_uid++; return m_uid - 1 ; } void DDPClient::subscribe(const QString& collection, const QJsonArray& params) { QJsonObject json; - json["msg"] = "sub"; - json["id"] = QString::number(m_uid); - json["name"] = collection; - json["params"] = params; + json[QStringLiteral("msg")] = "sub"; + json[QStringLiteral("id")] = QString::number(m_uid); + json[QStringLiteral("name")] = collection; + json[QStringLiteral("params")] = params; qint64 bytes = m_webSocket.sendTextMessage(QJsonDocument(json).toJson(QJsonDocument::Compact)); if (bytes < json.length()) { - qDebug() << "ERROR! I couldn't send all of my message. This is a bug! (try again)"; + qCDebug(RUQOLA_LOG) << "ERROR! I couldn't send all of my message. This is a bug! (try again)"; } m_uid++; } void DDPClient::onTextMessageReceived(const QString &message) { QJsonDocument response = QJsonDocument::fromJson(message.toUtf8()); if (!response.isNull() && response.isObject()) { QJsonObject root = response.object(); - QString messageType = root.value("msg").toString(); + QString messageType = root.value(QStringLiteral("msg")).toString(); - if (messageType == "updated") { + if (messageType == QLatin1String("updated")) { - } else if (messageType == "result") { + } else if (messageType == QLatin1String("result")) { - unsigned id = root.value("id").toString().toInt(); + unsigned id = root.value(QStringLiteral("id")).toString().toInt(); if (m_callbackHash.contains(id)) { std::function callback = m_callbackHash.take(id); - callback( QJsonDocument(root.value("result").toObject()) ); + callback( QJsonDocument(root.value(QStringLiteral("result")).toObject()) ); } - emit result(id, QJsonDocument(root.value("result").toObject())); + emit result(id, QJsonDocument(root.value(QStringLiteral("result")).toObject())); if (id == m_loginJob) { - if (root.value("error").toObject().value("error").toInt() == 403) { - qDebug() << "Wrong password or token expired"; + if (root.value(QStringLiteral("error")).toObject().value(QStringLiteral("error")).toInt() == 403) { + qCDebug(RUQOLA_LOG) << "Wrong password or token expired"; login(); // Let's keep trying to log in } else { - Ruqola::self()->setAuthToken(root.value("result").toObject().value("token").toString()); + Ruqola::self()->setAuthToken(root.value(QStringLiteral("result")).toObject().value(QStringLiteral("token")).toString()); setLoginStatus(DDPClient::LoggedIn); } } - } else if (messageType == "connected") { - qDebug() << "Connected"; + } else if (messageType == QLatin1String("connected")) { + qCDebug(RUQOLA_LOG) << "Connected"; m_connected = true; emit connectedChanged(); setLoginStatus(DDPClient::LoggingIn); //Ruqola::self()->authentication()->OAuthLogin(); login(); // Try to resume auth token login - } else if (messageType == "error") { - qDebug() << "ERROR!!" << message; - } else if (messageType == "ping") { - qDebug() << "Ping - Pong"; + } else if (messageType == QLatin1String("error")) { + qCDebug(RUQOLA_LOG) << "ERROR!!" << message; + } else if (messageType == QLatin1String("ping")) { + qCDebug(RUQOLA_LOG) << "Ping - Pong"; QJsonObject pong; - pong["msg"] = "pong"; + pong[QStringLiteral("msg")] = "pong"; m_webSocket.sendBinaryMessage(QJsonDocument(pong).toJson(QJsonDocument::Compact)); - } else if (messageType == "added"){ - qDebug() << "ADDING" <password().isEmpty()) { // If we have a password and we couldn't log in, let's stop here if (m_attemptedPasswordLogin) { setLoginStatus(LoginFailed); return; } m_attemptedPasswordLogin = true; QJsonObject user; - user["username"] = Ruqola::self()->userName(); + user[QStringLiteral("username")] = Ruqola::self()->userName(); QJsonObject json; - json["password"] = Ruqola::self()->password(); - json["user"] = user; - m_loginJob = method("login", QJsonDocument(json)); + json[QStringLiteral("password")] = Ruqola::self()->password(); + json[QStringLiteral("user")] = user; + m_loginJob = method(QStringLiteral("login"), QJsonDocument(json)); } else if (!Ruqola::self()->authToken().isEmpty() && !m_attemptedTokenLogin) { m_attemptedPasswordLogin = true; QJsonObject json; - json["resume"] = Ruqola::self()->authToken(); - m_loginJob = method("login", QJsonDocument(json)); + json[QStringLiteral("resume")] = Ruqola::self()->authToken(); + m_loginJob = method(QStringLiteral("login"), QJsonDocument(json)); } else { setLoginStatus(LoginFailed); } } void DDPClient::onWSConnected() { - qDebug() << "Websocket connected at URL" << m_url; + qCDebug(RUQOLA_LOG) << "Websocket connected at URL" << m_url; QJsonArray supportedVersions; supportedVersions.append("1"); QJsonObject protocol; - protocol["msg"] = "connect"; - protocol["version"] = "1"; - protocol["support"] = supportedVersions; + protocol[QStringLiteral("msg")] = "connect"; + protocol[QStringLiteral("version")] = "1"; + protocol[QStringLiteral("support")] = supportedVersions; QByteArray serialize = QJsonDocument(protocol).toJson(QJsonDocument::Compact); qint64 bytes = m_webSocket.sendTextMessage(serialize); if (bytes < serialize.length()) { - qDebug() << "onWSConnected: ERROR! I couldn't send all of my message. This is a bug! (try again)"; + qCDebug(RUQOLA_LOG) << "onWSConnected: ERROR! I couldn't send all of my message. This is a bug! (try again)"; } else { - qDebug() << "Successfully sent " << serialize; + qCDebug(RUQOLA_LOG) << "Successfully sent " << serialize; } } void DDPClient::onWSclosed() { - qDebug() << "WebSocket CLOSED" << m_webSocket.closeReason() << m_webSocket.error() << m_webSocket.closeCode(); + qCDebug(RUQOLA_LOG) << "WebSocket CLOSED" << m_webSocket.closeReason() << m_webSocket.error() << m_webSocket.closeCode(); setLoginStatus(NotConnected); } diff --git a/src/messagemodel.cpp b/src/messagemodel.cpp index f49c7be2..b388cab0 100644 --- a/src/messagemodel.cpp +++ b/src/messagemodel.cpp @@ -1,264 +1,265 @@ /* * * Copyright 2016 Riccardo Iaconelli * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include #include #include #include #include // #include #include #include #include "messagemodel.h" #include "ruqola.h" +#include "ruqola_debug.h" //Message::MessageStatus Message::messageStatus() const //{ // return m_messageStatus; //} //void Message::setMessageStatus(MessageStatus m) //{ // if (m_messageStatus!= m){ // m_messageStatus = m; // emit messageStatusChanged(); // } //} Message MessageModel::fromJSon(const QJsonObject& o) { Message message; - message.messageID = o["messageID"].toString(); - message.roomID = o["roomID"].toString(); - message.message = o["message"].toString(); - message.timestamp = (qint64) o["timestamp"].toDouble(); - message.username = o["username"].toString(); - message.userID = o["userID"].toString(); - message.updatedAt = (qint64) o["updatedAt"].toDouble(); - message.editedAt = (qint64) o["editedAt"].toDouble(); - message.editedByUsername = o["editedByUsername"].toString(); - message.editedByUserID = o["editedByUserID"].toString(); - message.url = o["url"].toString(); - message.meta = o["meta"].toString(); - message.headers = o["headers"].toString(); - message.parsedUrl = o["parsedUrl"].toString(); - message.imageUrl = o["imageUrl"].toString(); - message.color = o["color"].toString(); - message.alias = o["alias"].toString(); - message.avatar = o["avatar"].toString(); - message.groupable = o["groupable"].toBool(); - message.parseUrls = o["parseUrls"].toBool(); + message.messageID = o[QStringLiteral("messageID")].toString(); + message.roomID = o[QStringLiteral("roomID")].toString(); + message.message = o[QStringLiteral("message")].toString(); + message.timestamp = (qint64) o[QStringLiteral("timestamp")].toDouble(); + message.username = o[QStringLiteral("username")].toString(); + message.userID = o[QStringLiteral("userID")].toString(); + message.updatedAt = (qint64) o[QStringLiteral("updatedAt")].toDouble(); + message.editedAt = (qint64) o[QStringLiteral("editedAt")].toDouble(); + message.editedByUsername = o[QStringLiteral("editedByUsername")].toString(); + message.editedByUserID = o[QStringLiteral("editedByUserID")].toString(); + message.url = o[QStringLiteral("url")].toString(); + message.meta = o[QStringLiteral("meta")].toString(); + message.headers = o[QStringLiteral("headers")].toString(); + message.parsedUrl = o[QStringLiteral("parsedUrl")].toString(); + message.imageUrl = o[QStringLiteral("imageUrl")].toString(); + message.color = o[QStringLiteral("color")].toString(); + message.alias = o[QStringLiteral("alias")].toString(); + message.avatar = o[QStringLiteral("avatar")].toString(); + message.groupable = o[QStringLiteral("groupable")].toBool(); + message.parseUrls = o[QStringLiteral("parseUrls")].toBool(); - message.systemMessage = o["systemMessage"].toBool(); - message.systemMessageType = o["type"].toString(); + message.systemMessage = o[QStringLiteral("systemMessage")].toBool(); + message.systemMessageType = o[QStringLiteral("type")].toString(); return message; } QByteArray MessageModel::serialize(const Message& message) { QJsonDocument d; QJsonObject o; - o["messageID"] = message.messageID; - o["roomID"] = message.roomID; - o["message"] = message.message; - o["timestamp"] = message.timestamp; - o["username"] = message.username; - o["userID"] = message.userID; - o["updatedAt"] = message.updatedAt; - o["editedAt"] = message.editedAt; - o["editedByUsername"] = message.editedByUsername; - o["editedByUserID"] = message.editedByUserID; - o["url"] = message.url; - o["meta"] = message.meta; - o["headers"] = message.headers; - o["parsedUrl"] = message.parsedUrl; - o["imageUrl"] = message.imageUrl; - o["color"] = message.color; - o["alias"] = message.alias; - o["avatar"] = message.avatar; - o["groupable"] = message.groupable; - o["parseUrls"] = message.parseUrls; + o[QStringLiteral("messageID")] = message.messageID; + o[QStringLiteral("roomID")] = message.roomID; + o[QStringLiteral("message")] = message.message; + o[QStringLiteral("timestamp")] = message.timestamp; + o[QStringLiteral("username")] = message.username; + o[QStringLiteral("userID")] = message.userID; + o[QStringLiteral("updatedAt")] = message.updatedAt; + o[QStringLiteral("editedAt")] = message.editedAt; + o[QStringLiteral("editedByUsername")] = message.editedByUsername; + o[QStringLiteral("editedByUserID")] = message.editedByUserID; + o[QStringLiteral("url")] = message.url; + o[QStringLiteral("meta")] = message.meta; + o[QStringLiteral("headers")] = message.headers; + o[QStringLiteral("parsedUrl")] = message.parsedUrl; + o[QStringLiteral("imageUrl")] = message.imageUrl; + o[QStringLiteral("color")] = message.color; + o[QStringLiteral("alias")] = message.alias; + o[QStringLiteral("avatar")] = message.avatar; + o[QStringLiteral("groupable")] = message.groupable; + o[QStringLiteral("parseUrls")] = message.parseUrls; - o["systemMessage"] = message.systemMessage; - o["type"] = message.systemMessageType; + o[QStringLiteral("systemMessage")] = message.systemMessage; + o[QStringLiteral("type")] = message.systemMessageType; d.setObject(o); return d.toBinaryData(); } MessageModel::MessageModel(const QString &roomID, QObject* parent) : QAbstractListModel(parent), m_roomID(roomID) { - qDebug() << "Creating message Model"; - QDir cacheDir(Ruqola::self()->cacheBasePath()+"/rooms_cache"); + qCDebug(RUQOLA_LOG) << "Creating message Model"; + QDir cacheDir(Ruqola::self()->cacheBasePath()+QStringLiteral("/rooms_cache")); // load cache if (QFile::exists(cacheDir.absoluteFilePath(roomID)) && !roomID.isEmpty()) { QFile f(cacheDir.absoluteFilePath(roomID)); if (f.open(QIODevice::ReadOnly)) { QDataStream in(&f); while (!f.atEnd()) { char * byteArray; quint32 length; in.readBytes(byteArray, length); QByteArray arr = QByteArray::fromRawData(byteArray, length); Message m = MessageModel::fromJSon(QJsonDocument::fromBinaryData(arr).object()); addMessage(m); } } } } MessageModel::~MessageModel() { - QDir cacheDir(Ruqola::self()->cacheBasePath()+"/rooms_cache"); - qDebug() << "Caching to..." << cacheDir.path(); + QDir cacheDir(Ruqola::self()->cacheBasePath()+QStringLiteral("/rooms_cache")); + qCDebug(RUQOLA_LOG) << "Caching to..." << cacheDir.path(); if (!cacheDir.exists(cacheDir.path())) { cacheDir.mkpath(cacheDir.path()); } QFile f(cacheDir.absoluteFilePath(m_roomID)); if (f.open(QIODevice::WriteOnly)) { QDataStream out(&f); foreach (const Message m, m_allMessages) { QByteArray ms = MessageModel::serialize(m); out.writeBytes(ms, ms.size()); } } } QHash MessageModel::roleNames() const { QHash roles; roles[MessageText] = "messageText"; roles[Username] = "username"; roles[Timestamp] = "timestamp"; roles[UserID] = "userID"; roles[SystemMessage] = "systemMessage"; roles[SystemMessageType] = "type"; roles[MessageID] = "messageID"; roles[RoomID] = "roomID"; roles[UpdatedAt] = "updatedAt"; roles[EditedAt] = "editedAt"; roles[EditedByUserName] = "editedByUsername"; roles[EditedByUserID] = "editedByUserID"; roles[Url] = "URL"; roles[Meta] = "meta"; roles[Headers] = "headers"; roles[ParsedUrl] = "parsedUrl"; roles[ImageUrl] = "imageUrl"; roles[Color] = "color"; roles[Alias] = "alias"; roles[Avatar] = "avatar"; roles[Groupable] = "groupable"; roles[ParseUrls] = "parseUrls"; return roles; } qint64 MessageModel::lastTimestamp() const { if (m_allMessages.size()) { - qDebug() << "returning timestamp" << m_allMessages.last().timestamp; + qCDebug(RUQOLA_LOG) << "returning timestamp" << m_allMessages.last().timestamp; return m_allMessages.last().timestamp; } else { return 0; } } int MessageModel::rowCount(const QModelIndex& parent) const { return m_allMessages.size(); (void)parent; } void MessageModel::addMessage(const Message& message) { // Don't add empty messages if (message.message.isEmpty()) { return; } auto existingMessage = qFind(m_allMessages.begin(), m_allMessages.end(), message); bool present = (existingMessage != m_allMessages.end()); auto i = std::upper_bound(m_allMessages.begin(), m_allMessages.end(), message); int pos = i-m_allMessages.begin(); bool messageChanged = false; // if (qFind(m_allMessages.begin(), m_allMessages.end(), message) != m_allMessages.end()) { if (present){ // if (pos != m_allMessages.size()) { // we're at the end -// qDebug() << "detecting a message change"; +// qCDebug(RUQOLA_LOG) << "detecting a message change"; messageChanged = true; //Figure out a better way to update just the really changed message } else { beginInsertRows(QModelIndex(), pos, pos); } if (messageChanged) { m_allMessages.replace(pos-1, message); } else { m_allMessages.insert(i, message); } if (messageChanged) { emit dataChanged(createIndex(1, 1), createIndex(pos, 1)); } else { endInsertRows(); } } QVariant MessageModel::data(const QModelIndex& index, int role) const { int idx = index.row(); if (role == MessageModel::Username) { return m_allMessages.at(idx).username; } else if (role == MessageModel::MessageText) { return m_allMessages.at(idx).message; } else if (role == MessageModel::Timestamp) { return QVariant(m_allMessages.at(idx).timestamp); } else if (role == MessageModel::UserID) { return QVariant(m_allMessages.at(idx).userID); } else if (role == MessageModel::SystemMessage) { return QVariant(m_allMessages.at(idx).systemMessage); } else if (role == MessageModel::SystemMessageType) { return QVariant(m_allMessages.at(idx).systemMessageType); } else if (role == MessageModel::Alias) { return QVariant(m_allMessages.at(idx).alias); } else { - return QVariant(""); + return QVariant(QString()); } } // #include "messagelist.moc" diff --git a/src/messagemodel.h b/src/messagemodel.h index 9aa80a59..a4652174 100644 --- a/src/messagemodel.h +++ b/src/messagemodel.h @@ -1,205 +1,205 @@ /* * * Copyright 2016 Riccardo Iaconelli * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef MESSAGEMODEL_H #define MESSAGEMODEL_H #include #include #include #include #include #include class Message //: public QObject { // Q_OBJECT public: // enum MessageStatus { // Unsent, // Sending, // Sent, // SendFailed // }; // Q_ENUM(MessageStatus) // To be used in ID find: message ID inline bool operator==(const Message &other) const { return other.messageID == messageID; } // To be used in sorted insert: timestamp inline bool operator<(const Message &other) const { return timestamp < other.timestamp; } // MessageStatus messageStatus() const; // void setMessageStatus(MessageStatus m); //Message Object Fields // _id QString messageID; // rid QString roomID; // msg QString message; // ts qint64 timestamp; // u QString username; QString userID; // _updatedAt qint64 updatedAt; // editedAt qint64 editedAt; // editedBy QString editedByUsername; QString editedByUserID; // urls QString url; QString meta; QString headers; QString parsedUrl; // attachments QString imageUrl; QString color; // alias QString alias; // avatar QString avatar; // groupable bool groupable; // parseUrls bool parseUrls; bool systemMessage = false; QString systemMessageType; // MessageStatus m_messageStatus; signals: // void messageStatusChanged(); }; class MessageModel : public QAbstractListModel { Q_OBJECT public: enum MessageRoles { Username = Qt::UserRole + 1, MessageText, Timestamp, UserID, SystemMessage, SystemMessageType, MessageID, RoomID, UpdatedAt, EditedAt, EditedByUserName, EditedByUserID, Url, Meta, Headers, ParsedUrl, ImageUrl, Color, Alias, Avatar, Groupable, ParseUrls }; Q_ENUM(MessageRoles) - explicit MessageModel(const QString &roomID = "no_room", QObject *parent = nullptr); + explicit MessageModel(const QString &roomID = QStringLiteral("no_room"), QObject *parent = nullptr); virtual ~MessageModel(); /** * @brief Adds a message to QVector m_allMessages * * @param message The message to be added */ void addMessage(const Message& message); /** * @brief returns number of messages in QVector m_allMessages * * @param parent, it is void * @return int, The number of messages in QVector m_allMessages */ virtual int rowCount(const QModelIndex & parent = QModelIndex()) const; virtual QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const; /** * @brief Returns last timestamp of last message in QVector m_allMessages * * @return qint64 The last timestamp */ qint64 lastTimestamp() const; /** * @brief Constructs Message object from QJsonObject * * @param source The Json containing message attributes * @return Message object, The message constructed from Json */ static Message fromJSon(const QJsonObject &source); /** * @brief Constructs QBytearray from Message object * * @param message The Message object * @return QByteArray, The Json containing message attributes */ static QByteArray serialize(const Message &message); protected: virtual QHash roleNames() const; private: const QString m_roomID; QVector m_allMessages; QString m_writableLocation; }; #endif diff --git a/src/messagequeue.cpp b/src/messagequeue.cpp index 6cef432a..7cc91203 100644 --- a/src/messagequeue.cpp +++ b/src/messagequeue.cpp @@ -1,125 +1,126 @@ /* * * Copyright 2016 Riccardo Iaconelli * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "ruqola.h" #include "ddpclient.h" +#include "ruqola_debug.h" QPair MessageQueue::fromJson(const QJsonObject &object) { QPair pair; pair.first = object["method"].toString(); QJsonArray arr = object["params"].toArray(); pair.second = QJsonDocument(arr); return pair; } QByteArray MessageQueue::serialize(const QPair &pair) { QJsonDocument d; QJsonObject o; o["method"] = QJsonValue(pair.first); QJsonArray arr; if ( pair.second.isArray() ){ arr.append(pair.second.array()); } else if ( pair.second.isObject() ) { arr.append(pair.second.object()); } o["params"] = QJsonValue(arr); d.setObject(o); return d.toBinaryData(); } MessageQueue::MessageQueue(QObject *parent) : QObject(parent) { connect(Ruqola::self()->ddp(), &DDPClient::loginStatusChanged, this, &MessageQueue::onLoginStatusChanged); QDir cacheDir(Ruqola::self()->ddp()->cachePath()); // load unsent messages cache if (QFile::exists(cacheDir.absoluteFilePath("QueueCache"))) { QFile f(cacheDir.absoluteFilePath("QueueCache")); if (f.open(QIODevice::ReadOnly)) { QDataStream in(&f); while (!f.atEnd()) { char * byteArray; quint32 length; in.readBytes(byteArray, length); QByteArray ba = QByteArray::fromRawData(byteArray, length); QPair pair = MessageQueue::fromJson(QJsonDocument::fromBinaryData(ba).object()); QString method = pair.first; QJsonDocument params = pair.second; Ruqola::self()->ddp()->messageQueue().enqueue(qMakePair(method,params)); } } } } MessageQueue::~MessageQueue() { QDir cacheDir(Ruqola::self()->ddp()->cachePath()); - qDebug() << "Caching Unsent messages to... " << cacheDir.path(); + qCDebug(RUQOLA_LOG) << "Caching Unsent messages to... " << cacheDir.path(); if (!cacheDir.exists(cacheDir.path())) { cacheDir.mkpath(cacheDir.path()); } QFile f(cacheDir.absoluteFilePath("QueueCache")); if (f.open(QIODevice::WriteOnly)) { QDataStream out(&f); QQueue>::iterator it; QQueue> queue = Ruqola::self()->ddp()->messageQueue(); for ( it = queue.begin(); it != queue.end(); ++it ) { QPair pair = *it; QByteArray ba = serialize(pair); out.writeBytes(ba, ba.size()); } } } void MessageQueue::onLoginStatusChanged() { if (Ruqola::self()->loginStatus() == DDPClient::LoggedIn && !Ruqola::self()->ddp()->messageQueue().empty()){ //retry sending messages processQueue(); } } void MessageQueue::processQueue() { //can be optimized using single shot timer while ( Ruqola::self()->loginStatus() == DDPClient::LoggedIn && !Ruqola::self()->ddp()->messageQueue().empty() ){ QPair pair = Ruqola::self()->ddp()->messageQueue().head(); QString method = pair.first; QJsonDocument params = pair.second; Ruqola::self()->ddp()->method(method, params); } } diff --git a/src/rocketchatbackend.cpp b/src/rocketchatbackend.cpp index b3e66b3f..521c3593 100644 --- a/src/rocketchatbackend.cpp +++ b/src/rocketchatbackend.cpp @@ -1,253 +1,253 @@ /* * * Copyright 2016 Riccardo Iaconelli * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "rocketchatbackend.h" #include #include -#include +#include "ruqola_debug.h" #include "ruqola.h" #include "ddpclient.h" void debug_callback(const QJsonDocument &doc) { - qDebug() << "DEBUG:" << doc; + qCDebug(RUQOLA_LOG) << "DEBUG:" << doc; } void process_backlog(const QJsonDocument &messages) { - qDebug() << messages.object().value("messages").toArray().size(); + qCDebug(RUQOLA_LOG) << messages.object().value("messages").toArray().size(); RocketChatBackend::processIncomingMessages(messages.object().value("messages").toArray()); } void rooms_callback(const QJsonDocument &doc) { RoomModel *model = Ruqola::self()->roomModel(); QJsonArray removed = doc.object().value("remove").toArray(); QJsonArray updated = doc.object().value("update").toArray(); for (int i = 0; i < updated.size(); i++) { QJsonObject room = updated.at(i).toObject(); if (room.value("t").toString() != "d") { QString roomID = room.value("_id").toString(); MessageModel *roomModel = Ruqola::self()->getModelForRoom(roomID); // let's be extra safe around crashes if (Ruqola::self()->loginStatus() == DDPClient::LoggedIn) { Room r; r.id = roomID; r.name = room["name"].toString(); r.topic = room["topic"].toString(); - qDebug() << "Adding room" << r.name << r.id << r.topic; + qCDebug(RUQOLA_LOG) << "Adding room" << r.name << r.id << r.topic; model->addRoom(r); } QJsonArray params; params.append(QJsonValue(roomID)); Ruqola::self()->ddp()->subscribe("stream-room-messages", params); // Load history params.append(QJsonValue(QJsonValue::Null)); params.append(QJsonValue(50)); // Max number of messages to load; QJsonObject dateObject; dateObject["$date"] = QJsonValue(roomModel->lastTimestamp()); params.append(dateObject); Ruqola::self()->ddp()->method("loadHistory", QJsonDocument(params), process_backlog); } } } void subs_callback(const QJsonDocument &doc) { RoomModel *model = Ruqola::self()->roomModel(); QJsonArray removed = doc.object().value("remove").toArray(); QJsonArray updated = doc.object().value("update").toArray(); for (int i = 0; i < updated.size(); i++) { QJsonObject room = updated.at(i).toObject(); if (room.value("t").toString() != "d") { QString roomID = room.value("rid").toString(); MessageModel *roomModel = Ruqola::self()->getModelForRoom(roomID); // let's be extra safe around crashes if (Ruqola::self()->loginStatus() == DDPClient::LoggedIn) { Room r; r.id = roomID; r.name = room["name"].toString(); r.topic = room["topic"].toString(); -// qDebug() << "Adding room" << r.name << r.id << r.topic; +// qCDebug(RUQOLA_LOG) << "Adding room" << r.name << r.id << r.topic; model->addRoom(r); } QJsonArray params; params.append(QJsonValue(roomID)); Ruqola::self()->ddp()->subscribe("stream-room-messages", params); // Load history params.append(QJsonValue(QJsonValue::Null)); params.append(QJsonValue(50)); // Max number of messages to load; QJsonObject dateObject; dateObject["$date"] = QJsonValue(roomModel->lastTimestamp()); params.append(dateObject); Ruqola::self()->ddp()->method("loadHistory", QJsonDocument(params), process_backlog); } } } void RocketChatBackend::processIncomingMessages(const QJsonArray &messages) { foreach (const QJsonValue &v, messages) { QJsonObject o = v.toObject(); Message m; QString roomId = o.value("rid").toString(); QString type = o.value("t").toString(); m.messageID = o.value("_id").toString(); m.roomID = roomId; m.message = o.value("msg").toString(); m.timestamp = (qint64)o.value("ts").toObject().value("$date").toDouble(); m.username = o.value("u").toObject().value("username").toString(); m.userID = o.value("u").toObject().value("_id").toString(); m.updatedAt = o.value("_updatedAt").toObject().value("$date").toDouble(); m.editedAt = o.value("editedAt").toObject().value("$date").toDouble(); m.editedByUsername = o.value("editedBy").toObject().value("username").toString(); m.editedByUserID = o.value("editedBy").toObject().value("userID").toString(); m.url = o.value("urls").toObject().value("url").toString(); m.meta = o.value("urls").toObject().value("meta").toString(); m.headers = o.value("urls").toObject().value("headers").toString(); m.parsedUrl = o.value("urls").toObject().value("parsedUrl").toString(); m.imageUrl = o.value("attachments").toObject().value("imageUrl").toString(); m.color = o.value("attachments").toObject().value("color").toString(); m.alias = o.value("alias").toString(); m.avatar = o.value("avatar").toString(); m.groupable = o.value("groupable").toBool(); m.parseUrls = o.value("parseUrls").toBool(); if (!type.isEmpty()) { m.systemMessage = true; m.systemMessageType = type; } else { m.systemMessage = false; } Ruqola::self()->getModelForRoom(roomId)->addMessage(m); } } RocketChatBackend::RocketChatBackend(QObject* parent) : QObject(parent) { connect(Ruqola::self(), &Ruqola::loginStatusChanged, this, &RocketChatBackend::onLoginStatusChanged); connect(Ruqola::self(), &Ruqola::userIDChanged, this, &RocketChatBackend::onUserIDChanged); connect(Ruqola::self()->ddp(), &DDPClient::changed, this, &RocketChatBackend::onChanged); connect(Ruqola::self()->ddp(), &DDPClient::added, this, &RocketChatBackend::onAdded); } RocketChatBackend::~RocketChatBackend() { } void RocketChatBackend::onLoginStatusChanged() { if (Ruqola::self()->loginStatus() == DDPClient::LoggedIn) { -// qDebug() << "GETTING LIST OF ROOMS"; +// qCDebug(RUQOLA_LOG) << "GETTING LIST OF ROOMS"; QJsonObject params; params["$date"] = QJsonValue(0); // get ALL rooms we've ever seen Ruqola::self()->ddp()->method("rooms/get", QJsonDocument(params), rooms_callback); } } void RocketChatBackend::onLoggedIn() { } void RocketChatBackend::onAdded(const QJsonObject &object) { QString collection = object.value("collection").toString(); if (collection == "stream-room-messages") { } else if (collection == "users") { QJsonObject fields = object.value("fields").toObject(); QString username = fields.value("username").toString(); if(username == Ruqola::self()->userName()){ Ruqola::self()->setUserID(object["id"].toString()); - qDebug() << "User id set to " << Ruqola::self()->userID(); + qCDebug(RUQOLA_LOG) << "User id set to " << Ruqola::self()->userID(); } - qDebug() << "NEW USER ADDED: " << username; + qCDebug(RUQOLA_LOG) << "NEW USER ADDED: " << username; } else if (collection == "rooms") { } else if (collection == "stream-notify-user"){ } } void RocketChatBackend::onChanged(const QJsonObject &object) { QString collection = object["collection"].toString(); if (collection == "stream-room-messages") { QJsonObject fields = object.value("fields").toObject(); QString roomId = fields.value("eventName").toString(); QJsonArray contents = fields.value("args").toArray(); processIncomingMessages(contents); } else if (collection == "users") { - qDebug() << "USER CHANGED"; + qCDebug(RUQOLA_LOG) << "USER CHANGED"; } else if (collection == "rooms") { } else if (collection == "stream-notify-user") { QJsonObject fields = object.value("fields").toObject(); QJsonArray contents = fields.value("args").toArray(); QString message = contents.at(0).toObject()["text"].toString(); Ruqola::self()->notification()->showMessage("New message", message, QSystemTrayIcon::Information, 5000 ); - qDebug() << "New notification" << object.value("fields").toObject(); + qCDebug(RUQOLA_LOG) << "New notification" << object.value("fields").toObject(); } } void RocketChatBackend::onUserIDChanged() { - qDebug() << "subscribing to notification feed"; + qCDebug(RUQOLA_LOG) << "subscribing to notification feed"; QJsonArray params; params.append(QJsonValue(QString("%1/%2").arg(Ruqola::self()->userID()).arg(QString("notification")))); Ruqola::self()->ddp()->subscribe("stream-notify-user", params); } diff --git a/src/roommodel.cpp b/src/roommodel.cpp index a39da699..87415a10 100644 --- a/src/roommodel.cpp +++ b/src/roommodel.cpp @@ -1,267 +1,269 @@ /* * * Copyright 2016 Riccardo Iaconelli * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "roommodel.h" #include "ruqola.h" +#include "ruqola_debug.h" #include #include Room RoomModel::fromJSon(const QJsonObject& o) { Room r; - r.id = o["id"].toString(); - r.type = o["t"].toString(); - r.name = o["name"].toString(); - r.userName = o["userName"].toString(); - r.userID = o["userID"].toString(); - r.topic = o["topic"].toString(); - r.mutedUsers = o["mutedUsers"].toString(); - r.jitsiTimeout = o["jitsiTimeout"].toDouble(); - r.ro = o["ro"].toBool(); - r.unread = o["unread"].toInt(0); + + r.id = o[QStringLiteral("id")].toString(); + r.type = o[QStringLiteral("t")].toString(); + r.name = o[QStringLiteral("name")].toString(); + r.userName = o[QStringLiteral("userName")].toString(); + r.userID = o[QStringLiteral("userID")].toString(); + r.topic = o[QStringLiteral("topic")].toString(); + r.mutedUsers = o[QStringLiteral("mutedUsers")].toString(); + r.jitsiTimeout = o[QStringLiteral("jitsiTimeout")].toDouble(); + r.ro = o[QStringLiteral("ro")].toBool(); + r.unread = o[QStringLiteral("unread")].toInt(0); return r; } QByteArray RoomModel::serialize(const Room& r) { QJsonDocument d; QJsonObject o; - o["id"] = r.id; - o["t"] = r.type; - o["name"] = r.name; - o["userName"] = r.userName; - o["userID"] = r.userID; - o["topic"] = r.topic; - o["mutedUsers"] = r.mutedUsers; - o["jitsiTimeout"] = r.jitsiTimeout; - o["ro"] = r.ro; - o["unread"] = r.unread; + o[QStringLiteral("id")] = r.id; + o[QStringLiteral("t")] = r.type; + o[QStringLiteral("name")] = r.name; + o[QStringLiteral("userName")] = r.userName; + o[QStringLiteral("userID")] = r.userID; + o[QStringLiteral("topic")] = r.topic; + o[QStringLiteral("mutedUsers")] = r.mutedUsers; + o[QStringLiteral("jitsiTimeout")] = r.jitsiTimeout; + o[QStringLiteral("ro")] = r.ro; + o[QStringLiteral("unread")] = r.unread; d.setObject(o); return d.toBinaryData(); } RoomWrapper::RoomWrapper(QObject *parent) : QObject(parent) {} RoomWrapper::RoomWrapper(const Room &r, QObject *parent) : QObject(parent) { m_name = r.name; m_topic = r.topic; m_unread = r.unread; m_id = r.id; m_selected = r.selected; } QString RoomWrapper::getName() const { return m_name; } QString RoomWrapper::getTopic() const { return m_topic; } RoomModel::RoomModel(QObject* parent) : QAbstractListModel(parent) {} RoomModel::~RoomModel() { QDir cacheDir(Ruqola::self()->cacheBasePath()); if (!cacheDir.exists(cacheDir.path())) { cacheDir.mkpath(cacheDir.path()); } - QFile f(cacheDir.absoluteFilePath("rooms")); + QFile f(cacheDir.absoluteFilePath(QStringLiteral("rooms"))); if (f.open(QIODevice::WriteOnly)) { QDataStream out(&f); foreach (const Room m, m_roomsList) { QByteArray ms = RoomModel::serialize(m); out.writeBytes(ms, ms.size()); } } } void RoomModel::clear() { if (m_roomsList.size()) { beginRemoveRows(QModelIndex(), 0, rowCount()-1); m_roomsList.clear(); QAbstractItemModel::endRemoveRows(); } } RoomWrapper* RoomModel::findRoom(const QString& roomID) const { foreach (const Room r, m_roomsList) { if (r.id == roomID) { return new RoomWrapper(r); } } Room r; return new RoomWrapper(r); } // Clear data and refill it with data in the cache, if there is void RoomModel::reset() { if (Ruqola::self()->cacheBasePath().isEmpty()) { return; } clear(); QDir cacheDir(Ruqola::self()->cacheBasePath()); // load cache if (cacheDir.exists(cacheDir.path())) { - QFile f(cacheDir.absoluteFilePath("rooms")); + QFile f(cacheDir.absoluteFilePath(QStringLiteral("rooms"))); if (f.open(QIODevice::ReadOnly)) { QDataStream in(&f); while (!f.atEnd()) { char * byteArray; quint32 length; in.readBytes(byteArray, length); QByteArray arr = QByteArray::fromRawData(byteArray, length); Room m = RoomModel::fromJSon(QJsonDocument::fromBinaryData(arr).object()); addRoom(m.id, m.name, m.selected); } } - qDebug() << "Cache Loaded"; + qCDebug(RUQOLA_LOG) << "Cache Loaded"; } } QHash RoomModel::roleNames() const { QHash roles; roles[RoomName] = "name"; roles[RoomID] = "room_id"; roles[RoomSelected] = "selected"; roles[RoomUnread] = "unread"; roles[RoomType] = "type"; roles[RoomUserName] = "username"; roles[RoomUserID] = "userID"; roles[RoomTopic] = "topic"; roles[RoomMutedUsers] = "mutedUsers"; roles[RoomJitsiTimeout] = "jitsiTimeout"; roles[RoomRO] = "readOnly"; return roles; } int RoomModel::rowCount(const QModelIndex & parent) const { Q_UNUSED(parent); return m_roomsList.size(); } QVariant RoomModel::data(const QModelIndex & index, int role) const { Room r = m_roomsList.at(index.row()); if (role == RoomModel::RoomName) { return r.name; } else if (role == RoomModel::RoomID) { return r.id; } else if (role == RoomModel::RoomSelected) { return r.selected; } else if (role == RoomModel::RoomType) { return r.type; } else if (role == RoomModel::RoomUserID) { return r.userID; } else if (role == RoomModel::RoomUserName) { return r.userName; } else if (role == RoomModel::RoomTopic) { return r.topic; } else if (role == RoomModel::RoomMutedUsers) { return r.mutedUsers; } else if (role == RoomModel::RoomJitsiTimeout) { return r.jitsiTimeout; } else if (role == RoomModel::RoomRO) { return r.ro; } else { - return QVariant("0"); + return QVariant(QStringLiteral("0")); } } void RoomModel::addRoom(const QString& roomID, const QString& roomName, bool selected) { if (roomID.isEmpty() || roomName.isEmpty()) { return; } - qDebug() << "Adding room" << roomID << roomName; + qCDebug(RUQOLA_LOG) << "Adding room" << roomID << roomName; Room r; r.id = roomID; r.name = roomName; r.selected = selected; addRoom(r); } void RoomModel::addRoom(const Room &room) { auto existingRoom = qFind(m_roomsList.begin(), m_roomsList.end(), room); bool present = (existingRoom != m_roomsList.end()); auto i = qUpperBound(m_roomsList.begin(), m_roomsList.end(), room); int pos = i-m_roomsList.begin(); bool roomChanged = false; - qDebug() << pos; + qCDebug(RUQOLA_LOG) << pos; // if (qFind(m_roomsList.begin(), m_roomsList.end(), room) != m_roomsList.end() && pos > 0) { if (present) { -// qDebug() << (qFind(m_roomsList.begin(), m_roomsList.end(), room) - m_roomsList.begin()); +// qCDebug(RUQOLA_LOG) << (qFind(m_roomsList.begin(), m_roomsList.end(), room) - m_roomsList.begin()); // if (pos != m_roomsList.size()) { // we're at the end - qDebug() << "Room changed!"; + qCDebug(RUQOLA_LOG) << "Room changed!"; roomChanged = true; //Figure out a better way to update just the really changed message } else { beginInsertRows(QModelIndex(), pos, pos); } if (roomChanged) { m_roomsList.replace(pos-1, room); } else { - qDebug() << "Inserting room at position" <getModelForRoom(room.id); } // #include "roommodel.moc" diff --git a/src/ruqola.cpp b/src/ruqola.cpp index 226371a1..b42869fb 100644 --- a/src/ruqola.cpp +++ b/src/ruqola.cpp @@ -1,328 +1,329 @@ /* * * Copyright 2016 Riccardo Iaconelli * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "ruqola.h" #include "roommodel.h" #include "ddpclient.h" #include "notification.h" #include "messagequeue.h" +#include "ruqola_debug.h" #include #include #include #include Ruqola *Ruqola::m_self = nullptr; QString Ruqola::authToken() const { return m_authToken; } QString Ruqola::userName() const { return m_userName; } QString Ruqola::userID() const { return m_userID; } QString Ruqola::password() const { return m_password; } void Ruqola::setAuthToken(const QString& token) { - qDebug() << "Setting token to" << token; + qCDebug(RUQOLA_LOG) << "Setting token to" << token; QSettings s; m_authToken = token; s.setValue("authToken", token); } void Ruqola::setPassword(const QString& password) { m_password = password; } void Ruqola::setUserName(const QString& username) { m_userName = username; QSettings s; s.setValue("username", username); emit userNameChanged(); } void Ruqola::setUserID(const QString& userID) { m_userName = userID; QSettings s; s.setValue("userID", userID); emit userIDChanged(); } RoomModel * Ruqola::roomModel() { if (!m_roomModel) { - qDebug() << "creating new RoomModel"; + qCDebug(RUQOLA_LOG) << "creating new RoomModel"; m_roomModel = new RoomModel(this); - qDebug() << m_roomModel; + qCDebug(RUQOLA_LOG) << m_roomModel; } return m_roomModel; } DDPClient * Ruqola::ddp() { if (!m_ddp) { m_ddp = new DDPClient(serverURL()); connect(m_ddp, &DDPClient::loginStatusChanged, this, &Ruqola::loginStatusChanged); } return m_ddp; } MessageQueue * Ruqola::messageQueue() { if (!m_messageQueue) { m_messageQueue = new MessageQueue(); // retry to send any unsent messages Ruqola::self()->messageQueue()->processQueue(); } return m_messageQueue; } Notification * Ruqola::notification() { if (!m_notification) { m_notification = new Notification(); m_notification->show(); } return m_notification; } Authentication * Ruqola::authentication() { if (!m_authentication) { m_authentication = new Authentication(); } return m_authentication; } void Ruqola::attachmentButtonClicked() { QString fileName = QFileDialog::getOpenFileName(Q_NULLPTR, tr("Select one or more files to open"), QDir::homePath(), tr("Images (*.png *.jpeg *.jpg)")); if (fileName.isEmpty()) { return; } - qDebug() << "Selected Image " << fileName; + qCDebug(RUQOLA_LOG) << "Selected Image " << fileName; QFile file(fileName); if (!file.open(QFile::ReadOnly)) { - qDebug() << "Cannot open the selected file"; + qCDebug(RUQOLA_LOG) << "Cannot open the selected file"; return; } const QString message = QString::fromLatin1(file.readAll().toBase64()); const QString roomID("3cGRyFLWgnPL7B79n"); //hard code roomID for now const QString type("image"); sendMessage(roomID, message, type); } void Ruqola::sendMessage(const QString &roomID, const QString &message, const QString &type) { QJsonObject json; json["rid"] = roomID; json["msg"] = message; json["type"] = type; ddp()->method("sendMessage", QJsonDocument(json), DDPClient::Persistent); } MessageModel * Ruqola::getModelForRoom(const QString& roomID) { if (m_messageModels.contains(roomID)) { return m_messageModels.value(roomID); } else { m_messageModels[roomID] = new MessageModel(roomID, this); return m_messageModels[roomID]; } } QString Ruqola::serverURL() const { return m_serverURL; } void Ruqola::setServerURL(const QString& serverURL) { if (m_serverURL == serverURL) { return; } QSettings s; s.setValue("serverURL", serverURL); m_serverURL = serverURL; emit serverURLChanged(); } DDPClient::LoginStatus Ruqola::loginStatus() { if (m_ddp) { return ddp()->loginStatus(); } else { return DDPClient::LoggedOut; } } void Ruqola::tryLogin() { - qDebug() << "Attempting login" << userName() << "on" << serverURL(); + qCDebug(RUQOLA_LOG) << "Attempting login" << userName() << "on" << serverURL(); // Reset model views foreach (const QString key, m_messageModels.keys()) { MessageModel *m = m_messageModels.take(key); delete m; } delete m_ddp; m_ddp = 0; // This creates a new ddp() object. // DDP will automatically try to connect and login. ddp(); // In the meantime, load cache... //if(Ruqola::self()->ddp()->isConnected() && Ruqola::self()->loginStatus() == DDPClient::LoggedIn) { m_roomModel->reset(); //} } void Ruqola::tryOAuthLogin() { // Reset model views foreach (const QString key, m_messageModels.keys()) { MessageModel *m = m_messageModels.take(key); delete m; } delete m_ddp; m_ddp = 0; // This creates a new ddp() object. // DDP will automatically try to connect and login. ddp(); m_roomModel->reset(); if(Ruqola::self()->ddp()->isConnected()){ m_authentication->OAuthLogin(); } } void Ruqola::logOut() { QSettings s; s.setValue("authToken", QString("")); setAuthToken(QString("")); setPassword(QString("")); foreach (const QString &key, m_messageModels.keys()) { MessageModel *m = m_messageModels.take(key); delete m; } m_roomModel->clear(); QJsonObject user; user["username"] = Ruqola::self()->userName(); QJsonObject json; json["user"] = user; Ruqola::self()->ddp()->method("logout", QJsonDocument(json)); delete m_ddp; m_ddp = nullptr; emit loginStatusChanged(); - qDebug() << "Successfully logged out!"; + qCDebug(RUQOLA_LOG) << "Successfully logged out!"; } QString Ruqola::cacheBasePath() const { if (m_serverURL.isEmpty()) { return QString(); } return QStandardPaths::writableLocation(QStandardPaths::CacheLocation)+'/'+m_serverURL; } RoomWrapper * Ruqola::getRoom(const QString& roomID) { return roomModel()->findRoom(roomID); } Ruqola::Ruqola(QObject* parent): QObject(parent), m_ddp(0), m_messageQueue(0), m_roomModel(0), m_notification(0), m_authentication(0) { QSettings s; m_serverURL = s.value("serverURL", "demo.rocket.chat").toString(); m_userName = s.value("username").toString(); m_userID = s.value("userID").toString(); m_authToken = s.value("authToken").toString(); } Ruqola * Ruqola::self() { if (!m_self) { m_self = new Ruqola; // Create DDP object so we try to connect at startup m_self->ddp(); // Clear rooms data and refill it with data in the cache, if there is m_self->roomModel()->reset(); // Create systray to show notifications on Desktop #if !defined(Q_OS_ANDROID) || !defined(Q_OS_IOS) m_self->notification(); #endif //Initialize the messageQueue object m_self->messageQueue(); //Initialize the OAuth object m_self->authentication(); } return m_self; }