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