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