Introduce basic (not-yet-compressing) implementation of Compressor
[quassel.git] / src / common / ircchannel.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-2014 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 "ircchannel.h"
22
23 #include "network.h"
24 #include "ircuser.h"
25 #include "util.h"
26
27 #include <QMapIterator>
28 #include <QHashIterator>
29 #include <QTextCodec>
30
31 #include <QDebug>
32
33 INIT_SYNCABLE_OBJECT(IrcChannel)
34 IrcChannel::IrcChannel(const QString &channelname, Network *network)
35     : SyncableObject(network),
36     _initialized(false),
37     _name(channelname),
38     _topic(QString()),
39     _encrypted(false),
40     _network(network),
41     _codecForEncoding(0),
42     _codecForDecoding(0)
43 {
44     setObjectName(QString::number(network->networkId().toInt()) + "/" +  channelname);
45 }
46
47
48 IrcChannel::~IrcChannel()
49 {
50 }
51
52
53 // ====================
54 //  PUBLIC:
55 // ====================
56 bool IrcChannel::isKnownUser(IrcUser *ircuser) const
57 {
58     if (ircuser == 0) {
59         qWarning() << "Channel" << name() << "received IrcUser Nullpointer!";
60         return false;
61     }
62
63     if (!_userModes.contains(ircuser)) {
64         qWarning() << "Channel" << name() << "received data for unknown User" << ircuser->nick();
65         return false;
66     }
67
68     return true;
69 }
70
71
72 bool IrcChannel::isValidChannelUserMode(const QString &mode) const
73 {
74     bool isvalid = true;
75     if (mode.size() > 1) {
76         qWarning() << "Channel" << name() << "received Channel User Mode which is longer then 1 Char:" << mode;
77         isvalid = false;
78     }
79     return isvalid;
80 }
81
82
83 QString IrcChannel::userModes(IrcUser *ircuser) const
84 {
85     if (_userModes.contains(ircuser))
86         return _userModes[ircuser];
87     else
88         return QString();
89 }
90
91
92 QString IrcChannel::userModes(const QString &nick) const
93 {
94     return userModes(network()->ircUser(nick));
95 }
96
97
98 void IrcChannel::setCodecForEncoding(const QString &name)
99 {
100     setCodecForEncoding(QTextCodec::codecForName(name.toAscii()));
101 }
102
103
104 void IrcChannel::setCodecForEncoding(QTextCodec *codec)
105 {
106     _codecForEncoding = codec;
107 }
108
109
110 void IrcChannel::setCodecForDecoding(const QString &name)
111 {
112     setCodecForDecoding(QTextCodec::codecForName(name.toAscii()));
113 }
114
115
116 void IrcChannel::setCodecForDecoding(QTextCodec *codec)
117 {
118     _codecForDecoding = codec;
119 }
120
121
122 QString IrcChannel::decodeString(const QByteArray &text) const
123 {
124     if (!codecForDecoding()) return network()->decodeString(text);
125     return ::decodeString(text, _codecForDecoding);
126 }
127
128
129 QByteArray IrcChannel::encodeString(const QString &string) const
130 {
131     if (codecForEncoding()) {
132         return _codecForEncoding->fromUnicode(string);
133     }
134     return network()->encodeString(string);
135 }
136
137
138 // ====================
139 //  PUBLIC SLOTS:
140 // ====================
141 void IrcChannel::setTopic(const QString &topic)
142 {
143     _topic = topic;
144     SYNC(ARG(topic))
145     emit topicSet(topic);
146 }
147
148
149 void IrcChannel::setPassword(const QString &password)
150 {
151     _password = password;
152     SYNC(ARG(password))
153 }
154
155 void IrcChannel::setEncrypted(bool encrypted)
156 {
157     _encrypted = encrypted;
158     SYNC(ARG(encrypted))
159     emit encryptedSet(encrypted);
160 }
161
162
163 void IrcChannel::joinIrcUsers(const QList<IrcUser *> &users, const QStringList &modes)
164 {
165     if (users.isEmpty())
166         return;
167
168     if (users.count() != modes.count()) {
169         qWarning() << "IrcChannel::addUsers(): number of nicks does not match number of modes!";
170         return;
171     }
172
173     QStringList newNicks;
174     QStringList newModes;
175     QList<IrcUser *> newUsers;
176
177     IrcUser *ircuser;
178     for (int i = 0; i < users.count(); i++) {
179         ircuser = users[i];
180         if (!ircuser || _userModes.contains(ircuser)) {
181             addUserMode(ircuser, modes[i]);
182             continue;
183         }
184
185         _userModes[ircuser] = modes[i];
186         ircuser->joinChannel(this);
187         connect(ircuser, SIGNAL(nickSet(QString)), this, SLOT(ircUserNickSet(QString)));
188
189         // connect(ircuser, SIGNAL(destroyed()), this, SLOT(ircUserDestroyed()));
190         // if you wonder why there is no counterpart to ircUserJoined:
191         // the joines are propagted by the ircuser. the signal ircUserJoined is only for convenience
192
193         newNicks << ircuser->nick();
194         newModes << modes[i];
195         newUsers << ircuser;
196     }
197
198     if (newNicks.isEmpty())
199         return;
200
201     SYNC_OTHER(joinIrcUsers, ARG(newNicks), ARG(newModes));
202     emit ircUsersJoined(newUsers);
203 }
204
205
206 void IrcChannel::joinIrcUsers(const QStringList &nicks, const QStringList &modes)
207 {
208     QList<IrcUser *> users;
209     foreach(QString nick, nicks)
210     users << network()->newIrcUser(nick);
211     joinIrcUsers(users, modes);
212 }
213
214
215 void IrcChannel::joinIrcUser(IrcUser *ircuser)
216 {
217     QList<IrcUser *> users;
218     users << ircuser;
219     QStringList modes;
220     modes << QString();
221     joinIrcUsers(users, modes);
222 }
223
224
225 void IrcChannel::part(IrcUser *ircuser)
226 {
227     if (isKnownUser(ircuser)) {
228         _userModes.remove(ircuser);
229         ircuser->partChannel(this);
230         // if you wonder why there is no counterpart to ircUserParted:
231         // the joines are propagted by the ircuser. the signal ircUserParted is only for convenience
232         disconnect(ircuser, 0, this, 0);
233         emit ircUserParted(ircuser);
234
235         if (network()->isMe(ircuser) || _userModes.isEmpty()) {
236             // in either case we're no longer in the channel
237             //  -> clean up the channel and destroy it
238             QList<IrcUser *> users = _userModes.keys();
239             _userModes.clear();
240             foreach(IrcUser *user, users) {
241                 disconnect(user, 0, this, 0);
242                 user->partChannel(this);
243             }
244             emit parted();
245             network()->removeIrcChannel(this);
246         }
247     }
248 }
249
250
251 void IrcChannel::part(const QString &nick)
252 {
253     part(network()->ircUser(nick));
254 }
255
256
257 // SET USER MODE
258 void IrcChannel::setUserModes(IrcUser *ircuser, const QString &modes)
259 {
260     if (isKnownUser(ircuser)) {
261         _userModes[ircuser] = modes;
262         QString nick = ircuser->nick();
263         SYNC_OTHER(setUserModes, ARG(nick), ARG(modes))
264         emit ircUserModesSet(ircuser, modes);
265     }
266 }
267
268
269 void IrcChannel::setUserModes(const QString &nick, const QString &modes)
270 {
271     setUserModes(network()->ircUser(nick), modes);
272 }
273
274
275 // ADD USER MODE
276 void IrcChannel::addUserMode(IrcUser *ircuser, const QString &mode)
277 {
278     if (!isKnownUser(ircuser) || !isValidChannelUserMode(mode))
279         return;
280
281     if (!_userModes[ircuser].contains(mode)) {
282         _userModes[ircuser] += mode;
283         QString nick = ircuser->nick();
284         SYNC_OTHER(addUserMode, ARG(nick), ARG(mode))
285         emit ircUserModeAdded(ircuser, mode);
286     }
287 }
288
289
290 void IrcChannel::addUserMode(const QString &nick, const QString &mode)
291 {
292     addUserMode(network()->ircUser(nick), mode);
293 }
294
295
296 // REMOVE USER MODE
297 void IrcChannel::removeUserMode(IrcUser *ircuser, const QString &mode)
298 {
299     if (!isKnownUser(ircuser) || !isValidChannelUserMode(mode))
300         return;
301
302     if (_userModes[ircuser].contains(mode)) {
303         _userModes[ircuser].remove(mode);
304         QString nick = ircuser->nick();
305         SYNC_OTHER(removeUserMode, ARG(nick), ARG(mode));
306         emit ircUserModeRemoved(ircuser, mode);
307     }
308 }
309
310
311 void IrcChannel::removeUserMode(const QString &nick, const QString &mode)
312 {
313     removeUserMode(network()->ircUser(nick), mode);
314 }
315
316
317 // INIT SET USER MODES
318 QVariantMap IrcChannel::initUserModes() const
319 {
320     QVariantMap usermodes;
321     QHash<IrcUser *, QString>::const_iterator iter = _userModes.constBegin();
322     while (iter != _userModes.constEnd()) {
323         usermodes[iter.key()->nick()] = iter.value();
324         iter++;
325     }
326     return usermodes;
327 }
328
329
330 void IrcChannel::initSetUserModes(const QVariantMap &usermodes)
331 {
332     QList<IrcUser *> users;
333     QStringList modes;
334     QVariantMap::const_iterator iter = usermodes.constBegin();
335     while (iter != usermodes.constEnd()) {
336         users << network()->newIrcUser(iter.key());
337         modes << iter.value().toString();
338         iter++;
339     }
340     joinIrcUsers(users, modes);
341 }
342
343
344 QVariantMap IrcChannel::initChanModes() const
345 {
346     QVariantMap channelModes;
347
348     QVariantMap A_modes;
349     QHash<QChar, QStringList>::const_iterator A_iter = _A_channelModes.constBegin();
350     while (A_iter != _A_channelModes.constEnd()) {
351         A_modes[A_iter.key()] = A_iter.value();
352         A_iter++;
353     }
354     channelModes["A"] = A_modes;
355
356     QVariantMap B_modes;
357     QHash<QChar, QString>::const_iterator B_iter = _B_channelModes.constBegin();
358     while (B_iter != _B_channelModes.constEnd()) {
359         B_modes[B_iter.key()] = B_iter.value();
360         B_iter++;
361     }
362     channelModes["B"] = B_modes;
363
364     QVariantMap C_modes;
365     QHash<QChar, QString>::const_iterator C_iter = _C_channelModes.constBegin();
366     while (C_iter != _C_channelModes.constEnd()) {
367         C_modes[C_iter.key()] = C_iter.value();
368         C_iter++;
369     }
370     channelModes["C"] = C_modes;
371
372     QString D_modes;
373     QSet<QChar>::const_iterator D_iter = _D_channelModes.constBegin();
374     while (D_iter != _D_channelModes.constEnd()) {
375         D_modes += *D_iter;
376         D_iter++;
377     }
378     channelModes["D"] = D_modes;
379
380     return channelModes;
381 }
382
383
384 void IrcChannel::initSetChanModes(const QVariantMap &channelModes)
385 {
386     QVariantMap::const_iterator iter = channelModes["A"].toMap().constBegin();
387     QVariantMap::const_iterator iterEnd = channelModes["A"].toMap().constEnd();
388     while (iter != iterEnd) {
389         _A_channelModes[iter.key()[0]] = iter.value().toStringList();
390         iter++;
391     }
392
393     iter = channelModes["B"].toMap().constBegin();
394     iterEnd = channelModes["B"].toMap().constEnd();
395     while (iter != iterEnd) {
396         _B_channelModes[iter.key()[0]] = iter.value().toString();
397         iter++;
398     }
399
400     iter = channelModes["C"].toMap().constBegin();
401     iterEnd = channelModes["C"].toMap().constEnd();
402     while (iter != iterEnd) {
403         _C_channelModes[iter.key()[0]] = iter.value().toString();
404         iter++;
405     }
406
407     QString D_modes = channelModes["D"].toString();
408     for (int i = 0; i < D_modes.count(); i++) {
409         _D_channelModes << D_modes[i];
410     }
411 }
412
413
414 void IrcChannel::ircUserDestroyed()
415 {
416     IrcUser *ircUser = static_cast<IrcUser *>(sender());
417     Q_ASSERT(ircUser);
418     _userModes.remove(ircUser);
419     // no further propagation.
420     // this leads only to fuck ups.
421 }
422
423
424 void IrcChannel::ircUserNickSet(QString nick)
425 {
426     IrcUser *ircUser = qobject_cast<IrcUser *>(sender());
427     Q_ASSERT(ircUser);
428     emit ircUserNickSet(ircUser, nick);
429 }
430
431
432 /*******************************************************************************
433  *
434  * 3.3 CHANMODES
435  *
436  *    o  CHANMODES=A,B,C,D
437  *
438  *    The CHANMODES token specifies the modes that may be set on a channel.
439  *    These modes are split into four categories, as follows:
440  *
441  *    o  Type A: Modes that add or remove an address to or from a list.
442  *       These modes always take a parameter when sent by the server to a
443  *       client; when sent by a client, they may be specified without a
444  *       parameter, which requests the server to display the current
445  *       contents of the corresponding list on the channel to the client.
446  *    o  Type B: Modes that change a setting on the channel.  These modes
447  *       always take a parameter.
448  *    o  Type C: Modes that change a setting on the channel. These modes
449  *       take a parameter only when set; the parameter is absent when the
450  *       mode is removed both in the client's and server's MODE command.
451  *    o  Type D: Modes that change a setting on the channel. These modes
452  *       never take a parameter.
453  *
454  *    If the server sends any additional types after these 4, the client
455  *    MUST ignore them; this is intended to allow future extension of this
456  *    token.
457  *
458  *    The IRC server MUST NOT list modes in CHANMODES which are also
459  *    present in the PREFIX parameter; however, for completeness, modes
460  *    described in PREFIX may be treated as type B modes.
461  *
462  ******************************************************************************/
463
464 /*******************************************************************************
465  * Short Version:
466  * A --> add/remove from List
467  * B --> set value or remove
468  * C --> set value or remove
469  * D --> on/off
470  *
471  * B and C behave very similar... we store the data in different datastructes
472  * for future compatibility
473  ******************************************************************************/
474
475 // NOTE: the behavior of addChannelMode and removeChannelMode depends on the type of mode
476 // see list above for chanmode types
477 void IrcChannel::addChannelMode(const QChar &mode, const QString &value)
478 {
479     Network::ChannelModeType modeType = network()->channelModeType(mode);
480
481     switch (modeType) {
482     case Network::NOT_A_CHANMODE:
483         return;
484     case Network::A_CHANMODE:
485         if (!_A_channelModes.contains(mode))
486             _A_channelModes[mode] = QStringList(value);
487         else if (!_A_channelModes[mode].contains(value))
488             _A_channelModes[mode] << value;
489         break;
490
491     case Network::B_CHANMODE:
492         _B_channelModes[mode] = value;
493         break;
494
495     case Network::C_CHANMODE:
496         _C_channelModes[mode] = value;
497         break;
498
499     case Network::D_CHANMODE:
500         _D_channelModes << mode;
501         break;
502     }
503     SYNC(ARG(mode), ARG(value))
504 }
505
506
507 void IrcChannel::removeChannelMode(const QChar &mode, const QString &value)
508 {
509     Network::ChannelModeType modeType = network()->channelModeType(mode);
510
511     switch (modeType) {
512     case Network::NOT_A_CHANMODE:
513         return;
514     case Network::A_CHANMODE:
515         if (_A_channelModes.contains(mode))
516             _A_channelModes[mode].removeAll(value);
517         break;
518
519     case Network::B_CHANMODE:
520         _B_channelModes.remove(mode);
521         break;
522
523     case Network::C_CHANMODE:
524         _C_channelModes.remove(mode);
525         break;
526
527     case Network::D_CHANMODE:
528         _D_channelModes.remove(mode);
529         break;
530     }
531     SYNC(ARG(mode), ARG(value))
532 }
533
534
535 bool IrcChannel::hasMode(const QChar &mode) const
536 {
537     Network::ChannelModeType modeType = network()->channelModeType(mode);
538
539     switch (modeType) {
540     case Network::NOT_A_CHANMODE:
541         return false;
542     case Network::A_CHANMODE:
543         return _A_channelModes.contains(mode);
544     case Network::B_CHANMODE:
545         return _B_channelModes.contains(mode);
546     case Network::C_CHANMODE:
547         return _C_channelModes.contains(mode);
548     case Network::D_CHANMODE:
549         return _D_channelModes.contains(mode);
550     default:
551         return false;
552     }
553 }
554
555
556 QString IrcChannel::modeValue(const QChar &mode) const
557 {
558     Network::ChannelModeType modeType = network()->channelModeType(mode);
559
560     switch (modeType) {
561     case Network::B_CHANMODE:
562         if (_B_channelModes.contains(mode))
563             return _B_channelModes[mode];
564         else
565             return QString();
566     case Network::C_CHANMODE:
567         if (_C_channelModes.contains(mode))
568             return _C_channelModes[mode];
569         else
570             return QString();
571     default:
572         return QString();
573     }
574 }
575
576
577 QStringList IrcChannel::modeValueList(const QChar &mode) const
578 {
579     Network::ChannelModeType modeType = network()->channelModeType(mode);
580
581     switch (modeType) {
582     case Network::A_CHANMODE:
583         if (_A_channelModes.contains(mode))
584             return _A_channelModes[mode];
585     default:
586         return QStringList();
587     }
588 }
589
590
591 QString IrcChannel::channelModeString() const
592 {
593     QStringList params;
594     QString modeString;
595
596     QSet<QChar>::const_iterator D_iter = _D_channelModes.constBegin();
597     while (D_iter != _D_channelModes.constEnd()) {
598         modeString += *D_iter;
599         D_iter++;
600     }
601
602     QHash<QChar, QString>::const_iterator BC_iter = _C_channelModes.constBegin();
603     while (BC_iter != _C_channelModes.constEnd()) {
604         modeString += BC_iter.key();
605         params << BC_iter.value();
606         BC_iter++;
607     }
608
609     BC_iter = _B_channelModes.constBegin();
610     while (BC_iter != _B_channelModes.constEnd()) {
611         modeString += BC_iter.key();
612         params << BC_iter.value();
613         BC_iter++;
614     }
615     if (modeString.isEmpty())
616         return modeString;
617     else
618         return QString("+%1 %2").arg(modeString).arg(params.join(" "));
619 }