forked from blue/squawk
Merge branch 'master' into fileUpload
This commit is contained in:
commit
09749bac51
100 changed files with 6396 additions and 551 deletions
|
@ -1,12 +1,12 @@
|
|||
cmake_minimum_required(VERSION 3.0)
|
||||
project(squawkCORE)
|
||||
|
||||
# Instruct CMake to run moc automatically when needed.
|
||||
set (CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
|
||||
find_package(Qt5Widgets CONFIG REQUIRED)
|
||||
find_package(Qt5Core CONFIG REQUIRED)
|
||||
find_package(Qt5Gui CONFIG REQUIRED)
|
||||
find_package(Qt5Network CONFIG REQUIRED)
|
||||
find_package(Qt5Xml CONFIG REQUIRED)
|
||||
|
||||
set(squawkCORE_SRC
|
||||
squawk.cpp
|
||||
|
@ -17,16 +17,23 @@ set(squawkCORE_SRC
|
|||
conference.cpp
|
||||
storage.cpp
|
||||
networkaccess.cpp
|
||||
adapterFuctions.cpp
|
||||
)
|
||||
|
||||
# Tell CMake to create the helloworld executable
|
||||
add_library(squawkCORE ${squawkCORE_SRC})
|
||||
|
||||
if(SYSTEM_QXMPP)
|
||||
find_package(QXmpp CONFIG REQUIRED)
|
||||
get_target_property(QXMPP_INTERFACE_INCLUDE_DIRECTORIES QXmpp::QXmpp INTERFACE_INCLUDE_DIRECTORIES)
|
||||
target_include_directories(squawkCORE PUBLIC ${QXMPP_INTERFACE_INCLUDE_DIRECTORIES})
|
||||
endif()
|
||||
|
||||
|
||||
# Use the Widgets module from Qt 5.
|
||||
target_link_libraries(squawkCORE Qt5::Core)
|
||||
target_link_libraries(squawkCORE Qt5::Network)
|
||||
target_link_libraries(squawkCORE Qt5::Gui)
|
||||
target_link_libraries(squawkCORE Qt5::Xml)
|
||||
target_link_libraries(squawkCORE qxmpp)
|
||||
target_link_libraries(squawkCORE lmdb)
|
||||
|
||||
# Install the executable
|
||||
install(TARGETS squawkCORE DESTINATION lib)
|
||||
|
|
528
core/account.cpp
528
core/account.cpp
|
@ -36,49 +36,99 @@ Account::Account(const QString& p_login, const QString& p_server, const QString&
|
|||
am(new QXmppMamManager()),
|
||||
mm(new QXmppMucManager()),
|
||||
bm(new QXmppBookmarkManager()),
|
||||
rm(client.findExtension<QXmppRosterManager>()),
|
||||
vm(client.findExtension<QXmppVCardManager>()),
|
||||
contacts(),
|
||||
conferences(),
|
||||
maxReconnectTimes(0),
|
||||
reconnectTimes(0),
|
||||
queuedContacts(),
|
||||
outOfRosterContacts()
|
||||
outOfRosterContacts(),
|
||||
avatarHash(),
|
||||
avatarType(),
|
||||
ownVCardRequestInProgress(false)
|
||||
{
|
||||
config.setUser(p_login);
|
||||
config.setDomain(p_server);
|
||||
config.setPassword(p_password);
|
||||
config.setAutoAcceptSubscriptions(true);
|
||||
|
||||
QObject::connect(&client, SIGNAL(connected()), this, SLOT(onClientConnected()));
|
||||
QObject::connect(&client, SIGNAL(disconnected()), this, SLOT(onClientDisconnected()));
|
||||
QObject::connect(&client, SIGNAL(presenceReceived(const QXmppPresence&)), this, SLOT(onPresenceReceived(const QXmppPresence&)));
|
||||
QObject::connect(&client, SIGNAL(messageReceived(const QXmppMessage&)), this, SLOT(onMessageReceived(const QXmppMessage&)));
|
||||
QObject::connect(&client, SIGNAL(error(QXmppClient::Error)), this, SLOT(onClientError(QXmppClient::Error)));
|
||||
QObject::connect(&client, &QXmppClient::connected, this, &Account::onClientConnected);
|
||||
QObject::connect(&client, &QXmppClient::disconnected, this, &Account::onClientDisconnected);
|
||||
QObject::connect(&client, &QXmppClient::presenceReceived, this, &Account::onPresenceReceived);
|
||||
QObject::connect(&client, &QXmppClient::messageReceived, this, &Account::onMessageReceived);
|
||||
QObject::connect(&client, &QXmppClient::error, this, &Account::onClientError);
|
||||
|
||||
QXmppRosterManager& rm = client.rosterManager();
|
||||
|
||||
QObject::connect(&rm, SIGNAL(rosterReceived()), this, SLOT(onRosterReceived()));
|
||||
QObject::connect(&rm, SIGNAL(itemAdded(const QString&)), this, SLOT(onRosterItemAdded(const QString&)));
|
||||
QObject::connect(&rm, SIGNAL(itemRemoved(const QString&)), this, SLOT(onRosterItemRemoved(const QString&)));
|
||||
QObject::connect(&rm, SIGNAL(itemChanged(const QString&)), this, SLOT(onRosterItemChanged(const QString&)));
|
||||
//QObject::connect(&rm, SIGNAL(presenceChanged(const QString&, const QString&)), this, SLOT(onRosterPresenceChanged(const QString&, const QString&)));
|
||||
QObject::connect(rm, &QXmppRosterManager::rosterReceived, this, &Account::onRosterReceived);
|
||||
QObject::connect(rm, &QXmppRosterManager::itemAdded, this, &Account::onRosterItemAdded);
|
||||
QObject::connect(rm, &QXmppRosterManager::itemRemoved, this, &Account::onRosterItemRemoved);
|
||||
QObject::connect(rm, &QXmppRosterManager::itemChanged, this, &Account::onRosterItemChanged);
|
||||
//QObject::connect(&rm, &QXmppRosterManager::presenceChanged, this, &Account::onRosterPresenceChanged);
|
||||
|
||||
client.addExtension(cm);
|
||||
|
||||
QObject::connect(cm, SIGNAL(messageReceived(const QXmppMessage&)), this, SLOT(onCarbonMessageReceived(const QXmppMessage&)));
|
||||
QObject::connect(cm, SIGNAL(messageSent(const QXmppMessage&)), this, SLOT(onCarbonMessageSent(const QXmppMessage&)));
|
||||
QObject::connect(cm, &QXmppCarbonManager::messageReceived, this, &Account::onCarbonMessageReceived);
|
||||
QObject::connect(cm, &QXmppCarbonManager::messageSent, this, &Account::onCarbonMessageSent);
|
||||
|
||||
client.addExtension(am);
|
||||
|
||||
QObject::connect(am, SIGNAL(logMessage(QXmppLogger::MessageType, const QString&)), this, SLOT(onMamLog(QXmppLogger::MessageType, const QString)));
|
||||
QObject::connect(am, SIGNAL(archivedMessageReceived(const QString&, const QXmppMessage&)), this, SLOT(onMamMessageReceived(const QString&, const QXmppMessage&)));
|
||||
QObject::connect(am, SIGNAL(resultsRecieved(const QString&, const QXmppResultSetReply&, bool)),
|
||||
this, SLOT(onMamResultsReceived(const QString&, const QXmppResultSetReply&, bool)));
|
||||
QObject::connect(am, &QXmppMamManager::logMessage, this, &Account::onMamLog);
|
||||
QObject::connect(am, &QXmppMamManager::archivedMessageReceived, this, &Account::onMamMessageReceived);
|
||||
QObject::connect(am, &QXmppMamManager::resultsRecieved, this, &Account::onMamResultsReceived);
|
||||
|
||||
client.addExtension(mm);
|
||||
QObject::connect(mm, SIGNAL(roomAdded(QXmppMucRoom*)), this, SLOT(onMucRoomAdded(QXmppMucRoom*)));
|
||||
QObject::connect(mm, &QXmppMucManager::roomAdded, this, &Account::onMucRoomAdded);
|
||||
|
||||
client.addExtension(bm);
|
||||
QObject::connect(bm, SIGNAL(bookmarksReceived(const QXmppBookmarkSet&)), this, SLOT(bookmarksReceived(const QXmppBookmarkSet&)));
|
||||
QObject::connect(bm, &QXmppBookmarkManager::bookmarksReceived, this, &Account::bookmarksReceived);
|
||||
|
||||
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
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
Account::~Account()
|
||||
|
@ -189,8 +239,10 @@ QString Core::Account::getServer() const
|
|||
|
||||
void Core::Account::onRosterReceived()
|
||||
{
|
||||
QXmppRosterManager& rm = client.rosterManager();
|
||||
QStringList bj = rm.getRosterBareJids();
|
||||
vm->requestClientVCard(); //TODO need to make sure server actually supports vCards
|
||||
ownVCardRequestInProgress = true;
|
||||
|
||||
QStringList bj = rm->getRosterBareJids();
|
||||
for (int i = 0; i < bj.size(); ++i) {
|
||||
const QString& jid = bj[i];
|
||||
addedAccount(jid);
|
||||
|
@ -210,8 +262,7 @@ void Core::Account::onRosterItemAdded(const QString& bareJid)
|
|||
addedAccount(bareJid);
|
||||
std::map<QString, QString>::const_iterator itr = queuedContacts.find(bareJid);
|
||||
if (itr != queuedContacts.end()) {
|
||||
QXmppRosterManager& rm = client.rosterManager();
|
||||
rm.subscribe(bareJid, itr->second);
|
||||
rm->subscribe(bareJid, itr->second);
|
||||
queuedContacts.erase(itr);
|
||||
}
|
||||
}
|
||||
|
@ -224,8 +275,7 @@ void Core::Account::onRosterItemChanged(const QString& bareJid)
|
|||
return;
|
||||
}
|
||||
Contact* contact = itr->second;
|
||||
QXmppRosterManager& rm = client.rosterManager();
|
||||
QXmppRosterIq::Item re = rm.getRosterEntry(bareJid);
|
||||
QXmppRosterIq::Item re = rm->getRosterEntry(bareJid);
|
||||
|
||||
Shared::SubscriptionState state = castSubscriptionState(re.subscriptionType());
|
||||
|
||||
|
@ -254,9 +304,8 @@ void Core::Account::onRosterItemRemoved(const QString& bareJid)
|
|||
|
||||
void Core::Account::addedAccount(const QString& jid)
|
||||
{
|
||||
QXmppRosterManager& rm = client.rosterManager();
|
||||
std::map<QString, Contact*>::const_iterator itr = contacts.find(jid);
|
||||
QXmppRosterIq::Item re = rm.getRosterEntry(jid);
|
||||
QXmppRosterIq::Item re = rm->getRosterEntry(jid);
|
||||
Contact* contact;
|
||||
bool newContact = false;
|
||||
if (itr == contacts.end()) {
|
||||
|
@ -279,6 +328,19 @@ void Core::Account::addedAccount(const QString& jid)
|
|||
{"name", re.name()},
|
||||
{"state", state}
|
||||
});
|
||||
|
||||
if (contact->hasAvatar()) {
|
||||
if (!contact->isAvatarAutoGenerated()) {
|
||||
cData.insert("avatarState", static_cast<uint>(Shared::Avatar::valid));
|
||||
} else {
|
||||
cData.insert("avatarState", static_cast<uint>(Shared::Avatar::autocreated));
|
||||
}
|
||||
cData.insert("avatarPath", contact->avatarPath());
|
||||
} else {
|
||||
cData.insert("avatarState", static_cast<uint>(Shared::Avatar::empty));
|
||||
cData.insert("avatarPath", "");
|
||||
requestVCard(jid);
|
||||
}
|
||||
int grCount = 0;
|
||||
for (QSet<QString>::const_iterator itr = gr.begin(), end = gr.end(); itr != end; ++itr) {
|
||||
const QString& groupName = *itr;
|
||||
|
@ -296,39 +358,35 @@ void Core::Account::addedAccount(const QString& jid)
|
|||
|
||||
void Core::Account::handleNewRosterItem(Core::RosterItem* contact)
|
||||
{
|
||||
|
||||
QObject::connect(contact, SIGNAL(needHistory(const QString&, const QString&, const QDateTime&)), this, SLOT(onContactNeedHistory(const QString&, const QString&, const QDateTime&)));
|
||||
QObject::connect(contact, SIGNAL(historyResponse(const std::list<Shared::Message>&)), this, SLOT(onContactHistoryResponse(const std::list<Shared::Message>&)));
|
||||
QObject::connect(contact, SIGNAL(nameChanged(const QString&)), this, SLOT(onContactNameChanged(const QString&)));
|
||||
QObject::connect(contact, &RosterItem::needHistory, this, &Account::onContactNeedHistory);
|
||||
QObject::connect(contact, &RosterItem::historyResponse, this, &Account::onContactHistoryResponse);
|
||||
QObject::connect(contact, &RosterItem::nameChanged, this, &Account::onContactNameChanged);
|
||||
QObject::connect(contact, &RosterItem::avatarChanged, this, &Account::onContactAvatarChanged);
|
||||
}
|
||||
|
||||
void Core::Account::handleNewContact(Core::Contact* contact)
|
||||
{
|
||||
handleNewRosterItem(contact);
|
||||
QObject::connect(contact, SIGNAL(groupAdded(const QString&)), this, SLOT(onContactGroupAdded(const QString&)));
|
||||
QObject::connect(contact, SIGNAL(groupRemoved(const QString&)), this, SLOT(onContactGroupRemoved(const QString&)));
|
||||
QObject::connect(contact, SIGNAL(subscriptionStateChanged(Shared::SubscriptionState)),
|
||||
this, SLOT(onContactSubscriptionStateChanged(Shared::SubscriptionState)));
|
||||
QObject::connect(contact, &Contact::groupAdded, this, &Account::onContactGroupAdded);
|
||||
QObject::connect(contact, &Contact::groupRemoved, this, &Account::onContactGroupRemoved);
|
||||
QObject::connect(contact, &Contact::subscriptionStateChanged, this, &Account::onContactSubscriptionStateChanged);
|
||||
}
|
||||
|
||||
void Core::Account::handleNewConference(Core::Conference* contact)
|
||||
{
|
||||
handleNewRosterItem(contact);
|
||||
QObject::connect(contact, SIGNAL(nickChanged(const QString&)), this, SLOT(onMucNickNameChanged(const QString&)));
|
||||
QObject::connect(contact, SIGNAL(subjectChanged(const QString&)), this, SLOT(onMucSubjectChanged(const QString&)));
|
||||
QObject::connect(contact, SIGNAL(joinedChanged(bool)), this, SLOT(onMucJoinedChanged(bool)));
|
||||
QObject::connect(contact, SIGNAL(autoJoinChanged(bool)), this, SLOT(onMucAutoJoinChanged(bool)));
|
||||
QObject::connect(contact, SIGNAL(addParticipant(const QString&, const QMap<QString, QVariant>&)),
|
||||
this, SLOT(onMucAddParticipant(const QString&, const QMap<QString, QVariant>&)));
|
||||
QObject::connect(contact, SIGNAL(changeParticipant(const QString&, const QMap<QString, QVariant>&)),
|
||||
this, SLOT(onMucChangeParticipant(const QString&, const QMap<QString, QVariant>&)));
|
||||
QObject::connect(contact, SIGNAL(removeParticipant(const QString&)), this, SLOT(onMucRemoveParticipant(const QString&)));
|
||||
QObject::connect(contact, &Conference::nickChanged, this, &Account::onMucNickNameChanged);
|
||||
QObject::connect(contact, &Conference::subjectChanged, this, &Account::onMucSubjectChanged);
|
||||
QObject::connect(contact, &Conference::joinedChanged, this, &Account::onMucJoinedChanged);
|
||||
QObject::connect(contact, &Conference::autoJoinChanged, this, &Account::onMucAutoJoinChanged);
|
||||
QObject::connect(contact, &Conference::addParticipant, this, &Account::onMucAddParticipant);
|
||||
QObject::connect(contact, &Conference::changeParticipant, this, &Account::onMucChangeParticipant);
|
||||
QObject::connect(contact, &Conference::removeParticipant, this, &Account::onMucRemoveParticipant);
|
||||
}
|
||||
|
||||
|
||||
void Core::Account::onPresenceReceived(const QXmppPresence& presence)
|
||||
void Core::Account::onPresenceReceived(const QXmppPresence& p_presence)
|
||||
{
|
||||
QString id = presence.from();
|
||||
QString id = p_presence.from();
|
||||
QStringList comps = id.split("/");
|
||||
QString jid = comps.front();
|
||||
QString resource = comps.back();
|
||||
|
@ -337,25 +395,75 @@ void Core::Account::onPresenceReceived(const QXmppPresence& presence)
|
|||
|
||||
if (jid == myJid) {
|
||||
if (resource == getResource()) {
|
||||
emit availabilityChanged(presence.availableStatusType());
|
||||
emit availabilityChanged(p_presence.availableStatusType());
|
||||
} else {
|
||||
qDebug() << "Received a presence for another resource of my " << name << " account, skipping";
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (pendingVCardRequests.find(jid) == pendingVCardRequests.end()) {
|
||||
std::map<QString, Contact*>::const_iterator itr = contacts.find(jid);
|
||||
if (itr != contacts.end()) {
|
||||
Contact* cnt = itr->second;
|
||||
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 (!cnt->hasAvatar() || (cnt->hasAvatar() && !cnt->isAvatarAutoGenerated())) {
|
||||
cnt->setAutoGeneratedAvatar();
|
||||
}
|
||||
break;
|
||||
case QXmppPresence::VCardUpdateValidPhoto: //there is a photo, need to load
|
||||
if (cnt->hasAvatar()) {
|
||||
if (cnt->isAvatarAutoGenerated()) {
|
||||
requestVCard(jid);
|
||||
} else {
|
||||
if (cnt->avatarHash() != p_presence.photoHash()) {
|
||||
requestVCard(jid);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
requestVCard(jid);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (presence.type()) {
|
||||
switch (p_presence.type()) {
|
||||
case QXmppPresence::Error:
|
||||
qDebug() << "An error reported by presence from " << id;
|
||||
qDebug() << "An error reported by presence from" << id << p_presence.error().text();
|
||||
break;
|
||||
case QXmppPresence::Available:{
|
||||
QDateTime lastInteraction = presence.lastUserInteraction();
|
||||
QDateTime lastInteraction = p_presence.lastUserInteraction();
|
||||
if (!lastInteraction.isValid()) {
|
||||
lastInteraction = QDateTime::currentDateTime();
|
||||
}
|
||||
emit addPresence(jid, resource, {
|
||||
{"lastActivity", lastInteraction},
|
||||
{"availability", presence.availableStatusType()}, //TODO check and handle invisible
|
||||
{"status", presence.statusText()}
|
||||
{"availability", p_presence.availableStatusType()}, //TODO check and handle invisible
|
||||
{"status", p_presence.statusText()}
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
@ -380,7 +488,7 @@ void Core::Account::onRosterPresenceChanged(const QString& bareJid, const QStrin
|
|||
{
|
||||
//not used for now;
|
||||
qDebug() << "presence changed for " << bareJid << " resource " << resource;
|
||||
const QXmppPresence& presence = client.rosterManager().getPresence(bareJid, resource);
|
||||
const QXmppPresence& presence = rm->getPresence(bareJid, resource);
|
||||
}
|
||||
|
||||
void Core::Account::setLogin(const QString& p_login)
|
||||
|
@ -940,8 +1048,7 @@ void Core::Account::onClientError(QXmppClient::Error err)
|
|||
void Core::Account::subscribeToContact(const QString& jid, const QString& reason)
|
||||
{
|
||||
if (state == Shared::connected) {
|
||||
QXmppRosterManager& rm = client.rosterManager();
|
||||
rm.subscribe(jid, reason);
|
||||
rm->subscribe(jid, reason);
|
||||
} else {
|
||||
qDebug() << "An attempt to subscribe account " << name << " to contact " << jid << " but the account is not in the connected state, skipping";
|
||||
}
|
||||
|
@ -950,8 +1057,7 @@ void Core::Account::subscribeToContact(const QString& jid, const QString& reason
|
|||
void Core::Account::unsubscribeFromContact(const QString& jid, const QString& reason)
|
||||
{
|
||||
if (state == Shared::connected) {
|
||||
QXmppRosterManager& rm = client.rosterManager();
|
||||
rm.unsubscribe(jid, reason);
|
||||
rm->unsubscribe(jid, reason);
|
||||
} else {
|
||||
qDebug() << "An attempt to unsubscribe account " << name << " from contact " << jid << " but the account is not in the connected state, skipping";
|
||||
}
|
||||
|
@ -965,8 +1071,7 @@ void Core::Account::removeContactRequest(const QString& jid)
|
|||
outOfRosterContacts.erase(itr);
|
||||
onRosterItemRemoved(jid);
|
||||
} else {
|
||||
QXmppRosterManager& rm = client.rosterManager();
|
||||
rm.removeItem(jid);
|
||||
rm->removeItem(jid);
|
||||
}
|
||||
} else {
|
||||
qDebug() << "An attempt to remove contact " << jid << " from account " << name << " but the account is not in the connected state, skipping";
|
||||
|
@ -982,8 +1087,7 @@ void Core::Account::addContactRequest(const QString& jid, const QString& name, c
|
|||
qDebug() << "An attempt to add contact " << jid << " to account " << name << " but the account is already queued for adding, skipping";
|
||||
} else {
|
||||
queuedContacts.insert(std::make_pair(jid, "")); //TODO need to add reason here;
|
||||
QXmppRosterManager& rm = client.rosterManager();
|
||||
rm.addItem(jid, name, groups);
|
||||
rm->addItem(jid, name, groups);
|
||||
}
|
||||
} else {
|
||||
qDebug() << "An attempt to add contact " << jid << " to account " << name << " but the account is not in the connected state, skipping";
|
||||
|
@ -1153,3 +1257,297 @@ void Core::Account::addNewRoom(const QString& jid, const QString& nick, const QS
|
|||
{"name", conf->getName()}
|
||||
});
|
||||
}
|
||||
|
||||
void Core::Account::addContactToGroupRequest(const QString& jid, const QString& groupName)
|
||||
{
|
||||
std::map<QString, Contact*>::const_iterator itr = contacts.find(jid);
|
||||
if (itr == contacts.end()) {
|
||||
qDebug() << "An attempt to add non existing contact" << jid << "of account" << name << "to the group" << groupName << ", skipping";
|
||||
} else {
|
||||
QXmppRosterIq::Item item = rm->getRosterEntry(jid);
|
||||
QSet<QString> groups = item.groups();
|
||||
if (groups.find(groupName) == groups.end()) { //TODO need to change it, I guess that sort of code is better in qxmpp lib
|
||||
groups.insert(groupName);
|
||||
item.setGroups(groups);
|
||||
|
||||
QXmppRosterIq iq;
|
||||
iq.setType(QXmppIq::Set);
|
||||
iq.addItem(item);
|
||||
client.sendPacket(iq);
|
||||
} else {
|
||||
qDebug() << "An attempt to add contact" << jid << "of account" << name << "to the group" << groupName << "but it's already in that group, skipping";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Core::Account::removeContactFromGroupRequest(const QString& jid, const QString& groupName)
|
||||
{
|
||||
std::map<QString, Contact*>::const_iterator itr = contacts.find(jid);
|
||||
if (itr == contacts.end()) {
|
||||
qDebug() << "An attempt to remove non existing contact" << jid << "of account" << name << "from the group" << groupName << ", skipping";
|
||||
} else {
|
||||
QXmppRosterIq::Item item = rm->getRosterEntry(jid);
|
||||
QSet<QString> groups = item.groups();
|
||||
QSet<QString>::const_iterator gItr = groups.find(groupName);
|
||||
if (gItr != groups.end()) {
|
||||
groups.erase(gItr);
|
||||
item.setGroups(groups);
|
||||
|
||||
QXmppRosterIq iq;
|
||||
iq.setType(QXmppIq::Set);
|
||||
iq.addItem(item);
|
||||
client.sendPacket(iq);
|
||||
} else {
|
||||
qDebug() << "An attempt to remove contact" << jid << "of account" << name << "from the group" << groupName << "but it's not in that group, skipping";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Core::Account::renameContactRequest(const QString& jid, const QString& newName)
|
||||
{
|
||||
std::map<QString, Contact*>::const_iterator itr = contacts.find(jid);
|
||||
if (itr == contacts.end()) {
|
||||
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 jid = card.from();
|
||||
pendingVCardRequests.erase(jid);
|
||||
RosterItem* item = 0;
|
||||
|
||||
std::map<QString, Contact*>::const_iterator contItr = contacts.find(jid);
|
||||
if (contItr == contacts.end()) {
|
||||
std::map<QString, Conference*>::const_iterator confItr = conferences.find(jid);
|
||||
if (confItr == conferences.end()) {
|
||||
if (jid == getLogin() + "@" + getServer()) {
|
||||
onOwnVCardReceived(card);
|
||||
} else {
|
||||
qDebug() << "received vCard" << jid << "doesn't belong to any of known contacts or conferences, skipping";
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
item = confItr->second;
|
||||
}
|
||||
} else {
|
||||
item = contItr->second;
|
||||
}
|
||||
|
||||
QByteArray ava = card.photo();
|
||||
|
||||
if (ava.size() > 0) {
|
||||
item->setAvatar(ava);
|
||||
} else {
|
||||
if (!item->hasAvatar() || !item->isAvatarAutoGenerated()) {
|
||||
item->setAutoGeneratedAvatar();
|
||||
}
|
||||
}
|
||||
|
||||
Shared::VCard vCard;
|
||||
initializeVCard(vCard, card);
|
||||
|
||||
if (item->hasAvatar()) {
|
||||
if (!item->isAvatarAutoGenerated()) {
|
||||
vCard.setAvatarType(Shared::Avatar::valid);
|
||||
} else {
|
||||
vCard.setAvatarType(Shared::Avatar::autocreated);
|
||||
}
|
||||
vCard.setAvatarPath(item->avatarPath());
|
||||
} else {
|
||||
vCard.setAvatarType(Shared::Avatar::empty);
|
||||
}
|
||||
|
||||
QMap<QString, QVariant> cd = {
|
||||
{"avatarState", static_cast<quint8>(vCard.getAvatarType())},
|
||||
{"avatarPath", vCard.getAvatarPath()}
|
||||
};
|
||||
emit changeContact(jid, cd);
|
||||
emit receivedVCard(jid, vCard);
|
||||
}
|
||||
|
||||
void Core::Account::onOwnVCardReceived(const QXmppVCardIq& card)
|
||||
{
|
||||
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";
|
||||
} else {
|
||||
avatarType = "";
|
||||
avatarHash = "";
|
||||
avaChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
return QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/" + name + "/" + "avatar." + avatarType;
|
||||
}
|
||||
|
||||
void Core::Account::onContactAvatarChanged(Shared::Avatar type, const QString& path)
|
||||
{
|
||||
RosterItem* item = static_cast<RosterItem*>(sender());
|
||||
QMap<QString, QVariant> cData({
|
||||
{"avatarState", static_cast<uint>(type)},
|
||||
{"avatarPath", path}
|
||||
});
|
||||
|
||||
emit changeContact(item->jid, cData);
|
||||
}
|
||||
|
||||
void Core::Account::requestVCard(const QString& jid)
|
||||
{
|
||||
if (pendingVCardRequests.find(jid) == pendingVCardRequests.end()) {
|
||||
if (jid == getLogin() + "@" + getServer()) {
|
||||
if (!ownVCardRequestInProgress) {
|
||||
vm->requestClientVCard();
|
||||
ownVCardRequestInProgress = true;
|
||||
}
|
||||
} else {
|
||||
vm->requestVCard(jid);
|
||||
pendingVCardRequests.insert(jid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Core::Account::uploadVCard(const Shared::VCard& card)
|
||||
{
|
||||
QXmppVCardIq iq;
|
||||
initializeQXmppVCard(iq, card);
|
||||
|
||||
bool avatarChanged = false;
|
||||
if (card.getAvatarType() == Shared::Avatar::empty) {
|
||||
if (avatarType.size() > 0) {
|
||||
avatarChanged = true;
|
||||
}
|
||||
} else {
|
||||
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";
|
||||
avatarChanged = true;
|
||||
} else {
|
||||
data = oA.readAll();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
data = avatar.readAll();
|
||||
avatarChanged = true;
|
||||
}
|
||||
} 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";
|
||||
avatarChanged = true;
|
||||
} else {
|
||||
data = oA.readAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (data.size() > 0) {
|
||||
QMimeDatabase db;
|
||||
type = db.mimeTypeForData(data).name();
|
||||
iq.setPhoto(data);
|
||||
iq.setPhotoType(type);
|
||||
}
|
||||
}
|
||||
|
||||
vm->setClientVCard(iq);
|
||||
onOwnVCardReceived(iq);
|
||||
}
|
||||
|
|
|
@ -19,7 +19,13 @@
|
|||
#ifndef CORE_ACCOUNT_H
|
||||
#define CORE_ACCOUNT_H
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <QObject>
|
||||
#include <QCryptographicHash>
|
||||
#include <QFile>
|
||||
#include <QMimeDatabase>
|
||||
#include <QStandardPaths>
|
||||
#include <QDir>
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
|
@ -31,6 +37,8 @@
|
|||
#include <QXmppBookmarkManager.h>
|
||||
#include <QXmppBookmarkSet.h>
|
||||
#include <QXmppHttpUploadIq.h>
|
||||
#include <QXmppVCardIq.h>
|
||||
#include <QXmppVCardManager.h>
|
||||
#include "../global.h"
|
||||
#include "contact.h"
|
||||
#include "conference.h"
|
||||
|
@ -55,6 +63,7 @@ public:
|
|||
QString getServer() const;
|
||||
QString getPassword() const;
|
||||
QString getResource() const;
|
||||
QString getAvatarPath() const;
|
||||
Shared::Availability getAvailability() const;
|
||||
|
||||
void setName(const QString& p_name);
|
||||
|
@ -71,13 +80,19 @@ public:
|
|||
void unsubscribeFromContact(const QString& jid, const QString& reason);
|
||||
void removeContactRequest(const QString& jid);
|
||||
void addContactRequest(const QString& jid, const QString& name, const QSet<QString>& groups);
|
||||
void addContactToGroupRequest(const QString& jid, const QString& groupName);
|
||||
void removeContactFromGroupRequest(const QString& jid, const QString& groupName);
|
||||
void renameContactRequest(const QString& jid, const QString& newName);
|
||||
|
||||
void setRoomJoined(const QString& jid, bool joined);
|
||||
void setRoomAutoJoin(const QString& jid, bool joined);
|
||||
void removeRoomRequest(const QString& jid);
|
||||
void addRoomRequest(const QString& jid, const QString& nick, const QString& password, bool autoJoin);
|
||||
void requestVCard(const QString& jid);
|
||||
void uploadVCard(const Shared::VCard& card);
|
||||
|
||||
signals:
|
||||
void changed(const QMap<QString, QVariant>& data);
|
||||
void connectionStateChanged(int);
|
||||
void availabilityChanged(int);
|
||||
void addGroup(const QString& name);
|
||||
|
@ -97,6 +112,7 @@ signals:
|
|||
void addRoomParticipant(const QString& jid, const QString& nickName, const QMap<QString, QVariant>& data);
|
||||
void changeRoomParticipant(const QString& jid, const QString& nickName, const QMap<QString, QVariant>& data);
|
||||
void removeRoomParticipant(const QString& jid, const QString& nickName);
|
||||
void receivedVCard(const QString& jid, const Shared::VCard& card);
|
||||
|
||||
private:
|
||||
QString name;
|
||||
|
@ -110,6 +126,8 @@ private:
|
|||
QXmppMamManager* am;
|
||||
QXmppMucManager* mm;
|
||||
QXmppBookmarkManager* bm;
|
||||
QXmppRosterManager* rm;
|
||||
QXmppVCardManager* vm;
|
||||
std::map<QString, Contact*> contacts;
|
||||
std::map<QString, Conference*> conferences;
|
||||
unsigned int maxReconnectTimes;
|
||||
|
@ -117,6 +135,11 @@ private:
|
|||
|
||||
std::map<QString, QString> queuedContacts;
|
||||
std::set<QString> outOfRosterContacts;
|
||||
std::set<QString> pendingVCardRequests;
|
||||
|
||||
QString avatarHash;
|
||||
QString avatarType;
|
||||
bool ownVCardRequestInProgress;
|
||||
|
||||
private slots:
|
||||
void onClientConnected();
|
||||
|
@ -155,8 +178,12 @@ private slots:
|
|||
void onContactSubscriptionStateChanged(Shared::SubscriptionState state);
|
||||
void onContactHistoryResponse(const std::list<Shared::Message>& list);
|
||||
void onContactNeedHistory(const QString& before, const QString& after, const QDateTime& at);
|
||||
void onContactAvatarChanged(Shared::Avatar, const QString& path);
|
||||
|
||||
void onMamLog(QXmppLogger::MessageType type, const QString &msg);
|
||||
|
||||
void onVCardReceived(const QXmppVCardIq& card);
|
||||
void onOwnVCardReceived(const QXmppVCardIq& card);
|
||||
|
||||
private:
|
||||
void addedAccount(const QString &bareJid);
|
||||
|
@ -176,6 +203,8 @@ private:
|
|||
|
||||
};
|
||||
|
||||
void initializeVCard(Shared::VCard& vCard, const QXmppVCardIq& card);
|
||||
void initializeQXmppVCard(QXmppVCardIq& card, const Shared::VCard& vCard);
|
||||
}
|
||||
|
||||
|
||||
|
|
275
core/adapterFuctions.cpp
Normal file
275
core/adapterFuctions.cpp
Normal file
|
@ -0,0 +1,275 @@
|
|||
/*
|
||||
* 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 "account.h"
|
||||
|
||||
void Core::initializeVCard(Shared::VCard& vCard, const QXmppVCardIq& card)
|
||||
{
|
||||
vCard.setFullName(card.fullName());
|
||||
vCard.setFirstName(card.firstName());
|
||||
vCard.setMiddleName(card.middleName());
|
||||
vCard.setLastName(card.lastName());
|
||||
vCard.setBirthday(card.birthday());
|
||||
vCard.setNickName(card.nickName());
|
||||
vCard.setDescription(card.description());
|
||||
vCard.setUrl(card.url());
|
||||
QXmppVCardOrganization org = card.organization();
|
||||
vCard.setOrgName(org.organization());
|
||||
vCard.setOrgRole(org.role());
|
||||
vCard.setOrgUnit(org.unit());
|
||||
vCard.setOrgTitle(org.title());
|
||||
|
||||
QList<QXmppVCardEmail> emails = card.emails();
|
||||
std::deque<Shared::VCard::Email>& myEmails = vCard.getEmails();
|
||||
for (const QXmppVCardEmail& em : emails) {
|
||||
QString addr = em.address();
|
||||
if (addr.size() != 0) {
|
||||
QXmppVCardEmail::Type et = em.type();
|
||||
bool prefered = false;
|
||||
bool accounted = false;
|
||||
if (et & QXmppVCardEmail::Preferred) {
|
||||
prefered = true;
|
||||
}
|
||||
if (et & QXmppVCardEmail::Home) {
|
||||
myEmails.emplace_back(addr, Shared::VCard::Email::home, prefered);
|
||||
accounted = true;
|
||||
}
|
||||
if (et & QXmppVCardEmail::Work) {
|
||||
myEmails.emplace_back(addr, Shared::VCard::Email::work, prefered);
|
||||
accounted = true;
|
||||
}
|
||||
if (!accounted) {
|
||||
myEmails.emplace_back(addr, Shared::VCard::Email::none, prefered);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
QList<QXmppVCardPhone> phones = card.phones();
|
||||
std::deque<Shared::VCard::Phone>& myPhones = vCard.getPhones();
|
||||
for (const QXmppVCardPhone& ph : phones) {
|
||||
QString num = ph.number();
|
||||
if (num.size() != 0) {
|
||||
QXmppVCardPhone::Type pt = ph.type();
|
||||
bool prefered = false;
|
||||
bool accounted = false;
|
||||
if (pt & QXmppVCardPhone::Preferred) {
|
||||
prefered = true;
|
||||
}
|
||||
|
||||
bool home = false;
|
||||
bool work = false;
|
||||
|
||||
if (pt & QXmppVCardPhone::Home) {
|
||||
home = true;
|
||||
}
|
||||
if (pt & QXmppVCardPhone::Work) {
|
||||
work = true;
|
||||
}
|
||||
|
||||
|
||||
if (pt & QXmppVCardPhone::Fax) {
|
||||
if (home || work) {
|
||||
if (home) {
|
||||
myPhones.emplace_back(num, Shared::VCard::Phone::fax, Shared::VCard::Phone::home, prefered);
|
||||
}
|
||||
if (work) {
|
||||
myPhones.emplace_back(num, Shared::VCard::Phone::fax, Shared::VCard::Phone::work, prefered);
|
||||
}
|
||||
} else {
|
||||
myPhones.emplace_back(num, Shared::VCard::Phone::fax, Shared::VCard::Phone::none, prefered);
|
||||
}
|
||||
accounted = true;
|
||||
}
|
||||
if (pt & QXmppVCardPhone::Voice) {
|
||||
if (home || work) {
|
||||
if (home) {
|
||||
myPhones.emplace_back(num, Shared::VCard::Phone::voice, Shared::VCard::Phone::home, prefered);
|
||||
}
|
||||
if (work) {
|
||||
myPhones.emplace_back(num, Shared::VCard::Phone::voice, Shared::VCard::Phone::work, prefered);
|
||||
}
|
||||
} else {
|
||||
myPhones.emplace_back(num, Shared::VCard::Phone::voice, Shared::VCard::Phone::none, prefered);
|
||||
}
|
||||
accounted = true;
|
||||
}
|
||||
if (pt & QXmppVCardPhone::Pager) {
|
||||
if (home || work) {
|
||||
if (home) {
|
||||
myPhones.emplace_back(num, Shared::VCard::Phone::pager, Shared::VCard::Phone::home, prefered);
|
||||
}
|
||||
if (work) {
|
||||
myPhones.emplace_back(num, Shared::VCard::Phone::pager, Shared::VCard::Phone::work, prefered);
|
||||
}
|
||||
} else {
|
||||
myPhones.emplace_back(num, Shared::VCard::Phone::pager, Shared::VCard::Phone::none, prefered);
|
||||
}
|
||||
accounted = true;
|
||||
}
|
||||
if (pt & QXmppVCardPhone::Cell) {
|
||||
if (home || work) {
|
||||
if (home) {
|
||||
myPhones.emplace_back(num, Shared::VCard::Phone::cell, Shared::VCard::Phone::home, prefered);
|
||||
}
|
||||
if (work) {
|
||||
myPhones.emplace_back(num, Shared::VCard::Phone::cell, Shared::VCard::Phone::work, prefered);
|
||||
}
|
||||
} else {
|
||||
myPhones.emplace_back(num, Shared::VCard::Phone::cell, Shared::VCard::Phone::none, prefered);
|
||||
}
|
||||
accounted = true;
|
||||
}
|
||||
if (pt & QXmppVCardPhone::Video) {
|
||||
if (home || work) {
|
||||
if (home) {
|
||||
myPhones.emplace_back(num, Shared::VCard::Phone::video, Shared::VCard::Phone::home, prefered);
|
||||
}
|
||||
if (work) {
|
||||
myPhones.emplace_back(num, Shared::VCard::Phone::video, Shared::VCard::Phone::work, prefered);
|
||||
}
|
||||
} else {
|
||||
myPhones.emplace_back(num, Shared::VCard::Phone::video, Shared::VCard::Phone::none, prefered);
|
||||
}
|
||||
accounted = true;
|
||||
}
|
||||
if (pt & QXmppVCardPhone::Modem) {
|
||||
if (home || work) {
|
||||
if (home) {
|
||||
myPhones.emplace_back(num, Shared::VCard::Phone::modem, Shared::VCard::Phone::home, prefered);
|
||||
}
|
||||
if (work) {
|
||||
myPhones.emplace_back(num, Shared::VCard::Phone::modem, Shared::VCard::Phone::work, prefered);
|
||||
}
|
||||
} else {
|
||||
myPhones.emplace_back(num, Shared::VCard::Phone::modem, Shared::VCard::Phone::none, prefered);
|
||||
}
|
||||
accounted = true;
|
||||
}
|
||||
if (!accounted) {
|
||||
if (home || work) {
|
||||
if (home) {
|
||||
myPhones.emplace_back(num, Shared::VCard::Phone::other, Shared::VCard::Phone::home, prefered);
|
||||
}
|
||||
if (work) {
|
||||
myPhones.emplace_back(num, Shared::VCard::Phone::other, Shared::VCard::Phone::work, prefered);
|
||||
}
|
||||
} else {
|
||||
myPhones.emplace_back(num, Shared::VCard::Phone::other, Shared::VCard::Phone::none, prefered);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Core::initializeQXmppVCard(QXmppVCardIq& iq, const Shared::VCard& card) {
|
||||
iq.setFullName(card.getFullName());
|
||||
iq.setFirstName(card.getFirstName());
|
||||
iq.setMiddleName(card.getMiddleName());
|
||||
iq.setLastName(card.getLastName());
|
||||
iq.setNickName(card.getNickName());
|
||||
iq.setBirthday(card.getBirthday());
|
||||
iq.setDescription(card.getDescription());
|
||||
iq.setUrl(card.getUrl());
|
||||
QXmppVCardOrganization org;
|
||||
org.setOrganization(card.getOrgName());
|
||||
org.setUnit(card.getOrgUnit());
|
||||
org.setRole(card.getOrgRole());
|
||||
org.setTitle(card.getOrgTitle());
|
||||
iq.setOrganization(org);
|
||||
|
||||
const std::deque<Shared::VCard::Email>& myEmails = card.getEmails();
|
||||
QList<QXmppVCardEmail> emails;
|
||||
for (const Shared::VCard::Email& mEm : myEmails) {
|
||||
QXmppVCardEmail em;
|
||||
QXmppVCardEmail::Type t = QXmppVCardEmail::Internet;
|
||||
if (mEm.prefered) {
|
||||
t = t | QXmppVCardEmail::Preferred;
|
||||
}
|
||||
if (mEm.role == Shared::VCard::Email::home) {
|
||||
t = t | QXmppVCardEmail::Home;
|
||||
} else if (mEm.role == Shared::VCard::Email::work) {
|
||||
t = t | QXmppVCardEmail::Work;
|
||||
}
|
||||
em.setType(t);
|
||||
em.setAddress(mEm.address);
|
||||
|
||||
emails.push_back(em);
|
||||
}
|
||||
|
||||
std::map<QString, QXmppVCardPhone> phones;
|
||||
QList<QXmppVCardPhone> phs;
|
||||
const std::deque<Shared::VCard::Phone>& myPhones = card.getPhones();
|
||||
for (const Shared::VCard::Phone& mPh : myPhones) {
|
||||
std::map<QString, QXmppVCardPhone>::iterator itr = phones.find(mPh.number);
|
||||
if (itr == phones.end()) {
|
||||
itr = phones.emplace(mPh.number, QXmppVCardPhone()).first;
|
||||
}
|
||||
QXmppVCardPhone& phone = itr->second;
|
||||
phone.setNumber(mPh.number);
|
||||
|
||||
switch (mPh.type) {
|
||||
case Shared::VCard::Phone::fax:
|
||||
phone.setType(phone.type() | QXmppVCardPhone::Fax);
|
||||
break;
|
||||
case Shared::VCard::Phone::pager:
|
||||
phone.setType(phone.type() | QXmppVCardPhone::Pager);
|
||||
break;
|
||||
case Shared::VCard::Phone::voice:
|
||||
phone.setType(phone.type() | QXmppVCardPhone::Voice);
|
||||
break;
|
||||
case Shared::VCard::Phone::cell:
|
||||
phone.setType(phone.type() | QXmppVCardPhone::Cell);
|
||||
break;
|
||||
case Shared::VCard::Phone::video:
|
||||
phone.setType(phone.type() | QXmppVCardPhone::Video);
|
||||
break;
|
||||
case Shared::VCard::Phone::modem:
|
||||
phone.setType(phone.type() | QXmppVCardPhone::Modem);
|
||||
break;
|
||||
case Shared::VCard::Phone::other:
|
||||
phone.setType(phone.type() | QXmppVCardPhone::PCS); //loss of information, but I don't even know what the heck is this type of phone!
|
||||
break;
|
||||
}
|
||||
|
||||
switch (mPh.role) {
|
||||
case Shared::VCard::Phone::home:
|
||||
phone.setType(phone.type() | QXmppVCardPhone::Home);
|
||||
break;
|
||||
case Shared::VCard::Phone::work:
|
||||
phone.setType(phone.type() | QXmppVCardPhone::Work);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (mPh.prefered) {
|
||||
phone.setType(phone.type() | QXmppVCardPhone::Preferred);
|
||||
}
|
||||
}
|
||||
for (const std::pair<QString, QXmppVCardPhone>& phone : phones) {
|
||||
phs.push_back(phone.second);
|
||||
}
|
||||
|
||||
iq.setEmails(emails);
|
||||
iq.setPhones(phs);
|
||||
}
|
||||
|
||||
#endif // CORE_ADAPTER_FUNCTIONS_H
|
323
core/archive.cpp
323
core/archive.cpp
|
@ -32,7 +32,11 @@ Core::Archive::Archive(const QString& p_jid, QObject* parent):
|
|||
environment(),
|
||||
main(),
|
||||
order(),
|
||||
stats()
|
||||
stats(),
|
||||
hasAvatar(false),
|
||||
avatarAutoGenerated(false),
|
||||
avatarHash(),
|
||||
avatarType()
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -57,7 +61,7 @@ void Core::Archive::open(const QString& account)
|
|||
}
|
||||
|
||||
mdb_env_set_maxdbs(environment, 4);
|
||||
mdb_env_set_mapsize(environment, 1UL * 1024UL * 1024UL * 1024UL);
|
||||
mdb_env_set_mapsize(environment, 512UL * 1024UL * 1024UL);
|
||||
mdb_env_open(environment, path.toStdString().c_str(), 0, 0664);
|
||||
|
||||
MDB_txn *txn;
|
||||
|
@ -66,7 +70,49 @@ void Core::Archive::open(const QString& account)
|
|||
mdb_dbi_open(txn, "order", MDB_CREATE | MDB_INTEGERKEY, &order);
|
||||
mdb_dbi_open(txn, "stats", MDB_CREATE, &stats);
|
||||
mdb_txn_commit(txn);
|
||||
fromTheBeginning = _isFromTheBeginning();
|
||||
|
||||
mdb_txn_begin(environment, NULL, 0, &txn);
|
||||
try {
|
||||
fromTheBeginning = getStatBoolValue("beginning", txn);
|
||||
} catch (NotFound e) {
|
||||
fromTheBeginning = false;
|
||||
}
|
||||
try {
|
||||
hasAvatar = getStatBoolValue("hasAvatar", txn);
|
||||
} catch (NotFound e) {
|
||||
hasAvatar = false;
|
||||
}
|
||||
if (hasAvatar) {
|
||||
try {
|
||||
avatarAutoGenerated = getStatBoolValue("avatarAutoGenerated", txn);
|
||||
} catch (NotFound e) {
|
||||
avatarAutoGenerated = false;
|
||||
}
|
||||
|
||||
avatarType = getStatStringValue("avatarType", txn).c_str();
|
||||
if (avatarAutoGenerated) {
|
||||
avatarHash = "";
|
||||
} else {
|
||||
avatarHash = getStatStringValue("avatarHash", txn).c_str();
|
||||
}
|
||||
} else {
|
||||
avatarAutoGenerated = false;
|
||||
avatarHash = "";
|
||||
avatarType = "";
|
||||
}
|
||||
mdb_txn_abort(txn);
|
||||
|
||||
if (hasAvatar) {
|
||||
QFile ava(path + "/avatar." + avatarType);
|
||||
if (!ava.exists()) {
|
||||
bool success = dropAvatar();
|
||||
if (!success) {
|
||||
qDebug() << "error opening archive" << jid << "for account" << account
|
||||
<< ". There is supposed to be avatar but the file doesn't exist, couldn't even drop it, it surely will lead to an error";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
opened = true;
|
||||
}
|
||||
}
|
||||
|
@ -396,40 +442,6 @@ std::list<Shared::Message> Core::Archive::getBefore(int count, const QString& id
|
|||
return res;
|
||||
}
|
||||
|
||||
bool Core::Archive::_isFromTheBeginning()
|
||||
{
|
||||
std::string strKey = "beginning";
|
||||
|
||||
MDB_val lmdbKey, lmdbData;
|
||||
lmdbKey.mv_size = strKey.size();
|
||||
lmdbKey.mv_data = (char*)strKey.c_str();
|
||||
|
||||
MDB_txn *txn;
|
||||
int rc;
|
||||
mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn);
|
||||
rc = mdb_get(txn, stats, &lmdbKey, &lmdbData);
|
||||
if (rc == MDB_NOTFOUND) {
|
||||
mdb_txn_abort(txn);
|
||||
return false;
|
||||
} else if (rc) {
|
||||
qDebug() <<"isFromTheBeginning error: " << mdb_strerror(rc);
|
||||
mdb_txn_abort(txn);
|
||||
throw NotFound(strKey, jid.toStdString());
|
||||
} else {
|
||||
uint8_t value = *(uint8_t*)(lmdbData.mv_data);
|
||||
bool is;
|
||||
if (value == 144) {
|
||||
is = false;
|
||||
} else if (value == 72) {
|
||||
is = true;
|
||||
} else {
|
||||
qDebug() <<"isFromTheBeginning error: stored value doesn't match any magic number, the answer is most probably wrong";
|
||||
}
|
||||
mdb_txn_abort(txn);
|
||||
return is;
|
||||
}
|
||||
}
|
||||
|
||||
bool Core::Archive::isFromTheBeginning()
|
||||
{
|
||||
if (!opened) {
|
||||
|
@ -445,26 +457,15 @@ void Core::Archive::setFromTheBeginning(bool is)
|
|||
}
|
||||
if (fromTheBeginning != is) {
|
||||
fromTheBeginning = is;
|
||||
const std::string& id = "beginning";
|
||||
uint8_t value = 144;
|
||||
if (is) {
|
||||
value = 72;
|
||||
}
|
||||
|
||||
MDB_val lmdbKey, lmdbData;
|
||||
lmdbKey.mv_size = id.size();
|
||||
lmdbKey.mv_data = (char*)id.c_str();
|
||||
lmdbData.mv_size = sizeof value;
|
||||
lmdbData.mv_data = &value;
|
||||
MDB_txn *txn;
|
||||
mdb_txn_begin(environment, NULL, 0, &txn);
|
||||
int rc;
|
||||
rc = mdb_put(txn, stats, &lmdbKey, &lmdbData, 0);
|
||||
if (rc != 0) {
|
||||
qDebug() << "Couldn't store beginning key into stat database:" << mdb_strerror(rc);
|
||||
bool success = setStatValue("beginning", is, txn);
|
||||
if (success != 0) {
|
||||
mdb_txn_abort(txn);
|
||||
} else {
|
||||
mdb_txn_commit(txn);
|
||||
}
|
||||
mdb_txn_commit(txn);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -508,3 +509,221 @@ void Core::Archive::printKeys()
|
|||
mdb_cursor_close(cursor);
|
||||
mdb_txn_abort(txn);
|
||||
}
|
||||
|
||||
bool Core::Archive::getStatBoolValue(const std::string& id, MDB_txn* txn)
|
||||
{
|
||||
MDB_val lmdbKey, lmdbData;
|
||||
lmdbKey.mv_size = id.size();
|
||||
lmdbKey.mv_data = (char*)id.c_str();
|
||||
|
||||
int rc;
|
||||
rc = mdb_get(txn, stats, &lmdbKey, &lmdbData);
|
||||
if (rc == MDB_NOTFOUND) {
|
||||
throw NotFound(id, jid.toStdString());
|
||||
} else if (rc) {
|
||||
qDebug() << "error retrieving" << id.c_str() << "from stats db of" << jid << mdb_strerror(rc);
|
||||
throw 15; //TODO proper exception
|
||||
} else {
|
||||
uint8_t value = *(uint8_t*)(lmdbData.mv_data);
|
||||
bool is;
|
||||
if (value == 144) {
|
||||
is = false;
|
||||
} else if (value == 72) {
|
||||
is = true;
|
||||
} else {
|
||||
qDebug() << "error retrieving boolean stat" << id.c_str() << ": stored value doesn't match any magic number, the answer is most probably wrong";
|
||||
throw NotFound(id, jid.toStdString());
|
||||
}
|
||||
return is;
|
||||
}
|
||||
}
|
||||
|
||||
std::string Core::Archive::getStatStringValue(const std::string& id, MDB_txn* txn)
|
||||
{
|
||||
MDB_cursor* cursor;
|
||||
MDB_val lmdbKey, lmdbData;
|
||||
lmdbKey.mv_size = id.size();
|
||||
lmdbKey.mv_data = (char*)id.c_str();
|
||||
|
||||
int rc;
|
||||
rc = mdb_get(txn, stats, &lmdbKey, &lmdbData);
|
||||
if (rc == MDB_NOTFOUND) {
|
||||
throw NotFound(id, jid.toStdString());
|
||||
} else if (rc) {
|
||||
qDebug() << "error retrieving" << id.c_str() << "from stats db of" << jid << mdb_strerror(rc);
|
||||
throw 15; //TODO proper exception
|
||||
} else {
|
||||
std::string value((char*)lmdbData.mv_data, lmdbData.mv_size);
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
bool Core::Archive::setStatValue(const std::string& id, bool value, MDB_txn* txn)
|
||||
{
|
||||
uint8_t binvalue = 144;
|
||||
if (value) {
|
||||
binvalue = 72;
|
||||
}
|
||||
MDB_val lmdbKey, lmdbData;
|
||||
lmdbKey.mv_size = id.size();
|
||||
lmdbKey.mv_data = (char*)id.c_str();
|
||||
lmdbData.mv_size = sizeof binvalue;
|
||||
lmdbData.mv_data = &binvalue;
|
||||
int rc = mdb_put(txn, stats, &lmdbKey, &lmdbData, 0);
|
||||
if (rc != 0) {
|
||||
qDebug() << "Couldn't store" << id.c_str() << "key into stat database:" << mdb_strerror(rc);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Core::Archive::setStatValue(const std::string& id, const std::string& value, MDB_txn* txn)
|
||||
{
|
||||
MDB_val lmdbKey, lmdbData;
|
||||
lmdbKey.mv_size = id.size();
|
||||
lmdbKey.mv_data = (char*)id.c_str();
|
||||
lmdbData.mv_size = value.size();
|
||||
lmdbData.mv_data = (char*)value.c_str();
|
||||
int rc = mdb_put(txn, stats, &lmdbKey, &lmdbData, 0);
|
||||
if (rc != 0) {
|
||||
qDebug() << "Couldn't store" << id.c_str() << "key into stat database:" << mdb_strerror(rc);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Core::Archive::getHasAvatar() const
|
||||
{
|
||||
if (!opened) {
|
||||
throw Closed("getHasAvatar", jid.toStdString());
|
||||
}
|
||||
|
||||
return hasAvatar;
|
||||
}
|
||||
|
||||
bool Core::Archive::getAutoAvatar() const
|
||||
{
|
||||
if (!opened) {
|
||||
throw Closed("getAutoAvatar", jid.toStdString());
|
||||
}
|
||||
|
||||
return avatarAutoGenerated;
|
||||
}
|
||||
|
||||
QString Core::Archive::getAvatarHash() const
|
||||
{
|
||||
if (!opened) {
|
||||
throw Closed("getAvatarHash", jid.toStdString());
|
||||
}
|
||||
|
||||
return avatarHash;
|
||||
}
|
||||
|
||||
QString Core::Archive::getAvatarType() const
|
||||
{
|
||||
if (!opened) {
|
||||
throw Closed("getAvatarType", jid.toStdString());
|
||||
}
|
||||
|
||||
return avatarType;
|
||||
}
|
||||
|
||||
bool Core::Archive::dropAvatar()
|
||||
{
|
||||
MDB_txn *txn;
|
||||
mdb_txn_begin(environment, NULL, 0, &txn);
|
||||
bool success = setStatValue("hasAvatar", false, txn);
|
||||
success = success && setStatValue("avatarAutoGenerated", false, txn);
|
||||
success = success && setStatValue("avatarHash", "", txn);
|
||||
success = success && setStatValue("avatarType", "", txn);
|
||||
if (!success) {
|
||||
mdb_txn_abort(txn);
|
||||
return false;
|
||||
} else {
|
||||
hasAvatar = false;
|
||||
avatarAutoGenerated = false;
|
||||
avatarHash = "";
|
||||
avatarType = "";
|
||||
mdb_txn_commit(txn);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool Core::Archive::setAvatar(const QByteArray& data, bool generated)
|
||||
{
|
||||
if (!opened) {
|
||||
throw Closed("setAvatar", jid.toStdString());
|
||||
}
|
||||
|
||||
if (data.size() == 0) {
|
||||
if (!hasAvatar) {
|
||||
return false;
|
||||
} else {
|
||||
return dropAvatar();
|
||||
}
|
||||
} else {
|
||||
const char* cep;
|
||||
mdb_env_get_path(environment, &cep);
|
||||
QString currentPath(cep);
|
||||
bool needToRemoveOld = false;
|
||||
QCryptographicHash hash(QCryptographicHash::Sha1);
|
||||
hash.addData(data);
|
||||
QString newHash(hash.result());
|
||||
if (hasAvatar) {
|
||||
if (!generated && !avatarAutoGenerated && avatarHash == newHash) {
|
||||
return false;
|
||||
}
|
||||
QFile oldAvatar(currentPath + "/avatar." + avatarType);
|
||||
if (oldAvatar.exists()) {
|
||||
if (oldAvatar.rename(currentPath + "/avatar." + avatarType + ".bak")) {
|
||||
needToRemoveOld = true;
|
||||
} else {
|
||||
qDebug() << "Can't change avatar: couldn't get rid of the old avatar" << oldAvatar.fileName();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
QMimeDatabase db;
|
||||
QMimeType type = db.mimeTypeForData(data);
|
||||
QString ext = type.preferredSuffix();
|
||||
QFile newAvatar(currentPath + "/avatar." + ext);
|
||||
if (newAvatar.open(QFile::WriteOnly)) {
|
||||
newAvatar.write(data);
|
||||
newAvatar.close();
|
||||
|
||||
MDB_txn *txn;
|
||||
mdb_txn_begin(environment, NULL, 0, &txn);
|
||||
bool success = setStatValue("hasAvatar", true, txn);
|
||||
success = success && setStatValue("avatarAutoGenerated", generated, txn);
|
||||
success = success && setStatValue("avatarHash", newHash.toStdString(), txn);
|
||||
success = success && setStatValue("avatarType", ext.toStdString(), txn);
|
||||
if (!success) {
|
||||
qDebug() << "Can't change avatar: couldn't store changes to database for" << newAvatar.fileName() << "rolling back to the previous state";
|
||||
if (needToRemoveOld) {
|
||||
QFile oldAvatar(currentPath + "/avatar." + avatarType + ".bak");
|
||||
oldAvatar.rename(currentPath + "/avatar." + avatarType);
|
||||
}
|
||||
mdb_txn_abort(txn);
|
||||
return false;
|
||||
} else {
|
||||
hasAvatar = true;
|
||||
avatarAutoGenerated = generated;
|
||||
avatarHash = newHash;
|
||||
avatarType = ext;
|
||||
mdb_txn_commit(txn);
|
||||
if (needToRemoveOld) {
|
||||
QFile oldAvatar(currentPath + "/avatar." + avatarType + ".bak");
|
||||
oldAvatar.remove();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
qDebug() << "Can't change avatar: cant open file to write" << newAvatar.fileName() << "rolling back to the previous state";
|
||||
if (needToRemoveOld) {
|
||||
QFile oldAvatar(currentPath + "/avatar." + avatarType + ".bak");
|
||||
oldAvatar.rename(currentPath + "/avatar." + avatarType);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,10 @@
|
|||
#define CORE_ARCHIVE_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QCryptographicHash>
|
||||
#include <QMimeDatabase>
|
||||
#include <QMimeType>
|
||||
|
||||
#include "../global.h"
|
||||
#include <lmdb.h>
|
||||
#include "../exception.h"
|
||||
|
@ -49,6 +53,11 @@ public:
|
|||
std::list<Shared::Message> getBefore(int count, const QString& id);
|
||||
bool isFromTheBeginning();
|
||||
void setFromTheBeginning(bool is);
|
||||
bool getHasAvatar() const;
|
||||
bool getAutoAvatar() const;
|
||||
QString getAvatarHash() const;
|
||||
QString getAvatarType() const;
|
||||
bool setAvatar(const QByteArray& data, bool generated = false);
|
||||
|
||||
public:
|
||||
const QString jid;
|
||||
|
@ -131,10 +140,19 @@ private:
|
|||
MDB_dbi main;
|
||||
MDB_dbi order;
|
||||
MDB_dbi stats;
|
||||
bool hasAvatar;
|
||||
bool avatarAutoGenerated;
|
||||
QString avatarHash;
|
||||
QString avatarType;
|
||||
|
||||
bool _isFromTheBeginning();
|
||||
bool getStatBoolValue(const std::string& id, MDB_txn* txn);
|
||||
std::string getStatStringValue(const std::string& id, MDB_txn* txn);
|
||||
|
||||
bool setStatValue(const std::string& id, bool value, MDB_txn* txn);
|
||||
bool setStatValue(const std::string& id, const std::string& value, MDB_txn* txn);
|
||||
void printOrder();
|
||||
void printKeys();
|
||||
bool dropAvatar();
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -30,15 +30,15 @@ Core::Conference::Conference(const QString& p_jid, const QString& p_account, boo
|
|||
muc = true;
|
||||
name = p_name;
|
||||
|
||||
connect(room, SIGNAL(joined()), this, SLOT(onRoomJoined()));
|
||||
connect(room, SIGNAL(left()), this, SLOT(onRoomLeft()));
|
||||
connect(room, SIGNAL(nameChanged(const QString&)), this, SLOT(onRoomNameChanged(const QString&)));
|
||||
connect(room, SIGNAL(subjectChanged(const QString&)), this, SLOT(onRoomSubjectChanged(const QString&)));
|
||||
connect(room, SIGNAL(participantAdded(const QString&)), this, SLOT(onRoomParticipantAdded(const QString&)));
|
||||
connect(room, SIGNAL(participantChanged(const QString&)), this, SLOT(onRoomParticipantChanged(const QString&)));
|
||||
connect(room, SIGNAL(participantRemoved(const QString&)), this, SLOT(onRoomParticipantRemoved(const QString&)));
|
||||
connect(room, SIGNAL(nickNameChanged(const QString&)), this, SLOT(onRoomNickNameChanged(const QString&)));
|
||||
connect(room, SIGNAL(error(const QXmppStanza::Error&)), this, SLOT(onRoomError(const QXmppStanza::Error&)));
|
||||
connect(room, &QXmppMucRoom::joined, this, &Conference::onRoomJoined);
|
||||
connect(room, &QXmppMucRoom::left, this, &Conference::onRoomLeft);
|
||||
connect(room, &QXmppMucRoom::nameChanged, this, &Conference::onRoomNameChanged);
|
||||
connect(room, &QXmppMucRoom::subjectChanged, this, &Conference::onRoomSubjectChanged);
|
||||
connect(room, &QXmppMucRoom::participantAdded, this, &Conference::onRoomParticipantAdded);
|
||||
connect(room, &QXmppMucRoom::participantChanged, this, &Conference::onRoomParticipantChanged);
|
||||
connect(room, &QXmppMucRoom::participantRemoved, this, &Conference::onRoomParticipantRemoved);
|
||||
connect(room, &QXmppMucRoom::nickNameChanged, this, &Conference::onRoomNickNameChanged);
|
||||
connect(room, &QXmppMucRoom::error, this, &Conference::onRoomError);
|
||||
|
||||
room->setNickName(nick);
|
||||
if (autoJoin) {
|
||||
|
|
|
@ -331,9 +331,9 @@ void Core::NetworkAccess::startDownload(const QString& messageId, const QString&
|
|||
Transfer* dwn = new Transfer({{messageId}, 0, 0, true, "", 0});
|
||||
QNetworkRequest req(url);
|
||||
dwn->reply = manager->get(req);
|
||||
connect(dwn->reply, SIGNAL(downloadProgress(qint64, qint64)), SLOT(onDownloadProgress(qint64, qint64)));
|
||||
connect(dwn->reply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(onDownloadError(QNetworkReply::NetworkError)));
|
||||
connect(dwn->reply, SIGNAL(finished()), SLOT(onDownloadFinished()));
|
||||
connect(dwn->reply, &QNetworkReply::downloadProgress, this, &NetworkAccess::onDownloadProgress);
|
||||
connect(dwn->reply, qOverload<QNetworkReply::NetworkError>(&QNetworkReply::error), this, &NetworkAccess::onDownloadError);
|
||||
connect(dwn->reply, &QNetworkReply::finished, this, &NetworkAccess::onDownloadFinished);
|
||||
downloads.insert(std::make_pair(url, dwn));
|
||||
emit downloadFileProgress(messageId, 0);
|
||||
}
|
||||
|
|
|
@ -20,9 +20,10 @@
|
|||
|
||||
#include <QDebug>
|
||||
|
||||
Core::RosterItem::RosterItem(const QString& pJid, const QString& account, QObject* parent):
|
||||
Core::RosterItem::RosterItem(const QString& pJid, const QString& pAccount, QObject* parent):
|
||||
QObject(parent),
|
||||
jid(pJid),
|
||||
account(pAccount),
|
||||
name(),
|
||||
archiveState(empty),
|
||||
archive(new Archive(jid)),
|
||||
|
@ -331,3 +332,63 @@ bool Core::RosterItem::isMuc() const
|
|||
{
|
||||
return muc;
|
||||
}
|
||||
|
||||
QString Core::RosterItem::avatarHash() const
|
||||
{
|
||||
return archive->getAvatarHash();
|
||||
}
|
||||
|
||||
bool Core::RosterItem::isAvatarAutoGenerated() const
|
||||
{
|
||||
return archive->getAutoAvatar();
|
||||
}
|
||||
|
||||
QString Core::RosterItem::avatarPath() const
|
||||
{
|
||||
QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation));
|
||||
path += "/" + account + "/" + jid + "/avatar." + archive->getAvatarType();
|
||||
return path;
|
||||
}
|
||||
|
||||
bool Core::RosterItem::hasAvatar() const
|
||||
{
|
||||
return archive->getHasAvatar();
|
||||
}
|
||||
|
||||
void Core::RosterItem::setAvatar(const QByteArray& data)
|
||||
{
|
||||
if (archive->setAvatar(data, false)) {
|
||||
if (archive->getHasAvatar()) {
|
||||
emit avatarChanged(Shared::Avatar::empty, "");
|
||||
} else {
|
||||
emit avatarChanged(Shared::Avatar::valid, avatarPath());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Core::RosterItem::setAutoGeneratedAvatar()
|
||||
{
|
||||
QImage image(96, 96, QImage::Format_ARGB32_Premultiplied);
|
||||
QPainter painter(&image);
|
||||
quint8 colorIndex = rand() % Shared::colorPalette.size();
|
||||
const QColor& bg = Shared::colorPalette[colorIndex];
|
||||
painter.fillRect(image.rect(), bg);
|
||||
QFont f;
|
||||
f.setBold(true);
|
||||
f.setPixelSize(72);
|
||||
painter.setFont(f);
|
||||
if (bg.lightnessF() > 0.5) {
|
||||
painter.setPen(Qt::black);
|
||||
} else {
|
||||
painter.setPen(Qt::white);
|
||||
}
|
||||
painter.drawText(image.rect(), Qt::AlignCenter | Qt::AlignVCenter, jid.at(0).toUpper());
|
||||
QByteArray arr;
|
||||
QBuffer stream(&arr);
|
||||
stream.open(QBuffer::WriteOnly);
|
||||
image.save(&stream, "PNG");
|
||||
stream.close();
|
||||
if (archive->setAvatar(arr, true)) {
|
||||
emit avatarChanged(Shared::Avatar::autocreated, avatarPath());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,10 @@
|
|||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QStandardPaths>
|
||||
#include <QImage>
|
||||
#include <QPainter>
|
||||
#include <QBuffer>
|
||||
|
||||
#include <list>
|
||||
|
||||
|
@ -58,15 +62,23 @@ public:
|
|||
void flushMessagesToArchive(bool finished, const QString& firstId, const QString& lastId);
|
||||
void requestHistory(int count, const QString& before);
|
||||
void requestFromEmpty(int count, const QString& before);
|
||||
bool hasAvatar() const;
|
||||
bool isAvatarAutoGenerated() const;
|
||||
QString avatarHash() const;
|
||||
QString avatarPath() const;
|
||||
void setAvatar(const QByteArray& data);
|
||||
void setAutoGeneratedAvatar();
|
||||
|
||||
signals:
|
||||
void nameChanged(const QString& name);
|
||||
void subscriptionStateChanged(Shared::SubscriptionState state);
|
||||
void historyResponse(const std::list<Shared::Message>& messages);
|
||||
void needHistory(const QString& before, const QString& after, const QDateTime& afterTime = QDateTime());
|
||||
void avatarChanged(Shared::Avatar, const QString& path);
|
||||
|
||||
public:
|
||||
const QString jid;
|
||||
const QString account;
|
||||
|
||||
protected:
|
||||
QString name;
|
||||
|
|
125
core/squawk.cpp
125
core/squawk.cpp
|
@ -28,11 +28,11 @@ Core::Squawk::Squawk(QObject* parent):
|
|||
amap(),
|
||||
network()
|
||||
{
|
||||
connect(&network, SIGNAL(fileLocalPathResponse(const QString&, const QString&)), this, SIGNAL(fileLocalPathResponse(const QString&, const QString&)));
|
||||
connect(&network, SIGNAL(downloadFileProgress(const QString&, qreal)), this, SIGNAL(downloadFileProgress(const QString&, qreal)));
|
||||
connect(&network, SIGNAL(downloadFileError(const QString&, const QString&)), this, SIGNAL(downloadFileError(const QString&, const QString&)));
|
||||
connect(&network, SIGNAL(uploadFileProgress(const QString&, qreal)), this, SIGNAL(uploadFileProgress(const QString&, qreal)));
|
||||
connect(&network, SIGNAL(uploadFileError(const QString&, const QString&)), this, SIGNAL(uploadFileError(const QString&, const QString&)));
|
||||
connect(&network, &NetworkAccess::fileLocalPathResponse, this, &Squawk::fileLocalPathResponse);
|
||||
connect(&network, &NetworkAccess::downloadFileProgress, this, &Squawk::downloadFileProgress);
|
||||
connect(&network, &NetworkAccess::downloadFileError, this, &Squawk::downloadFileError);
|
||||
connect(&network, &NetworkAccess::uploadFileProgress, this, &Squawk::uploadFileProgress);
|
||||
connect(&network, &NetworkAccess::uploadFileError, this, &Squawk::uploadFileError);
|
||||
}
|
||||
|
||||
Core::Squawk::~Squawk()
|
||||
|
@ -103,42 +103,39 @@ void Core::Squawk::newAccountRequest(const QMap<QString, QVariant>& map)
|
|||
|
||||
void Core::Squawk::addAccount(const QString& login, const QString& server, const QString& password, const QString& name, const QString& resource)
|
||||
{
|
||||
QSettings settings;
|
||||
unsigned int reconnects = settings.value("reconnects", 2).toUInt();
|
||||
|
||||
Account* acc = new Account(login, server, password, name);
|
||||
acc->setResource(resource);
|
||||
acc->setReconnectTimes(reconnects);
|
||||
accounts.push_back(acc);
|
||||
amap.insert(std::make_pair(name, acc));
|
||||
|
||||
connect(acc, SIGNAL(connectionStateChanged(int)), this, SLOT(onAccountConnectionStateChanged(int)));
|
||||
connect(acc, SIGNAL(error(const QString&)), this, SLOT(onAccountError(const QString&)));
|
||||
connect(acc, SIGNAL(availabilityChanged(int)), this, SLOT(onAccountAvailabilityChanged(int)));
|
||||
connect(acc, SIGNAL(addContact(const QString&, const QString&, const QMap<QString, QVariant>&)),
|
||||
this, SLOT(onAccountAddContact(const QString&, const QString&, const QMap<QString, QVariant>&)));
|
||||
connect(acc, SIGNAL(addGroup(const QString&)), this, SLOT(onAccountAddGroup(const QString&)));
|
||||
connect(acc, SIGNAL(removeGroup(const QString&)), this, SLOT(onAccountRemoveGroup(const QString&)));
|
||||
connect(acc, SIGNAL(removeContact(const QString&)), this, SLOT(onAccountRemoveContact(const QString&)));
|
||||
connect(acc, SIGNAL(removeContact(const QString&, const QString&)), this, SLOT(onAccountRemoveContact(const QString&, const QString&)));
|
||||
connect(acc, SIGNAL(changeContact(const QString&, const QMap<QString, QVariant>&)),
|
||||
this, SLOT(onAccountChangeContact(const QString&, const QMap<QString, QVariant>&)));
|
||||
connect(acc, SIGNAL(addPresence(const QString&, const QString&, const QMap<QString, QVariant>&)),
|
||||
this, SLOT(onAccountAddPresence(const QString&, const QString&, const QMap<QString, QVariant>&)));
|
||||
connect(acc, SIGNAL(removePresence(const QString&, const QString&)), this, SLOT(onAccountRemovePresence(const QString&, const QString&)));
|
||||
connect(acc, SIGNAL(message(const Shared::Message&)), this, SLOT(onAccountMessage(const Shared::Message&)));
|
||||
connect(acc, SIGNAL(responseArchive(const QString&, const std::list<Shared::Message>&)),
|
||||
this, SLOT(onAccountResponseArchive(const QString&, const std::list<Shared::Message>&)));
|
||||
connect(acc, &Account::connectionStateChanged, this, &Squawk::onAccountConnectionStateChanged);
|
||||
connect(acc, &Account::changed, this, &Squawk::onAccountChanged);
|
||||
connect(acc, &Account::error, this, &Squawk::onAccountError);
|
||||
connect(acc, &Account::availabilityChanged, this, &Squawk::onAccountAvailabilityChanged);
|
||||
connect(acc, &Account::addContact, this, &Squawk::onAccountAddContact);
|
||||
connect(acc, &Account::addGroup, this, &Squawk::onAccountAddGroup);
|
||||
connect(acc, &Account::removeGroup, this, &Squawk::onAccountRemoveGroup);
|
||||
connect(acc, qOverload<const QString&, const QString&>(&Account::removeContact), this, qOverload<const QString&, const QString&>(&Squawk::onAccountRemoveContact));
|
||||
connect(acc, qOverload<const QString&>(&Account::removeContact), this, qOverload<const QString&>(&Squawk::onAccountRemoveContact));
|
||||
connect(acc, &Account::changeContact, this, &Squawk::onAccountChangeContact);
|
||||
connect(acc, &Account::addPresence, this, &Squawk::onAccountAddPresence);
|
||||
connect(acc, &Account::removePresence, this, &Squawk::onAccountRemovePresence);
|
||||
connect(acc, &Account::message, this, &Squawk::onAccountMessage);
|
||||
connect(acc, &Account::responseArchive, this, &Squawk::onAccountResponseArchive);
|
||||
|
||||
connect(acc, &Account::addRoom, this, &Squawk::onAccountAddRoom);
|
||||
connect(acc, &Account::changeRoom, this, &Squawk::onAccountChangeRoom);
|
||||
connect(acc, &Account::removeRoom, this, &Squawk::onAccountRemoveRoom);
|
||||
|
||||
connect(acc, SIGNAL(addRoom(const QString&, const QMap<QString, QVariant>&)),
|
||||
this, SLOT(onAccountAddRoom(const QString&, const QMap<QString, QVariant>&)));
|
||||
connect(acc, SIGNAL(changeRoom(const QString&, const QMap<QString, QVariant>&)),
|
||||
this, SLOT(onAccountChangeRoom(const QString&, const QMap<QString, QVariant>&)));
|
||||
connect(acc, SIGNAL(removeRoom(const QString&)), this, SLOT(onAccountRemoveRoom(const QString&)));
|
||||
|
||||
connect(acc, SIGNAL(addRoomParticipant(const QString&, const QString&, const QMap<QString, QVariant>&)),
|
||||
this, SLOT(onAccountAddRoomPresence(const QString&, const QString&, const QMap<QString, QVariant>&)));
|
||||
connect(acc, SIGNAL(changeRoomParticipant(const QString&, const QString&, const QMap<QString, QVariant>&)),
|
||||
this, SLOT(onAccountChangeRoomPresence(const QString&, const QString&, const QMap<QString, QVariant>&)));
|
||||
connect(acc, SIGNAL(removeRoomParticipant(const QString&, const QString&)),
|
||||
this, SLOT(onAccountRemoveRoomPresence(const QString&, const QString&)));
|
||||
connect(acc, &Account::addRoomParticipant, this, &Squawk::onAccountAddRoomPresence);
|
||||
connect(acc, &Account::changeRoomParticipant, this, &Squawk::onAccountChangeRoomPresence);
|
||||
connect(acc, &Account::removeRoomParticipant, this, &Squawk::onAccountRemoveRoomPresence);
|
||||
|
||||
connect(acc, &Account::receivedVCard, this, &Squawk::responseVCard);
|
||||
|
||||
QMap<QString, QVariant> map = {
|
||||
{"login", login},
|
||||
|
@ -148,8 +145,10 @@ void Core::Squawk::addAccount(const QString& login, const QString& server, const
|
|||
{"resource", resource},
|
||||
{"state", Shared::disconnected},
|
||||
{"offline", Shared::offline},
|
||||
{"error", ""}
|
||||
{"error", ""},
|
||||
{"avatarPath", acc->getAvatarPath()}
|
||||
};
|
||||
|
||||
emit newAccount(map);
|
||||
}
|
||||
|
||||
|
@ -261,6 +260,12 @@ void Core::Squawk::onAccountAvailabilityChanged(int state)
|
|||
emit changeAccount(acc->getName(), {{"availability", state}});
|
||||
}
|
||||
|
||||
void Core::Squawk::onAccountChanged(const QMap<QString, QVariant>& data)
|
||||
{
|
||||
Account* acc = static_cast<Account*>(sender());
|
||||
emit changeAccount(acc->getName(), data);
|
||||
}
|
||||
|
||||
void Core::Squawk::onAccountMessage(const Shared::Message& data)
|
||||
{
|
||||
Account* acc = static_cast<Account*>(sender());
|
||||
|
@ -504,3 +509,53 @@ void Core::Squawk::downloadFileRequest(const QString& messageId, const QString&
|
|||
{
|
||||
network.downladFileRequest(messageId, url);
|
||||
}
|
||||
|
||||
void Core::Squawk::addContactToGroupRequest(const QString& account, const QString& jid, const QString& groupName)
|
||||
{
|
||||
AccountsMap::const_iterator itr = amap.find(account);
|
||||
if (itr == amap.end()) {
|
||||
qDebug() << "An attempt to add contact" << jid << "of non existing account" << account << "to the group" << groupName << ", skipping";
|
||||
return;
|
||||
}
|
||||
itr->second->addContactToGroupRequest(jid, groupName);
|
||||
}
|
||||
|
||||
void Core::Squawk::removeContactFromGroupRequest(const QString& account, const QString& jid, const QString& groupName)
|
||||
{
|
||||
AccountsMap::const_iterator itr = amap.find(account);
|
||||
if (itr == amap.end()) {
|
||||
qDebug() << "An attempt to add contact" << jid << "of non existing account" << account << "to the group" << groupName << ", skipping";
|
||||
return;
|
||||
}
|
||||
itr->second->removeContactFromGroupRequest(jid, groupName);
|
||||
}
|
||||
|
||||
void Core::Squawk::renameContactRequest(const QString& account, const QString& jid, const QString& newName)
|
||||
{
|
||||
AccountsMap::const_iterator itr = amap.find(account);
|
||||
if (itr == amap.end()) {
|
||||
qDebug() << "An attempt to rename contact" << jid << "of non existing account" << account << ", skipping";
|
||||
return;
|
||||
}
|
||||
itr->second->renameContactRequest(jid, newName);
|
||||
}
|
||||
|
||||
void Core::Squawk::requestVCard(const QString& account, const QString& jid)
|
||||
{
|
||||
AccountsMap::const_iterator itr = amap.find(account);
|
||||
if (itr == amap.end()) {
|
||||
qDebug() << "An attempt to request" << jid << "vcard of non existing account" << account << ", skipping";
|
||||
return;
|
||||
}
|
||||
itr->second->requestVCard(jid);
|
||||
}
|
||||
|
||||
void Core::Squawk::uploadVCard(const QString& account, const Shared::VCard& card)
|
||||
{
|
||||
AccountsMap::const_iterator itr = amap.find(account);
|
||||
if (itr == amap.end()) {
|
||||
qDebug() << "An attempt to upload vcard to non existing account" << account << ", skipping";
|
||||
return;
|
||||
}
|
||||
itr->second->uploadVCard(card);
|
||||
}
|
||||
|
|
|
@ -23,7 +23,8 @@
|
|||
#include <QString>
|
||||
#include <QVariant>
|
||||
#include <QMap>
|
||||
#include <deque>
|
||||
#include <QtGlobal>
|
||||
|
||||
#include <deque>
|
||||
|
||||
#include "account.h"
|
||||
|
@ -67,6 +68,7 @@ signals:
|
|||
void downloadFileProgress(const QString& messageId, qreal value);
|
||||
void uploadFileError(const QString& messageId, const QString& error);
|
||||
void uploadFileProgress(const QString& messageId, qreal value);
|
||||
void responseVCard(const QString& jid, const Shared::VCard& card);
|
||||
|
||||
public slots:
|
||||
void start();
|
||||
|
@ -82,7 +84,10 @@ public slots:
|
|||
void requestArchive(const QString& account, const QString& jid, int count, const QString& before);
|
||||
void subscribeContact(const QString& account, const QString& jid, const QString& reason);
|
||||
void unsubscribeContact(const QString& account, const QString& jid, const QString& reason);
|
||||
void addContactToGroupRequest(const QString& account, const QString& jid, const QString& groupName);
|
||||
void removeContactFromGroupRequest(const QString& account, const QString& jid, const QString& groupName);
|
||||
void removeContactRequest(const QString& account, const QString& jid);
|
||||
void renameContactRequest(const QString& account, const QString& jid, const QString& newName);
|
||||
void addContactRequest(const QString& account, const QString& jid, const QString& name, const QSet<QString>& groups);
|
||||
void setRoomJoined(const QString& account, const QString& jid, bool joined);
|
||||
void setRoomAutoJoin(const QString& account, const QString& jid, bool joined);
|
||||
|
@ -90,6 +95,8 @@ public slots:
|
|||
void removeRoomRequest(const QString& account, const QString& jid);
|
||||
void fileLocalPathRequest(const QString& messageId, const QString& url);
|
||||
void downloadFileRequest(const QString& messageId, const QString& url);
|
||||
void requestVCard(const QString& account, const QString& jid);
|
||||
void uploadVCard(const QString& account, const Shared::VCard& card);
|
||||
|
||||
private:
|
||||
typedef std::deque<Account*> Accounts;
|
||||
|
@ -106,6 +113,7 @@ private:
|
|||
private slots:
|
||||
void onAccountConnectionStateChanged(int state);
|
||||
void onAccountAvailabilityChanged(int state);
|
||||
void onAccountChanged(const QMap<QString, QVariant>& data);
|
||||
void onAccountAddGroup(const QString& name);
|
||||
void onAccountError(const QString& text);
|
||||
void onAccountRemoveGroup(const QString& name);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue