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