Compare commits

...

30 Commits

Author SHA1 Message Date
Blue d162494ec8
Better way to store expanded elements in roster, several clean ups, translations 6 months ago
Blue 7e9eed2075
First tray attempt, seems to be working 6 months ago
Blue 7192286aeb
fix some bugs about disabled menus 8 months ago
Blue 645b92ba51
release 0.2.2 preparation 9 months ago
Blue 80c5e2f2b4
added en lolcalization file, actualized localizations 9 months ago
Blue 1f065f23e6
double click word selection handle, sigint sermentation fault fix 9 months ago
Blue 3c48577eee
selection message body now actually working 9 months ago
Blue 0340db7f2f
first successfull attempt to visualize selection on message body 9 months ago
Blue c3a45ec58c
merge conflicts, text copying from context menu in message line 9 months ago
Blue 7ba94e9deb
link clicking and hovering in message body now works! 9 months ago
Blue eac87e713f
seem to have found a text block, to activate with the click later 10 months ago
Blue d86e2c28a0
an attempt to display text in a better way with QTextDocument + QTextBrowser 10 months ago
Blue 2fcc432aef
some polish 10 months ago
Blue e58213b294
Now notifications have actions! Some more usefull functions to roster model 10 months ago
Blue 3916aec358
unread messages count now is displayed on the launcher icon 10 months ago
Blue 721d3a1a89
refactoring: UI squawk now belongs to a new class, it enables me doing trayed mode, when main window is destroyed 10 months ago
Blue 83cb220175
better notification sending, edited message now modifies notification (or sends), little structure change 10 months ago
Blue 18859cb960
first ideas for notifications 10 months ago
Blue 4c20a314f0
a crash fix on one of archive corner cases 10 months ago
Blue 51ac1ac709
first attempt 10 months ago
Blue 8f949277f6
actual pasword reasking on failed authentication 10 months ago
Blue ce686e121b
account removal bugfix, some testing 10 months ago
Blue f64e5c2df0
account connect/disconnect now activate/deactivate, it's a bit less contraversial; async account password asking new concept 10 months ago
Blue 2c26c7e264
ui squawk refactoring 10 months ago
Blue 69e0c88d8d
account refactoring, pep support discovery started 10 months ago
Blue 82d54ba4df
Report bugs tab and thanks to tab in about widget 10 months ago
Blue 1b66fda318
License is now can be viewed locally, some organization name packaging issies 10 months ago
Blue 9f746d203b
new tab in About: components 10 months ago
Blue 27377e0ec5
first attempt to make About window 10 months ago
Blue 4baa3bccbf
new screenshot 10 months ago
  1. 35
      CHANGELOG.md
  2. 5
      CMakeLists.txt
  3. 6
      LICENSE.md
  4. 2
      README.md
  5. 11
      core/CMakeLists.txt
  6. 538
      core/account.cpp
  7. 34
      core/account.h
  8. 8
      core/adapterfunctions.cpp
  9. 32
      core/adapterfunctions.h
  10. 2
      core/handlers/CMakeLists.txt
  11. 2
      core/handlers/messagehandler.cpp
  12. 15
      core/handlers/rosterhandler.cpp
  13. 2
      core/handlers/rosterhandler.h
  14. 312
      core/handlers/vcardhandler.cpp
  15. 65
      core/handlers/vcardhandler.h
  16. 209
      core/main.cpp
  17. 2
      core/networkaccess.h
  18. 28
      core/rosteritem.cpp
  19. 3
      core/rosteritem.h
  20. 217
      core/squawk.cpp
  21. 8
      core/squawk.h
  22. 8
      core/storage/CMakeLists.txt
  23. 0
      core/storage/archive.cpp
  24. 0
      core/storage/archive.h
  25. 0
      core/storage/storage.cpp
  26. 0
      core/storage/storage.h
  27. 0
      core/storage/urlstorage.cpp
  28. 0
      core/storage/urlstorage.h
  29. 7
      main/CMakeLists.txt
  30. 706
      main/application.cpp
  31. 132
      main/application.h
  32. 187
      main/dialogqueue.cpp
  33. 101
      main/dialogqueue.h
  34. 140
      main/main.cpp
  35. 4
      packaging/Archlinux/PKGBUILD
  36. 1
      shared/global.cpp
  37. 3
      shared/global.h
  38. 2
      shared/utils.cpp
  39. 6
      shared/utils.h
  40. 3
      translations/CMakeLists.txt
  41. 1340
      translations/squawk.en.ts
  42. 647
      translations/squawk.pt_BR.ts
  43. 645
      translations/squawk.ru.ts
  44. 6
      ui/CMakeLists.txt
  45. 26
      ui/models/account.cpp
  46. 4
      ui/models/account.h
  47. 30
      ui/models/accounts.cpp
  48. 4
      ui/models/accounts.h
  49. 10
      ui/models/contact.cpp
  50. 1
      ui/models/contact.h
  51. 11
      ui/models/element.cpp
  52. 3
      ui/models/element.h
  53. 5
      ui/models/item.cpp
  54. 1
      ui/models/item.h
  55. 5
      ui/models/reference.cpp
  56. 2
      ui/models/reference.h
  57. 10
      ui/models/room.cpp
  58. 2
      ui/models/room.h
  59. 240
      ui/models/roster.cpp
  60. 29
      ui/models/roster.h
  61. 705
      ui/squawk.cpp
  62. 97
      ui/squawk.h
  63. 15
      ui/squawk.ui
  64. 10
      ui/widgets/CMakeLists.txt
  65. 108
      ui/widgets/about.cpp
  66. 51
      ui/widgets/about.h
  67. 680
      ui/widgets/about.ui
  68. 11
      ui/widgets/accounts/CMakeLists.txt
  69. 1
      ui/widgets/accounts/account.cpp
  70. 0
      ui/widgets/accounts/account.h
  71. 45
      ui/widgets/accounts/account.ui
  72. 11
      ui/widgets/accounts/accounts.cpp
  73. 2
      ui/widgets/accounts/accounts.h
  74. 0
      ui/widgets/accounts/accounts.ui
  75. 60
      ui/widgets/accounts/credentialsprompt.cpp
  76. 52
      ui/widgets/accounts/credentialsprompt.h
  77. 144
      ui/widgets/accounts/credentialsprompt.ui
  78. 22
      ui/widgets/conversation.cpp
  79. 11
      ui/widgets/joinconference.cpp
  80. 162
      ui/widgets/messageline/feedview.cpp
  81. 15
      ui/widgets/messageline/feedview.h
  82. 290
      ui/widgets/messageline/messagedelegate.cpp
  83. 19
      ui/widgets/messageline/messagedelegate.h
  84. 24
      ui/widgets/messageline/messagefeed.cpp
  85. 5
      ui/widgets/messageline/messagefeed.h
  86. 8
      ui/widgets/newcontact.cpp
  87. 33
      ui/widgets/settings/pagegeneral.cpp
  88. 3
      ui/widgets/settings/pagegeneral.h
  89. 32
      ui/widgets/settings/pagegeneral.ui
  90. 11
      ui/widgets/settings/settings.cpp
  91. 1
      ui/widgets/settings/settings.h

@ -1,5 +1,40 @@
# Changelog
## Squawk 0.2.3 (UNRELEASED)
### Bug fixes
- "Add contact" and "Join conference" menu are enabled once again (pavavno)!
- availability is now read from the same section of config file it was stored
### Improvements
- deactivated accounts now don't appear in combobox of "Add contact" and "Join conference" dialogues
- all of the expandable roster items now get saved between launches
- settings file on the disk is not rewritten every roster element expansion or collapse
### New features
- Now you can enable tray icon from settings!
## Squawk 0.2.2 (May 05, 2022)
### Bug fixes
- now when you remove an account it actually gets removed
- segfault on uninitialized Availability in some rare occasions
- fixed crash when you open a dialog with someone that has only error messages in archive
- message height is now calculated correctly on Chinese and Japanese paragraphs
- the app doesn't crash on SIGINT anymore
### Improvements
- there is a way to disable an account and it wouldn't connect when you change availability
- if you cancel password query an account becomes inactive and doesn't annoy you anymore
- if you filled password field and chose KWallet as a storage Squawk wouldn't ask you again for the same password
- if left the password field empty and chose KWallet as a storage Squawk will try to get that password from KWallet before asking you to input it
- accounts now connect to the server asynchronously - if one is stopped on password prompt another is connecting
- actualized translations, added English localization file
### New features
- new "About" window with links, license, gratitudes
- if the authentication failed Squawk will ask again for your password and login
- now there is an amount of unread messages showing on top of Squawk launcher icon
- notifications now have buttons to open a conversation or to mark that message as read
## Squawk 0.2.1 (Apr 02, 2022)
### Bug fixes
- build in release mode now no longer spams warnings

@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.4)
project(squawk VERSION 0.2.1 LANGUAGES CXX)
project(squawk VERSION 0.2.3 LANGUAGES CXX)
cmake_policy(SET CMP0076 NEW)
cmake_policy(SET CMP0079 NEW)
@ -148,6 +148,7 @@ if(CMAKE_COMPILER_IS_GNUCXX)
target_compile_options(squawk PRIVATE ${COMPILE_OPTIONS})
endif(CMAKE_COMPILER_IS_GNUCXX)
add_subdirectory(main)
add_subdirectory(core)
add_subdirectory(external/simpleCrypt)
add_subdirectory(packaging)
@ -159,6 +160,8 @@ add_subdirectory(ui)
# Install the executable
install(TARGETS squawk DESTINATION ${CMAKE_INSTALL_BINDIR})
install(FILES README.md DESTINATION ${CMAKE_INSTALL_DATADIR}/macaw.me/squawk)
install(FILES LICENSE.md DESTINATION ${CMAKE_INSTALL_DATADIR}/macaw.me/squawk)
if (CMAKE_BUILD_TYPE STREQUAL "Release")
if (APPLE)

@ -595,17 +595,17 @@ pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
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 3 of the License, or
(at your option) any later version.
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 <http://www.gnu.org/licenses/>.

@ -4,7 +4,7 @@
[![AUR version](https://img.shields.io/aur/version/squawk?style=flat-square)](https://aur.archlinux.org/packages/squawk/)
[![Liberapay patrons](https://img.shields.io/liberapay/patrons/macaw.me?logo=liberapay&style=flat-square)](https://liberapay.com/macaw.me)
![Squawk screenshot](https://macaw.me/images/squawk/0.2.0.png)
![Squawk screenshot](https://macaw.me/images/squawk/0.2.2.png)
### Prerequisites

@ -6,14 +6,12 @@ endif(WIN32)
target_sources(squawk PRIVATE
account.cpp
account.h
adapterFuctions.cpp
archive.cpp
archive.h
adapterfunctions.cpp
adapterfunctions.h
conference.cpp
conference.h
contact.cpp
contact.h
main.cpp
networkaccess.cpp
networkaccess.h
rosteritem.cpp
@ -22,13 +20,10 @@ target_sources(squawk PRIVATE
signalcatcher.h
squawk.cpp
squawk.h
storage.cpp
storage.h
urlstorage.cpp
urlstorage.h
)
target_include_directories(squawk PRIVATE ${LMDB_INCLUDE_DIRS})
add_subdirectory(handlers)
add_subdirectory(storage)
add_subdirectory(passwordStorageEngines)

@ -22,7 +22,7 @@
using namespace Core;
Account::Account(const QString& p_login, const QString& p_server, const QString& p_password, const QString& p_name, NetworkAccess* p_net, QObject* parent):
Account::Account(const QString& p_login, const QString& p_server, const QString& p_password, const QString& p_name, bool p_active, NetworkAccess* p_net, QObject* parent):
QObject(parent),
name(p_name),
archiveQueries(),
@ -41,13 +41,15 @@ Account::Account(const QString& p_login, const QString& p_server, const QString&
rcpm(new QXmppMessageReceiptManager()),
reconnectScheduled(false),
reconnectTimer(new QTimer),
avatarHash(),
avatarType(),
ownVCardRequestInProgress(false),
network(p_net),
passwordType(Shared::AccountPassword::plain),
lastError(Error::none),
pepSupport(false),
active(p_active),
notReadyPassword(false),
mh(new MessageHandler(this)),
rh(new RosterHandler(this))
rh(new RosterHandler(this)),
vh(new VCardHandler(this))
{
config.setUser(p_login);
config.setDomain(p_server);
@ -73,10 +75,6 @@ Account::Account(const QString& p_login, const QString& p_server, const QString&
client.addExtension(mm);
client.addExtension(bm);
QObject::connect(vm, &QXmppVCardManager::vCardReceived, this, &Account::onVCardReceived);
//QObject::connect(&vm, &QXmppVCardManager::clientVCardReceived, this, &Account::onOwnVCardReceived); //for some reason it doesn't work, launching from common handler
client.addExtension(um);
QObject::connect(um, &QXmppUploadRequestManager::slotReceived, mh, &MessageHandler::onUploadSlotReceived);
QObject::connect(um, &QXmppUploadRequestManager::requestFailed, mh, &MessageHandler::onUploadSlotRequestFailed);
@ -91,62 +89,18 @@ Account::Account(const QString& p_login, const QString& p_server, const QString&
client.addExtension(rcpm);
QObject::connect(rcpm, &QXmppMessageReceiptManager::messageDelivered, mh, &MessageHandler::onReceiptReceived);
QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation));
path += "/" + name;
QDir dir(path);
if (!dir.exists()) {
bool res = dir.mkpath(path);
if (!res) {
qDebug() << "Couldn't create a cache directory for account" << name;
throw 22;
}
}
QFile* avatar = new QFile(path + "/avatar.png");
QString type = "png";
if (!avatar->exists()) {
delete avatar;
avatar = new QFile(path + "/avatar.jpg");
type = "jpg";
if (!avatar->exists()) {
delete avatar;
avatar = new QFile(path + "/avatar.jpeg");
type = "jpeg";
if (!avatar->exists()) {
delete avatar;
avatar = new QFile(path + "/avatar.gif");
type = "gif";
}
}
}
if (avatar->exists()) {
if (avatar->open(QFile::ReadOnly)) {
QCryptographicHash sha1(QCryptographicHash::Sha1);
sha1.addData(avatar);
avatarHash = sha1.result();
avatarType = type;
}
}
if (avatarType.size() != 0) {
presence.setVCardUpdateType(QXmppPresence::VCardUpdateValidPhoto);
presence.setPhotoHash(avatarHash.toUtf8());
} else {
presence.setVCardUpdateType(QXmppPresence::VCardUpdateNotReady);
}
reconnectTimer->setSingleShot(true);
QObject::connect(reconnectTimer, &QTimer::timeout, this, &Account::onReconnectTimer);
// QXmppLogger* logger = new QXmppLogger(this);
// logger->setLoggingType(QXmppLogger::SignalLogging);
// client.setLogger(logger);
//
// QObject::connect(logger, &QXmppLogger::message, this, [](QXmppLogger::MessageType type, const QString& text){
// qDebug() << text;
// });
if (name == "Test") {
QXmppLogger* logger = new QXmppLogger(this);
logger->setLoggingType(QXmppLogger::SignalLogging);
client.setLogger(logger);
QObject::connect(logger, &QXmppLogger::message, this, [](QXmppLogger::MessageType type, const QString& text){
qDebug() << text;
});
}
}
Account::~Account()
@ -160,6 +114,7 @@ Account::~Account()
QObject::disconnect(network, &NetworkAccess::downloadFileComplete, mh, &MessageHandler::onDownloadFileComplete);
QObject::disconnect(network, &NetworkAccess::loadFileError, mh, &MessageHandler::onLoadFileError);
delete vh;
delete mh;
delete rh;
@ -185,7 +140,12 @@ void Core::Account::connect()
reconnectTimer->stop();
}
if (state == Shared::ConnectionState::disconnected) {
client.connectToServer(config, presence);
if (notReadyPassword) {
emit needPassword();
} else {
client.connectToServer(config, presence);
}
} else {
qDebug("An attempt to connect an account which is already connected, skipping");
}
@ -224,6 +184,7 @@ void Core::Account::onClientStateChange(QXmppClient::State st)
dm->requestItems(getServer());
dm->requestInfo(getServer());
}
lastError = Error::none;
emit connectionStateChanged(state);
}
} else {
@ -255,45 +216,17 @@ void Core::Account::onClientStateChange(QXmppClient::State st)
void Core::Account::reconnect()
{
if (state == Shared::ConnectionState::connected && !reconnectScheduled) {
reconnectScheduled = true;
reconnectTimer->start(500);
client.disconnectFromServer();
} else {
qDebug() << "An attempt to reconnect account" << getName() << "which was not connected";
if (!reconnectScheduled) { //TODO define behavior if It was connection or disconnecting
if (state == Shared::ConnectionState::connected) {
reconnectScheduled = true;
reconnectTimer->start(500);
client.disconnectFromServer();
} else {
qDebug() << "An attempt to reconnect account" << getName() << "which was not connected";
}
}
}
QString Core::Account::getName() const {
return name;}
QString Core::Account::getLogin() const {
return config.user();}
QString Core::Account::getPassword() const {
return config.password();}
QString Core::Account::getServer() const {
return config.domain();}
Shared::AccountPassword Core::Account::getPasswordType() const {
return passwordType;}
void Core::Account::setPasswordType(Shared::AccountPassword pt) {
passwordType = pt; }
void Core::Account::setLogin(const QString& p_login) {
config.setUser(p_login);}
void Core::Account::setName(const QString& p_name) {
name = p_name;}
void Core::Account::setPassword(const QString& p_password) {
config.setPassword(p_password);}
void Core::Account::setServer(const QString& p_server) {
config.setDomain(p_server);}
Shared::Availability Core::Account::getAvailability() const
{
if (state == Shared::ConnectionState::connected) {
@ -325,32 +258,11 @@ void Core::Account::onPresenceReceived(const QXmppPresence& p_presence)
QString jid = comps.front().toLower();
QString resource = comps.back();
QString myJid = getLogin() + "@" + getServer();
if (jid == myJid) {
if (jid == getBareJid()) {
if (resource == getResource()) {
emit availabilityChanged(static_cast<Shared::Availability>(p_presence.availableStatusType()));
} else {
if (!ownVCardRequestInProgress) {
switch (p_presence.vCardUpdateType()) {
case QXmppPresence::VCardUpdateNone: //this presence has nothing to do with photo
break;
case QXmppPresence::VCardUpdateNotReady: //let's say the photo didn't change here
break;
case QXmppPresence::VCardUpdateNoPhoto: //there is no photo, need to drop if any
if (avatarType.size() > 0) {
vm->requestClientVCard();
ownVCardRequestInProgress = true;
}
break;
case QXmppPresence::VCardUpdateValidPhoto: //there is a photo, need to load
if (avatarHash != p_presence.photoHash()) {
vm->requestClientVCard();
ownVCardRequestInProgress = true;
}
break;
}
}
vh->handleOtherPresenceOfMyAccountChange(p_presence);
}
} else {
RosterItem* item = rh->getRosterItem(jid);
@ -392,18 +304,6 @@ void Core::Account::onPresenceReceived(const QXmppPresence& p_presence)
}
}
QString Core::Account::getResource() const {
return config.resource();}
void Core::Account::setResource(const QString& p_resource) {
config.setResource(p_resource);}
QString Core::Account::getFullJid() const {
return getLogin() + "@" + getServer() + "/" + getResource();}
void Core::Account::sendMessage(const Shared::Message& data) {
mh->sendMessage(data);}
void Core::Account::onMamMessageReceived(const QString& queryId, const QXmppMessage& msg)
{
if (msg.id().size() > 0 && (msg.body().size() > 0 || msg.outOfBandUrl().size() > 0)) {
@ -517,6 +417,7 @@ void Core::Account::onClientError(QXmppClient::Error err)
qDebug() << "Error";
QString errorText;
QString errorType;
lastError = Error::other;
switch (err) {
case QXmppClient::SocketError:
errorText = client.socketErrorString();
@ -558,6 +459,7 @@ void Core::Account::onClientError(QXmppClient::Error err)
break;
case QXmppStanza::Error::NotAuthorized:
errorText = "Authentication error";
lastError = Error::authentication;
break;
#if (QXMPP_VERSION) < QT_VERSION_CHECK(1, 3, 0)
case QXmppStanza::Error::PaymentRequired:
@ -667,241 +569,66 @@ void Core::Account::setRoomJoined(const QString& jid, bool joined)
conf->setJoined(joined);
}
void Core::Account::removeRoomRequest(const QString& jid){
rh->removeRoomRequest(jid);}
void Core::Account::addRoomRequest(const QString& jid, const QString& nick, const QString& password, bool autoJoin) {
rh->addRoomRequest(jid, nick, password, autoJoin);}
void Core::Account::addContactToGroupRequest(const QString& jid, const QString& groupName) {
rh->addContactToGroupRequest(jid, groupName);}
void Core::Account::removeContactFromGroupRequest(const QString& jid, const QString& groupName) {
rh->removeContactFromGroupRequest(jid, groupName);}
void Core::Account::renameContactRequest(const QString& jid, const QString& newName)
{
Contact* cnt = rh->getContact(jid);
if (cnt == 0) {
qDebug() << "An attempt to rename non existing contact" << jid << "of account" << name << ", skipping";
} else {
rm->renameItem(jid, newName);
}
}
void Core::Account::onVCardReceived(const QXmppVCardIq& card)
{
QString id = card.from();
QStringList comps = id.split("/");
QString jid = comps.front().toLower();
QString resource("");
if (comps.size() > 1) {
resource = comps.back();
}
pendingVCardRequests.erase(id);
RosterItem* item = rh->getRosterItem(jid);
if (item == 0) {
if (jid == getLogin() + "@" + getServer()) {
onOwnVCardReceived(card);
} else {
qDebug() << "received vCard" << jid << "doesn't belong to any of known contacts or conferences, skipping";
}
return;
}
Shared::VCard vCard = item->handleResponseVCard(card, resource);
emit receivedVCard(jid, vCard);
}
void Core::Account::onOwnVCardReceived(const QXmppVCardIq& card)
void Core::Account::onDiscoveryItemsReceived(const QXmppDiscoveryIq& items)
{
QByteArray ava = card.photo();
bool avaChanged = false;
QString path = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/" + name + "/";
if (ava.size() > 0) {
QCryptographicHash sha1(QCryptographicHash::Sha1);
sha1.addData(ava);
QString newHash(sha1.result());
QMimeDatabase db;
QMimeType newType = db.mimeTypeForData(ava);
if (avatarType.size() > 0) {
if (avatarHash != newHash) {
QString oldPath = path + "avatar." + avatarType;
QFile oldAvatar(oldPath);
bool oldToRemove = false;
if (oldAvatar.exists()) {
if (oldAvatar.rename(oldPath + ".bak")) {
oldToRemove = true;
} else {
qDebug() << "Received new avatar for account" << name << "but can't get rid of the old one, doing nothing";
}
}
QFile newAvatar(path + "avatar." + newType.preferredSuffix());
if (newAvatar.open(QFile::WriteOnly)) {
newAvatar.write(ava);
newAvatar.close();
avatarHash = newHash;
avatarType = newType.preferredSuffix();
avaChanged = true;
} else {
qDebug() << "Received new avatar for account" << name << "but can't save it";
if (oldToRemove) {
qDebug() << "rolling back to the old avatar";
if (!oldAvatar.rename(oldPath)) {
qDebug() << "Couldn't roll back to the old avatar in account" << name;
}
}
}
}
} else {
QFile newAvatar(path + "avatar." + newType.preferredSuffix());
if (newAvatar.open(QFile::WriteOnly)) {
newAvatar.write(ava);
newAvatar.close();
avatarHash = newHash;
avatarType = newType.preferredSuffix();
avaChanged = true;
} else {
qDebug() << "Received new avatar for account" << name << "but can't save it";
}
}
} else {
if (avatarType.size() > 0) {
QFile oldAvatar(path + "avatar." + avatarType);
if (!oldAvatar.remove()) {
qDebug() << "Received vCard for account" << name << "without avatar, but can't get rid of the file, doing nothing";
if (items.from() == getServer()) {
std::set<QString> needToRequest;
qDebug() << "Server items list received for account " << name << ":";
for (QXmppDiscoveryIq::Item item : items.items()) {
QString jid = item.jid();
if (jid != getServer()) {
qDebug() << " Node" << jid;
needToRequest.insert(jid);
} else {
avatarType = "";
avatarHash = "";
avaChanged = true;
qDebug() << " " << item.node().toStdString().c_str();
}
}
}
if (avaChanged) {
QMap<QString, QVariant> change;
if (avatarType.size() > 0) {
presence.setPhotoHash(avatarHash.toUtf8());
presence.setVCardUpdateType(QXmppPresence::VCardUpdateValidPhoto);
change.insert("avatarPath", path + "avatar." + avatarType);
} else {
presence.setPhotoHash("");
presence.setVCardUpdateType(QXmppPresence::VCardUpdateNoPhoto);
change.insert("avatarPath", "");
}
client.setClientPresence(presence);
emit changed(change);
}
ownVCardRequestInProgress = false;
Shared::VCard vCard;
initializeVCard(vCard, card);
if (avatarType.size() > 0) {
vCard.setAvatarType(Shared::Avatar::valid);
vCard.setAvatarPath(path + "avatar." + avatarType);
} else {
vCard.setAvatarType(Shared::Avatar::empty);
}
emit receivedVCard(getLogin() + "@" + getServer(), vCard);
}
QString Core::Account::getAvatarPath() const
{
if (avatarType.size() == 0) {
return "";
} else {
return QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/" + name + "/" + "avatar." + avatarType;
}
}
void Core::Account::requestVCard(const QString& jid)
{
if (pendingVCardRequests.find(jid) == pendingVCardRequests.end()) {
qDebug() << "requesting vCard" << jid;
if (jid == getLogin() + "@" + getServer()) {
if (!ownVCardRequestInProgress) {
vm->requestClientVCard();
ownVCardRequestInProgress = true;
}
} else {
vm->requestVCard(jid);
pendingVCardRequests.insert(jid);
for (const QString& jid : needToRequest) {
dm->requestInfo(jid);
}
}
}
void Core::Account::uploadVCard(const Shared::VCard& card)
void Core::Account::onDiscoveryInfoReceived(const QXmppDiscoveryIq& info)
{
QXmppVCardIq iq;
initializeQXmppVCard(iq, card);
if (card.getAvatarType() != Shared::Avatar::empty) {
QString newPath = card.getAvatarPath();
QString oldPath = getAvatarPath();
QByteArray data;
QString type;
if (newPath != oldPath) {
QFile avatar(newPath);
if (!avatar.open(QFile::ReadOnly)) {
qDebug() << "An attempt to upload new vCard to account" << name
<< "but it wasn't possible to read file" << newPath
<< "which was supposed to be new avatar, uploading old avatar";
if (avatarType.size() > 0) {
QFile oA(oldPath);
if (!oA.open(QFile::ReadOnly)) {
qDebug() << "Couldn't read old avatar of account" << name << ", uploading empty avatar";
} else {
data = oA.readAll();
}
}
} else {
data = avatar.readAll();
}
} else {
if (avatarType.size() > 0) {
QFile oA(oldPath);
if (!oA.open(QFile::ReadOnly)) {
qDebug() << "Couldn't read old avatar of account" << name << ", uploading empty avatar";
} else {
data = oA.readAll();
}
if (info.from() == getServer()) {
bool enableCC = false;
qDebug() << "Server info received for account" << name;
QStringList features = info.features();
qDebug() << "List of supported features of the server " << getServer() << ":";
for (const QString& feature : features) {
qDebug() << " " << feature.toStdString().c_str();
if (feature == "urn:xmpp:carbons:2") {
enableCC = true;
}
}
if (data.size() > 0) {
QMimeDatabase db;
type = db.mimeTypeForData(data).name();
iq.setPhoto(data);
iq.setPhotoType(type);
}
}
vm->setClientVCard(iq);
onOwnVCardReceived(iq);
}
void Core::Account::onDiscoveryItemsReceived(const QXmppDiscoveryIq& items)
{
for (QXmppDiscoveryIq::Item item : items.items()) {
if (item.jid() != getServer()) {
dm->requestInfo(item.jid());
}
}
}
void Core::Account::onDiscoveryInfoReceived(const QXmppDiscoveryIq& info)
{
qDebug() << "Discovery info received for account" << name;
if (info.from() == getServer()) {
if (info.features().contains("urn:xmpp:carbons:2")) {
if (enableCC) {
qDebug() << "Enabling carbon copies for account" << name;
cm->setCarbonsEnabled(true);
}
qDebug() << "Requesting account" << name << "capabilities";
dm->requestInfo(getBareJid());
} else if (info.from() == getBareJid()) {
qDebug() << "Received capabilities for account" << name << ":";
QList<QXmppDiscoveryIq::Identity> identities = info.identities();
bool pepSupported = false;
for (const QXmppDiscoveryIq::Identity& identity : identities) {
QString type = identity.type();
qDebug() << " " << identity.category() << type;
if (type == "pep") {
pepSupported = true;
}
}
rh->setPepSupport(pepSupported);
} else {
qDebug() << "Received info for account" << name << "about" << info.from();
QList<QXmppDiscoveryIq::Identity> identities = info.identities();
for (const QXmppDiscoveryIq::Identity& identity : identities) {
qDebug() << " " << identity.name() << identity.category() << identity.type();
}
}
}
@ -909,14 +636,8 @@ void Core::Account::handleDisconnection()
{
cm->setCarbonsEnabled(false);
rh->handleOffline();
vh->handleOffline();
archiveQueries.clear();
pendingVCardRequests.clear();
Shared::VCard vCard; //just to show, that there is now more pending request
for (const QString& jid : pendingVCardRequests) {
emit receivedVCard(jid, vCard); //need to show it better in the future, like with an error
}
pendingVCardRequests.clear();
ownVCardRequestInProgress = false;
}
void Core::Account::onContactHistoryResponse(const std::list<Shared::Message>& list, bool last)
@ -930,6 +651,66 @@ void Core::Account::onContactHistoryResponse(const std::list<Shared::Message>& l
emit responseArchive(contact->jid, list, last);
}
bool Core::Account::getActive() const {
return active;}
void Core::Account::setActive(bool p_active) {
if (active != p_active) {
active = p_active;
emit changed({
{"active", active}
});
}
}
QString Core::Account::getResource() const {
return config.resource();}
void Core::Account::setResource(const QString& p_resource) {
config.setResource(p_resource);}
QString Core::Account::getBareJid() const {
return getLogin() + "@" + getServer();}
QString Core::Account::getFullJid() const {
return getBareJid() + "/" + getResource();}
QString Core::Account::getName() const {
return name;}
QString Core::Account::getLogin() const {
return config.user();}
QString Core::Account::getPassword() const {
return config.password();}
QString Core::Account::getServer() const {
return config.domain();}
Shared::AccountPassword Core::Account::getPasswordType() const {
return passwordType;}
void Core::Account::setPasswordType(Shared::AccountPassword pt) {
passwordType = pt; }
void Core::Account::setLogin(const QString& p_login) {
config.setUser(p_login);}
void Core::Account::setName(const QString& p_name) {
name = p_name;}
void Core::Account::setPassword(const QString& p_password) {
config.setPassword(p_password);
notReadyPassword = false;
}
void Core::Account::setServer(const QString& p_server) {
config.setDomain(p_server);}
void Core::Account::sendMessage(const Shared::Message& data) {
mh->sendMessage(data);}
void Core::Account::requestChangeMessage(const QString& jid, const QString& messageId, const QMap<QString, QVariant>& data){
mh->requestChangeMessage(jid, messageId, data);}
@ -939,3 +720,40 @@ void Core::Account::resendMessage(const QString& jid, const QString& id) {
void Core::Account::replaceMessage(const QString& originalId, const Shared::Message& data) {
mh->sendMessage(data, false, originalId);}
void Core::Account::requestVCard(const QString& jid) {
vh->requestVCard(jid);}
void Core::Account::uploadVCard(const Shared::VCard& card) {
vh->uploadVCard(card);}
QString Core::Account::getAvatarPath() const {
return vh->getAvatarPath();}
void Core::Account::removeRoomRequest(const QString& jid){
rh->removeRoomRequest(jid);}
void Core::Account::addRoomRequest(const QString& jid, const QString& nick, const QString& password, bool autoJoin) {
rh->addRoomRequest(jid, nick, password, autoJoin);}
void Core::Account::addContactToGroupRequest(const QString& jid, const QString& groupName) {
rh->addContactToGroupRequest(jid, groupName);}
void Core::Account::removeContactFromGroupRequest(const QString& jid, const QString& groupName) {
rh->removeContactFromGroupRequest(jid, groupName);}
void Core::Account::renameContactRequest(const QString& jid, const QString& newName)
{
Contact* cnt = rh->getContact(jid);
if (cnt == 0) {
qDebug() << "An attempt to rename non existing contact" << jid << "of account" << name << ", skipping";
} else {
rm->renameItem(jid, newName);
}
}
void Core::Account::invalidatePassword() {
notReadyPassword = true;}
Core::Account::Error Core::Account::getLastError() const {
return lastError;}

@ -39,7 +39,6 @@
#include <QXmppBookmarkManager.h>
#include <QXmppBookmarkSet.h>
#include <QXmppUploadRequestManager.h>
#include <QXmppVCardIq.h>
#include <QXmppVCardManager.h>
#include <QXmppMessageReceiptManager.h>
@ -50,6 +49,7 @@
#include "handlers/messagehandler.h"
#include "handlers/rosterhandler.h"
#include "handlers/vcardhandler.h"
namespace Core
{
@ -59,12 +59,20 @@ class Account : public QObject
Q_OBJECT
friend class MessageHandler;
friend class RosterHandler;
friend class VCardHandler;
public:
enum class Error {
authentication,
other,
none
};
Account(
const QString& p_login,
const QString& p_server,
const QString& p_password,
const QString& p_name,
bool p_active,
NetworkAccess* p_net,
QObject* parent = 0);
~Account();
@ -76,8 +84,12 @@ public:
QString getPassword() const;
QString getResource() const;
QString getAvatarPath() const;
QString getBareJid() const;
QString getFullJid() const;
Shared::Availability getAvailability() const;
Shared::AccountPassword getPasswordType() const;
Error getLastError() const;
bool getActive() const;
void setName(const QString& p_name);
void setLogin(const QString& p_login);
@ -86,8 +98,8 @@ public:
void setResource(const QString& p_resource);
void setAvailability(Shared::Availability avail);
void setPasswordType(Shared::AccountPassword pt);
QString getFullJid() const;
void sendMessage(const Shared::Message& data);
void setActive(bool p_active);
void requestArchive(const QString& jid, int count, const QString& before);
void subscribeToContact(const QString& jid, const QString& reason);
void unsubscribeFromContact(const QString& jid, const QString& reason);
@ -105,6 +117,7 @@ public:
void uploadVCard(const Shared::VCard& card);
void resendMessage(const QString& jid, const QString& id);
void replaceMessage(const QString& originalId, const Shared::Message& data);
void invalidatePassword();
public slots:
void connect();
@ -137,6 +150,7 @@ signals:
void receivedVCard(const QString& jid, const Shared::VCard& card);
void uploadFile(const QFileInfo& file, const QUrl& set, const QUrl& get, QMap<QString, QString> headers);
void uploadFileError(const QString& jid, const QString& messageId, const QString& error);
void needPassword();
private:
QString name;
@ -157,16 +171,16 @@ private:
bool reconnectScheduled;
QTimer* reconnectTimer;
std::set<QString> pendingVCardRequests;
QString avatarHash;
QString avatarType;
bool ownVCardRequestInProgress;
NetworkAccess* network;
Shared::AccountPassword passwordType;
Error lastError;
bool pepSupport;
bool active;
bool notReadyPassword;
MessageHandler* mh;
RosterHandler* rh;
VCardHandler* vh;
private slots:
void onClientStateChange(QXmppClient::State state);
@ -179,9 +193,6 @@ private slots:
void onMamResultsReceived(const QString &queryId, const QXmppResultSetReply &resultSetReply, bool complete);
void onMamLog(QXmppLogger::MessageType type, const QString &msg);
void onVCardReceived(const QXmppVCardIq& card);
void onOwnVCardReceived(const QXmppVCardIq& card);
void onDiscoveryItemsReceived (const QXmppDiscoveryIq& items);
void onDiscoveryInfoReceived (const QXmppDiscoveryIq& info);
@ -191,9 +202,6 @@ private:
void handleDisconnection();
void onReconnectTimer();
};
void initializeVCard(Shared::VCard& vCard, const QXmppVCardIq& card);
void initializeQXmppVCard(QXmppVCardIq& card, const Shared::VCard& vCard);
}

@ -1,5 +1,5 @@
/*
* Squawk messenger.
* Squawk messenger.
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
*
* This program is free software: you can redistribute it and/or modify
@ -15,10 +15,8 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CORE_ADAPTER_FUNCTIONS_H
#define CORE_ADAPTER_FUNCTIONS_H
#include "account.h"
#include "adapterfunctions.h"
void Core::initializeVCard(Shared::VCard& vCard, const QXmppVCardIq& card)
{
@ -271,5 +269,3 @@ void Core::initializeQXmppVCard(QXmppVCardIq& iq, const Shared::VCard& card) {
iq.setEmails(emails);
iq.setPhones(phs);
}
#endif // CORE_ADAPTER_FUNCTIONS_H

@ -0,0 +1,32 @@
/*
* Squawk messenger.
* Copyright (C) 2019 Yury Gubich <blue@macaw.me>
*
* 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 3 of the License, or
* (at your option) any later version.
*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef CORE_ADAPTER_FUNCTIONS_H
#define CORE_ADAPTER_FUNCTIONS_H
#include <QXmppVCardIq.h>
#include <shared/vcard.h>
namespace Core {
void initializeVCard(Shared::VCard& vCard, const QXmppVCardIq& card);
void initializeQXmppVCard(QXmppVCardIq& card, const Shared::VCard& vCard);
}
#endif // CORE_ADAPTER_FUNCTIONS_H

@ -3,4 +3,6 @@ target_sources(squawk PRIVATE
messagehandler.h
rosterhandler.cpp
rosterhandler.h
vcardhandler.cpp
vcardhandler.h
)

@ -176,7 +176,7 @@ void Core::MessageHandler::initializeMessage(Shared::Message& target, const QXmp
target.setForwarded(forwarded);
if (guessing) {
if (target.getFromJid() == acc->getLogin() + "@" + acc->getServer()) {
if (target.getFromJid() == acc->getBareJid()) {
outgoing = true;
} else {
outgoing = false;

@ -26,7 +26,8 @@ Core::RosterHandler::RosterHandler(Core::Account* account):
conferences(),
groups(),
queuedContacts(),
outOfRosterContacts()
outOfRosterContacts(),
pepSupport(false)
{
connect(acc->rm, &QXmppRosterManager::rosterReceived, this, &RosterHandler::onRosterReceived);
connect(acc->rm, &QXmppRosterManager::itemAdded, this, &RosterHandler::onRosterItemAdded);
@ -51,8 +52,7 @@ Core::RosterHandler::~RosterHandler()
void Core::RosterHandler::onRosterReceived()
{
acc->vm->requestClientVCard(); //TODO need to make sure server actually supports vCards
acc->ownVCardRequestInProgress = true;
acc->requestVCard(acc->getBareJid()); //TODO need to make sure server actually supports vCards
QStringList bj = acc->rm->getRosterBareJids();
for (int i = 0; i < bj.size(); ++i) {
@ -588,4 +588,13 @@ void Core::RosterHandler::handleOffline()
pair.second->clearArchiveRequests();
pair.second->downgradeDatabaseState();
}
setPepSupport(false);
}
void Core::RosterHandler::setPepSupport(bool support)
{
if (pepSupport != support) {
pepSupport = support;
}
}

@ -64,6 +64,7 @@ public:
void storeConferences();
void clearConferences();
void setPepSupport(bool support);
private slots:
void onRosterReceived();
@ -107,6 +108,7 @@ private:
std::map<QString, std::set<QString>> groups;
std::map<QString, QString> queuedContacts;
std::set<QString> outOfRosterContacts;
bool pepSupport;
};
}

@ -0,0 +1,312 @@
// Squawk messenger.
// Copyright (C) 2019 Yury Gubich <blue@macaw.me>
//
// 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 3 of the License, or
// (at your option) any later version.
//
// 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 <http://www.gnu.org/