1 /***************************************************************************
2  *   Copyright (C) 2005-2020 by the Quassel Project                        *
3  *   devel@quassel-irc.org                                                 *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) version 3.                                           *
9  *                                                                         *
10  *   This program is distributed in the hope that it will be useful,       *
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13  *   GNU General Public License for more details.                          *
14  *                                                                         *
15  *   You should have received a copy of the GNU General Public License     *
16  *   along with this program; if not, write to the                         *
17  *   Free Software Foundation, Inc.,                                       *
18  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
19  ***************************************************************************/
20 
21 #include "corebuffersyncer.h"
22 
23 #include <algorithm>
24 #include <iterator>
25 #include <set>
26 
27 #include "core.h"
28 #include "corenetwork.h"
29 #include "coresession.h"
30 #include "ircchannel.h"
31 #include "util.h"
32 
33 class PurgeEvent : public QEvent
34 {
35 public:
PurgeEvent()36     PurgeEvent()
37         : QEvent(QEvent::User)
38     {}
39 };
40 
CoreBufferSyncer(CoreSession * parent)41 CoreBufferSyncer::CoreBufferSyncer(CoreSession* parent)
42     : BufferSyncer(Core::bufferLastMsgIds(parent->user()),
43                    Core::bufferLastSeenMsgIds(parent->user()),
44                    Core::bufferMarkerLineMsgIds(parent->user()),
45                    Core::bufferActivities(parent->user()),
46                    Core::highlightCounts(parent->user()),
47                    parent)
48     , _coreSession(parent)
49     , _purgeBuffers(false)
50 {
51     connect(parent, &CoreSession::displayMsg, this, &CoreBufferSyncer::addBufferActivity);
52     connect(parent, &CoreSession::displayMsg, this, &CoreBufferSyncer::addCoreHighlight);
53 }
54 
requestSetLastSeenMsg(BufferId buffer,const MsgId & msgId)55 void CoreBufferSyncer::requestSetLastSeenMsg(BufferId buffer, const MsgId& msgId)
56 {
57     if (setLastSeenMsg(buffer, msgId)) {
58         int activity = Core::bufferActivity(buffer, msgId);
59         int highlightCount = Core::highlightCount(buffer, msgId);
60 
61         setBufferActivity(buffer, activity);
62         setHighlightCount(buffer, highlightCount);
63 
64         dirtyLastSeenBuffers << buffer;
65     }
66 }
67 
requestSetMarkerLine(BufferId buffer,const MsgId & msgId)68 void CoreBufferSyncer::requestSetMarkerLine(BufferId buffer, const MsgId& msgId)
69 {
70     if (setMarkerLine(buffer, msgId))
71         dirtyMarkerLineBuffers << buffer;
72 }
73 
storeDirtyIds()74 void CoreBufferSyncer::storeDirtyIds()
75 {
76     UserId userId = _coreSession->user();
77     MsgId msgId;
78     foreach (BufferId bufferId, dirtyLastSeenBuffers) {
79         msgId = lastSeenMsg(bufferId);
80         if (msgId.isValid())
81             Core::setBufferLastSeenMsg(userId, bufferId, msgId);
82     }
83 
84     foreach (BufferId bufferId, dirtyMarkerLineBuffers) {
85         msgId = markerLine(bufferId);
86         if (msgId.isValid())
87             Core::setBufferMarkerLineMsg(userId, bufferId, msgId);
88     }
89 
90     foreach (BufferId bufferId, dirtyActivities) {
91         Core::setBufferActivity(userId, bufferId, activity(bufferId));
92     }
93 
94     foreach (BufferId bufferId, dirtyHighlights) {
95         Core::setHighlightCount(userId, bufferId, highlightCount(bufferId));
96     }
97 
98     dirtyLastSeenBuffers.clear();
99     dirtyMarkerLineBuffers.clear();
100     dirtyActivities.clear();
101     dirtyHighlights.clear();
102 }
103 
removeBuffer(BufferId bufferId)104 void CoreBufferSyncer::removeBuffer(BufferId bufferId)
105 {
106     BufferInfo bufferInfo = Core::getBufferInfo(_coreSession->user(), bufferId);
107     if (!bufferInfo.isValid()) {
108         qWarning() << "CoreBufferSyncer::removeBuffer(): invalid BufferId:" << bufferId << "for User:" << _coreSession->user();
109         return;
110     }
111 
112     if (bufferInfo.type() == BufferInfo::StatusBuffer) {
113         qWarning() << "CoreBufferSyncer::removeBuffer(): Status Buffers cannot be removed!";
114         return;
115     }
116 
117     if (bufferInfo.type() == BufferInfo::ChannelBuffer) {
118         CoreNetwork* net = _coreSession->network(bufferInfo.networkId());
119         if (!net) {
120             qWarning() << "CoreBufferSyncer::removeBuffer(): Received BufferInfo with unknown networkId!";
121             return;
122         }
123         IrcChannel* chan = net->ircChannel(bufferInfo.bufferName());
124         if (chan) {
125             qWarning() << "CoreBufferSyncer::removeBuffer(): Unable to remove Buffer for joined Channel:" << bufferInfo.bufferName();
126             return;
127         }
128     }
129     if (Core::removeBuffer(_coreSession->user(), bufferId))
130         BufferSyncer::removeBuffer(bufferId);
131 }
132 
renameBuffer(BufferId bufferId,QString newName)133 void CoreBufferSyncer::renameBuffer(BufferId bufferId, QString newName)
134 {
135     BufferInfo bufferInfo = Core::getBufferInfo(_coreSession->user(), bufferId);
136     if (!bufferInfo.isValid()) {
137         qWarning() << "CoreBufferSyncer::renameBuffer(): invalid BufferId:" << bufferId << "for User:" << _coreSession->user();
138         return;
139     }
140 
141     if (bufferInfo.type() != BufferInfo::QueryBuffer) {
142         qWarning() << "CoreBufferSyncer::renameBuffer(): only QueryBuffers can be renamed" << bufferId;
143         return;
144     }
145 
146     if (Core::renameBuffer(_coreSession->user(), bufferId, newName))
147         BufferSyncer::renameBuffer(bufferId, newName);
148 }
149 
mergeBuffersPermanently(BufferId bufferId1,BufferId bufferId2)150 void CoreBufferSyncer::mergeBuffersPermanently(BufferId bufferId1, BufferId bufferId2)
151 {
152     BufferInfo bufferInfo1 = Core::getBufferInfo(_coreSession->user(), bufferId1);
153     BufferInfo bufferInfo2 = Core::getBufferInfo(_coreSession->user(), bufferId2);
154     if (!bufferInfo1.isValid() || !bufferInfo2.isValid()) {
155         qWarning() << "CoreBufferSyncer::mergeBuffersPermanently(): invalid BufferIds:" << bufferId1 << bufferId2
156                    << "for User:" << _coreSession->user();
157         return;
158     }
159 
160     if ((bufferInfo1.type() != BufferInfo::QueryBuffer && bufferInfo1.type() != BufferInfo::ChannelBuffer)
161         || (bufferInfo2.type() != BufferInfo::QueryBuffer && bufferInfo2.type() != BufferInfo::ChannelBuffer)) {
162         qWarning() << "CoreBufferSyncer::mergeBuffersPermanently(): only QueryBuffers and/or ChannelBuffers can be merged!" << bufferId1
163                    << bufferId2;
164         return;
165     }
166 
167     if (Core::mergeBuffersPermanently(_coreSession->user(), bufferId1, bufferId2)) {
168         BufferSyncer::mergeBuffersPermanently(bufferId1, bufferId2);
169     }
170 }
171 
customEvent(QEvent * event)172 void CoreBufferSyncer::customEvent(QEvent* event)
173 {
174     if (event->type() != QEvent::User)
175         return;
176 
177     purgeBufferIds();
178     event->accept();
179 }
180 
requestPurgeBufferIds()181 void CoreBufferSyncer::requestPurgeBufferIds()
182 {
183     if (_purgeBuffers)
184         return;
185 
186     _purgeBuffers = true;
187     QCoreApplication::postEvent(this, new PurgeEvent());
188 }
189 
purgeBufferIds()190 void CoreBufferSyncer::purgeBufferIds()
191 {
192     _purgeBuffers = false;
193     auto bufferInfos = Core::requestBuffers(_coreSession->user());
194     std::set<BufferId> actualBuffers;
195     std::transform(bufferInfos.cbegin(), bufferInfos.cend(), std::inserter(actualBuffers, actualBuffers.end()),
196                    [](auto&& bufferInfo) { return bufferInfo.bufferId(); });
197 
198     QSet<BufferId> storedIds = toQSet(lastSeenBufferIds()) + toQSet(markerLineBufferIds());
199     foreach (BufferId bufferId, storedIds) {
200         if (actualBuffers.find(bufferId) == actualBuffers.end()) {
201             BufferSyncer::removeBuffer(bufferId);
202         }
203     }
204 }
205 
setBufferActivity(BufferId buffer,int activity)206 void CoreBufferSyncer::setBufferActivity(BufferId buffer, int activity)
207 {
208     BufferSyncer::setBufferActivity(buffer, activity);
209     dirtyActivities << buffer;
210 }
211 
setHighlightCount(BufferId buffer,int highlightCount)212 void CoreBufferSyncer::setHighlightCount(BufferId buffer, int highlightCount)
213 {
214     BufferSyncer::setHighlightCount(buffer, highlightCount);
215     dirtyHighlights << buffer;
216 }
217