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