164
165
void HistoryDaemon::onRolesChanged(const HandleRolesMap &added, const HandleRolesMap &removed)
169
167
ChannelInterfaceRolesInterface *roles_interface = qobject_cast<ChannelInterfaceRolesInterface*>(sender());
170
RolesMap roles = roles_interface->getRoles();
172
168
Tp::TextChannelPtr channel(qobject_cast<Tp::TextChannel*>(sender()->parent()));
170
if (!mRolesMap.contains(channel->objectPath())) {
171
rolesMap = roles_interface->getRoles();
173
rolesMap = mRolesMap[channel->objectPath()];
176
QMapIterator<uint, uint> it(removed);
177
while (it.hasNext()) {
179
rolesMap.remove(it.key());
182
QMapIterator<uint, uint> it2(added);
183
while (it2.hasNext()) {
185
rolesMap[it2.key()] = it2.value();
188
mRolesMap[channel->objectPath()] = rolesMap;
173
190
QVariantMap properties = propertiesFromChannel(channel);
174
191
QVariantMap thread = threadForProperties(channel->property(History::FieldAccountId).toString(),
175
192
History::EventTypeText,
177
194
matchFlagsForChannel(channel),
180
writeRolesInformationEvents(thread, channel, roles);
182
updateRoomRoles(channel, roles);
196
if (!thread.isEmpty()) {
197
writeRolesInformationEvents(thread, channel, rolesMap);
198
updateRoomRoles(channel, rolesMap);
185
202
QVariantMap HistoryDaemon::propertiesFromChannel(const Tp::ChannelPtr &textChannel)
187
204
QVariantMap properties;
188
205
QVariantList participants;
189
206
QStringList participantIds;
191
ChannelInterfaceRolesInterface *roles_interface = textChannel->optionalInterface<ChannelInterfaceRolesInterface>();
193
if (roles_interface) {
194
roles = roles_interface->getRoles();
197
Q_FOREACH(const Tp::ContactPtr contact, textChannel->groupContacts(false)) {
198
QVariantMap contactProperties;
199
contactProperties[History::FieldAlias] = contact->alias();
200
contactProperties[History::FieldAccountId] = textChannel->property(History::FieldAccountId).toString();
201
contactProperties[History::FieldIdentifier] = contact->id();
202
contactProperties[History::FieldParticipantState] = History::ParticipantStateRegular;
203
contactProperties[History::FieldParticipantRoles] = roles[contact->handle().at(0)];
204
participantIds << contact->id();
205
participants << contactProperties;
208
Q_FOREACH(const Tp::ContactPtr contact, textChannel->groupRemotePendingContacts(false)) {
209
QVariantMap contactProperties;
210
contactProperties[History::FieldAlias] = contact->alias();
211
contactProperties[History::FieldAccountId] = textChannel->property(History::FieldAccountId).toString();
212
contactProperties[History::FieldIdentifier] = contact->id();
213
contactProperties[History::FieldParticipantState] = History::ParticipantStateRemotePending;
214
contactProperties[History::FieldParticipantRoles] = roles[contact->handle().at(0)];
215
participantIds << contact->id();
216
participants << contactProperties;
219
Q_FOREACH(const Tp::ContactPtr contact, textChannel->groupLocalPendingContacts(false)) {
220
QVariantMap contactProperties;
221
contactProperties[History::FieldAlias] = contact->alias();
222
contactProperties[History::FieldAccountId] = textChannel->property(History::FieldAccountId).toString();
223
contactProperties[History::FieldIdentifier] = contact->id();
224
contactProperties[History::FieldParticipantState] = History::ParticipantStateLocalPending;
225
contactProperties[History::FieldParticipantRoles] = roles[contact->handle().at(0)];
226
participantIds << contact->id();
227
participants << contactProperties;
230
if (participants.isEmpty() && textChannel->targetHandleType() == Tp::HandleTypeContact &&
231
textChannel->targetContact() == textChannel->connection()->selfContact()) {
232
QVariantMap contactProperties;
233
contactProperties[History::FieldAlias] = textChannel->targetContact()->alias();
234
contactProperties[History::FieldAccountId] = textChannel->property(History::FieldAccountId).toString();
235
contactProperties[History::FieldIdentifier] = textChannel->targetContact()->id();
236
contactProperties[History::FieldParticipantState] = History::ParticipantStateRegular;
237
participantIds << textChannel->targetContact()->id();
238
participants << contactProperties;
207
QString accountId = textChannel->property(History::FieldAccountId).toString();
209
if (History::Utils::shouldIncludeParticipants(accountId, fromTelepathyHandleType(textChannel->targetHandleType()))) {
210
ChannelInterfaceRolesInterface *roles_interface = textChannel->optionalInterface<ChannelInterfaceRolesInterface>();
212
if (roles_interface) {
213
if (mRolesMap.contains(textChannel->objectPath())) {
214
roles = mRolesMap[textChannel->objectPath()];
218
Q_FOREACH(const Tp::ContactPtr contact, textChannel->groupContacts(false)) {
219
QVariantMap contactProperties;
220
contactProperties[History::FieldAlias] = contact->alias();
221
contactProperties[History::FieldAccountId] = accountId;
222
contactProperties[History::FieldIdentifier] = contact->id();
223
contactProperties[History::FieldParticipantState] = History::ParticipantStateRegular;
224
contactProperties[History::FieldParticipantRoles] = roles[contact->handle().at(0)];
225
participantIds << contact->id();
226
participants << contactProperties;
229
Q_FOREACH(const Tp::ContactPtr contact, textChannel->groupRemotePendingContacts(false)) {
230
QVariantMap contactProperties;
231
contactProperties[History::FieldAlias] = contact->alias();
232
contactProperties[History::FieldAccountId] = accountId;
233
contactProperties[History::FieldIdentifier] = contact->id();
234
contactProperties[History::FieldParticipantState] = History::ParticipantStateRemotePending;
235
contactProperties[History::FieldParticipantRoles] = roles[contact->handle().at(0)];
236
participantIds << contact->id();
237
participants << contactProperties;
240
Q_FOREACH(const Tp::ContactPtr contact, textChannel->groupLocalPendingContacts(false)) {
241
QVariantMap contactProperties;
242
contactProperties[History::FieldAlias] = contact->alias();
243
contactProperties[History::FieldAccountId] = accountId;
244
contactProperties[History::FieldIdentifier] = contact->id();
245
contactProperties[History::FieldParticipantState] = History::ParticipantStateLocalPending;
246
contactProperties[History::FieldParticipantRoles] = roles[contact->handle().at(0)];
247
participantIds << contact->id();
248
participants << contactProperties;
251
if (participants.isEmpty() && textChannel->targetHandleType() == Tp::HandleTypeContact &&
252
textChannel->targetContact() == textChannel->connection()->selfContact()) {
253
QVariantMap contactProperties;
254
contactProperties[History::FieldAlias] = textChannel->targetContact()->alias();
255
contactProperties[History::FieldAccountId] = accountId;
256
contactProperties[History::FieldIdentifier] = textChannel->targetContact()->id();
257
contactProperties[History::FieldParticipantState] = History::ParticipantStateRegular;
258
participantIds << textChannel->targetContact()->id();
259
participants << contactProperties;
241
263
// We map chatType directly from telepathy targetHandleType: None, Contact, Room
339
QString HistoryDaemon::threadIdForProperties(const QString &accountId, History::EventType type, const QVariantMap &properties, History::MatchFlags matchFlags, bool create)
342
return QString::null;
345
QString threadId = mBackend->threadIdForProperties(accountId,
349
if (threadId.isEmpty() && create) {
350
QVariantMap thread = mBackend->createThreadForProperties(accountId, type, properties);
351
if (!thread.isEmpty()) {
352
if (properties.contains("Requested") && properties[History::FieldChatType].toInt() == History::ChatTypeRoom) {
353
QVariantMap map = thread[History::FieldChatRoomInfo].toMap();
354
map["Requested"] = properties["Requested"];
355
thread[History::FieldChatRoomInfo] = map;
357
mDBus.notifyThreadsAdded(QList<QVariantMap>() << thread);
358
threadId = thread[History::FieldThreadId].toString();
364
QList<QVariantMap> HistoryDaemon::participantsForThreads(const QList<QVariantMap> &threadIds)
367
return QList<QVariantMap>();
369
return mBackend->participantsForThreads(threadIds);
317
372
QString HistoryDaemon::queryThreads(int type, const QVariantMap &sort, const QVariantMap &filter, const QVariantMap &properties)
572
void HistoryDaemon::markThreadsAsRead(const QList<QVariantMap> &threads)
578
QList<QVariantMap> modifiedThreads;
580
Q_FOREACH(const QVariantMap &thread, threads) {
581
mBackend->beginBatchOperation();
582
QVariantMap newThread = mBackend->markThreadAsRead(thread);
583
if (!newThread.isEmpty()) {
584
modifiedThreads << newThread;
587
mBackend->endBatchOperation();
590
if (!modifiedThreads.isEmpty()) {
591
mDBus.notifyThreadsModified(modifiedThreads);
515
595
bool HistoryDaemon::removeThreads(const QList<QVariantMap> &threads)
521
// In order to remove a thread all we have to do is to remove all its items
522
// then it is going to be removed by removeEvents() once it detects the thread is
524
QList<QVariantMap> events;
525
QMap<QString, QVariantMap> removedEmptyThreads;
601
// If the thread has events
602
mBackend->beginBatchOperation();
526
603
Q_FOREACH(const QVariantMap &thread, threads) {
527
QList<QVariantMap> thisEvents = mBackend->eventsForThread(thread);
528
if (thisEvents.isEmpty()) {
529
mBackend->beginBatchOperation();
530
if (!mBackend->removeThread(thread)) {
531
mBackend->rollbackBatchOperation();
534
mBackend->endBatchOperation();
535
QString hash = hashThread(thread);
536
removedEmptyThreads[hash] = thread;
539
events += thisEvents;
542
if (!removedEmptyThreads.isEmpty()) {
543
mDBus.notifyThreadsRemoved(removedEmptyThreads.values());
546
if (events.size() > 0) {
547
if(removeEvents(events)) {
604
if (!mBackend->removeThread(thread)) {
605
mBackend->rollbackBatchOperation();
609
mBackend->endBatchOperation();
610
mDBus.notifyThreadsRemoved(threads);
555
614
void HistoryDaemon::onObserverCreated()
615
673
writeEvents(QList<QVariantMap>() << event, properties);
676
void HistoryDaemon::onTextChannelInvalidated(const Tp::TextChannelPtr channel)
678
mRolesMap.remove(channel->objectPath());
679
QString accountId = channel->property(History::FieldAccountId).toString();
680
QVariantMap properties = propertiesFromChannel(channel);
682
// first try to fetch the existing thread to see if there is any.
683
QVariantMap thread = threadForProperties(accountId,
684
History::EventTypeText,
686
matchFlagsForChannel(channel),
689
QVariantMap roomInfo = thread[History::FieldChatRoomInfo].toMap();
690
if ((roomInfo.contains("Persistent") && !roomInfo["Persistent"].toBool()) && History::TelepathyHelper::instance()->accountForId(accountId)->protocolName() != "ofono") {
691
writeInformationEvent(thread, History::InformationTypeSelfLeaving);
693
updateRoomProperties(channel, QVariantMap{{"Joined", false}});
696
channel->disconnect(this);
618
699
void HistoryDaemon::onTextChannelAvailable(const Tp::TextChannelPtr channel)
620
701
// for Rooms we need to explicitly create the thread to allow users to send messages to groups even
621
702
// before they receive any message.
622
703
// for other types, we can wait until messages are received
623
705
if (channel->targetHandleType() == Tp::HandleTypeRoom) {
624
706
QString accountId = channel->property(History::FieldAccountId).toString();
625
707
QVariantMap properties = propertiesFromChannel(channel);
711
801
matchFlagsForChannel(channel),
713
if (!thread.isEmpty()) {
803
if (!thread.isEmpty() && !selfContactIsPending) {
804
QList<QVariantMap> added;
805
QList<QVariantMap> removed;
806
QList<QVariantMap> modified;
714
807
if (hasRemotePendingMembersAdded) {
715
808
Q_FOREACH (const Tp::ContactPtr& contact, groupRemotePendingMembersAdded) {
716
809
if (!foundInThread(contact, thread)) {
717
writeInformationEvent(thread, History::InformationTypeInvitationSent, contact->alias());
810
writeInformationEvent(thread, History::InformationTypeInvitationSent, contact->alias(), QString(), QString(), true);
811
QVariantMap participant;
812
participant[History::FieldIdentifier] = contact->id();
813
participant[History::FieldAlias] = contact->alias();
814
participant[History::FieldParticipantState] = History::ParticipantStateRemotePending;
815
added << participant;
721
820
if (hasMembersAdded) {
722
821
Q_FOREACH (const Tp::ContactPtr& contact, groupMembersAdded) {
723
822
// if this member was not previously regular member in thread, notify about his join
724
if (!foundAsMemberInThread(contact, thread)) {
725
writeInformationEvent(thread, History::InformationTypeJoined, contact->alias());
823
if (!foundAsMemberInThread(contact, thread) && contact->id() != channel->groupSelfContact()->id()) {
825
writeInformationEvent(thread, History::InformationTypeJoined, contact->alias(), QString(), QString(), true);
827
QVariantMap participant;
828
participant[History::FieldIdentifier] = contact->id();
829
participant[History::FieldAlias] = contact->alias();
830
participant[History::FieldParticipantState] = History::ParticipantStateRegular;
831
added << participant;
744
writeInformationEvent(thread, type);
746
updateRoomProperties(channel, QVariantMap{{"Joined", false}});
850
if (thread[History::FieldChatRoomInfo].toMap()["Joined"].toBool()) {
851
writeInformationEvent(thread, type);
853
updateRoomProperties(channel, QVariantMap{{"Joined", false}}, true);
748
856
else // don't notify any other group member removal if we are leaving the group
750
858
Q_FOREACH (const Tp::ContactPtr& contact, groupMembersRemoved) {
751
859
// inform about removed members other than us
752
860
if (contact->id() != channel->groupSelfContact()->id()) {
753
writeInformationEvent(thread, History::InformationTypeLeaving, contact->alias());
861
writeInformationEvent(thread, History::InformationTypeLeaving, contact->alias(), QString(), QString(), true);
863
QVariantMap participant;
864
participant[History::FieldIdentifier] = contact->id();
865
participant[History::FieldAlias] = contact->alias();
866
removed << participant;
870
mDBus.notifyThreadParticipantsChanged(thread, added, removed, QList<QVariantMap>());
761
updateRoomParticipants(channel);
874
updateRoomParticipants(channel, !selfContactIsPending);
764
void HistoryDaemon::updateRoomParticipants(const Tp::TextChannelPtr channel)
877
void HistoryDaemon::updateRoomParticipants(const Tp::TextChannelPtr channel, bool notify)
811
930
QString accountId = channel->property(History::FieldAccountId).toString();
812
931
QString threadId = channel->targetId();
813
932
if (mBackend->updateRoomParticipants(accountId, threadId, History::EventTypeText, participants)) {
814
QVariantMap updatedThread = getSingleThread(History::EventTypeText, accountId, threadId, QVariantMap());
815
mDBus.notifyThreadsModified(QList<QVariantMap>() << updatedThread);
934
QVariantMap updatedThread = getSingleThread(History::EventTypeText, accountId, threadId, QVariantMap());
935
mDBus.notifyThreadsModified(QList<QVariantMap>() << updatedThread);
819
void HistoryDaemon::updateRoomRoles(const Tp::TextChannelPtr &channel, const RolesMap &rolesMap)
940
void HistoryDaemon::updateRoomRoles(const Tp::TextChannelPtr &channel, const RolesMap &rolesMap, bool notify)
865
988
updateRoomProperties(accountId, threadId, type, properties, invalidated);
868
void HistoryDaemon::updateRoomProperties(const Tp::TextChannelPtr &channel, const QVariantMap &properties)
991
void HistoryDaemon::updateRoomProperties(const Tp::TextChannelPtr &channel, const QVariantMap &properties, bool notify)
870
993
QString accountId = channel->property(History::FieldAccountId).toString();
871
994
QString threadId = channel->targetId();
872
995
History::EventType type = History::EventTypeText;
873
updateRoomProperties(accountId, threadId, type, properties, QStringList());
996
updateRoomProperties(accountId, threadId, type, properties, QStringList(), notify);
876
void HistoryDaemon::updateRoomProperties(const QString &accountId, const QString &threadId, History::EventType type, const QVariantMap &properties, const QStringList &invalidated)
999
void HistoryDaemon::updateRoomProperties(const QString &accountId, const QString &threadId, History::EventType type, const QVariantMap &properties, const QStringList &invalidated, bool notify)
878
1001
if (mBackend->updateRoomInfo(accountId, threadId, type, properties, invalidated)) {
879
QVariantMap thread = getSingleThread(type, accountId, threadId, QVariantMap());
880
mDBus.notifyThreadsModified(QList<QVariantMap>() << thread);
1003
QVariantMap thread = getSingleThread(type, accountId, threadId, QVariantMap());
1004
mDBus.notifyThreadsModified(QList<QVariantMap>() << thread);
884
1009
void HistoryDaemon::onMessageReceived(const Tp::TextChannelPtr textChannel, const Tp::ReceivedMessage &message)
886
1011
QString eventId;
887
Tp::MessagePart header = message.header();
888
1012
QString senderId;
1014
QString accountId = textChannel->property(History::FieldAccountId).toString();
1015
QString threadId = textChannel->property(History::FieldThreadId).toString();
889
1016
QVariantMap properties = propertiesFromChannel(textChannel);
1018
if (threadId.isNull()) {
1019
threadId = threadIdForProperties(accountId,
1020
History::EventTypeText,
1022
matchFlagsForChannel(textChannel),
890
1026
History::MessageStatus status = History::MessageStatusUnknown;
891
1027
if (!message.sender() || message.sender()->handle().at(0) == textChannel->connection()->selfHandle()) {
892
1028
senderId = "self";
925
History::MessageStatus status;
926
switch (message.deliveryDetails().status()) {
927
case Tp::DeliveryStatusAccepted:
928
status = History::MessageStatusAccepted;
930
case Tp::DeliveryStatusDeleted:
931
status = History::MessageStatusDeleted;
933
case Tp::DeliveryStatusDelivered:
934
status = History::MessageStatusDelivered;
936
case Tp::DeliveryStatusPermanentlyFailed:
937
status = History::MessageStatusPermanentlyFailed;
939
case Tp::DeliveryStatusRead:
940
status = History::MessageStatusRead;
942
case Tp::DeliveryStatusTemporarilyFailed:
943
status = History::MessageStatusTemporarilyFailed;
945
case Tp::DeliveryStatusUnknown:
946
status = History::MessageStatusUnknown;
950
textEvent[History::FieldMessageStatus] = (int) status;
1061
textEvent[History::FieldMessageStatus] = (int) fromTelepathyDeliveryStatus(message.deliveryDetails().status());
951
1062
if (!writeEvents(QList<QVariantMap>() << textEvent, properties)) {
952
1063
qWarning() << "Failed to save the new message status!";
958
QVariantMap thread = threadForProperties(textChannel->property(History::FieldAccountId).toString(),
959
History::EventTypeText,
961
matchFlagsForChannel(textChannel),
964
1069
QList<QVariantMap> attachments;
965
1070
History::MessageType type = History::MessageTypeText;
966
1071
QString subject;
968
1073
if (message.hasNonTextContent()) {
969
QString normalizedAccountId = QString(QCryptographicHash::hash(thread[History::FieldAccountId].toString().toLatin1(), QCryptographicHash::Md5).toHex());
970
QString normalizedThreadId = QString(QCryptographicHash::hash(thread[History::FieldThreadId].toString().toLatin1(), QCryptographicHash::Md5).toHex());
1074
QString normalizedAccountId = QString(QCryptographicHash::hash(accountId.toLatin1(), QCryptographicHash::Md5).toHex());
1075
QString normalizedThreadId = QString(QCryptographicHash::hash(threadId.toLatin1(), QCryptographicHash::Md5).toHex());
971
1076
QString normalizedEventId = QString(QCryptographicHash::hash(eventId.toLatin1(), QCryptographicHash::Md5).toHex());
972
1077
QString mmsStoragePath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation);
1358
History::MessageStatus HistoryDaemon::fromTelepathyDeliveryStatus(Tp::DeliveryStatus deliveryStatus)
1360
History::MessageStatus status;
1361
switch (deliveryStatus) {
1362
case Tp::DeliveryStatusAccepted:
1363
status = History::MessageStatusAccepted;
1365
case Tp::DeliveryStatusDeleted:
1366
status = History::MessageStatusDeleted;
1368
case Tp::DeliveryStatusDelivered:
1369
status = History::MessageStatusDelivered;
1371
case Tp::DeliveryStatusPermanentlyFailed:
1372
status = History::MessageStatusPermanentlyFailed;
1374
case Tp::DeliveryStatusRead:
1375
status = History::MessageStatusRead;
1377
case Tp::DeliveryStatusTemporarilyFailed:
1378
status = History::MessageStatusTemporarilyFailed;
1380
case Tp::DeliveryStatusUnknown:
1381
status = History::MessageStatusUnknown;
1388
History::ChatType HistoryDaemon::fromTelepathyHandleType(const Tp::HandleType &type)
1390
History::ChatType chatType;
1392
case Tp::HandleTypeContact:
1393
chatType = History::ChatTypeContact;
1395
case Tp::HandleTypeNone:
1396
chatType = History::ChatTypeNone;
1398
case Tp::HandleTypeRoom:
1399
chatType = History::ChatTypeRoom;