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