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