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