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 | |
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 | _encrypted(false), |
40 | _network(network), |
41 | _codecForEncoding(0), |
42 | _codecForDecoding(0) |
43 | { |
44 | setObjectName(QString::number(network->networkId().toInt()) + "/" + channelname); |
45 | } |
46 | |
47 | |
48 | IrcChannel::~IrcChannel() |
49 | { |
50 | } |
51 | |
52 | |
53 | // ==================== |
54 | // PUBLIC: |
55 | // ==================== |
56 | bool 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 | |
72 | bool 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 | |
83 | QString IrcChannel::userModes(IrcUser *ircuser) const |
84 | { |
85 | if (_userModes.contains(ircuser)) |
86 | return _userModes[ircuser]; |
87 | else |
88 | return QString(); |
89 | } |
90 | |
91 | |
92 | QString IrcChannel::userModes(const QString &nick) const |
93 | { |
94 | return userModes(network()->ircUser(nick)); |
95 | } |
96 | |
97 | |
98 | void IrcChannel::setCodecForEncoding(const QString &name) |
99 | { |
100 | setCodecForEncoding(QTextCodec::codecForName(name.toLatin1())); |
101 | } |
102 | |
103 | |
104 | void IrcChannel::setCodecForEncoding(QTextCodec *codec) |
105 | { |
106 | _codecForEncoding = codec; |
107 | } |
108 | |
109 | |
110 | void IrcChannel::setCodecForDecoding(const QString &name) |
111 | { |
112 | setCodecForDecoding(QTextCodec::codecForName(name.toLatin1())); |
113 | } |
114 | |
115 | |
116 | void IrcChannel::setCodecForDecoding(QTextCodec *codec) |
117 | { |
118 | _codecForDecoding = codec; |
119 | } |
120 | |
121 | |
122 | QString IrcChannel::decodeString(const QByteArray &text) const |
123 | { |
124 | if (!codecForDecoding()) return network()->decodeString(text); |
125 | return ::decodeString(text, _codecForDecoding); |
126 | } |
127 | |
128 | |
129 | QByteArray 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 | // ==================== |
141 | void IrcChannel::setTopic(const QString &topic) |
142 | { |
143 | _topic = topic; |
144 | SYNC(ARG(topic)) |
145 | emit topicSet(topic); |
146 | } |
147 | |
148 | |
149 | void IrcChannel::setPassword(const QString &password) |
150 | { |
151 | _password = password; |
152 | SYNC(ARG(password)) |
153 | } |
154 | |
155 | void IrcChannel::setEncrypted(bool encrypted) |
156 | { |
157 | _encrypted = encrypted; |
158 | SYNC(ARG(encrypted)) |
159 | emit encryptedSet(encrypted); |
160 | } |
161 | |
162 | |
163 | void 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 | |
206 | void 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 | |
215 | void 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 | |
225 | void 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 | |
251 | void IrcChannel::part(const QString &nick) |
252 | { |
253 | part(network()->ircUser(nick)); |
254 | } |
255 | |
256 | |
257 | // SET USER MODE |
258 | void 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 | |
269 | void IrcChannel::setUserModes(const QString &nick, const QString &modes) |
270 | { |
271 | setUserModes(network()->ircUser(nick), modes); |
272 | } |
273 | |
274 | |
275 | // ADD USER MODE |
276 | void 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 | |
290 | void IrcChannel::addUserMode(const QString &nick, const QString &mode) |
291 | { |
292 | addUserMode(network()->ircUser(nick), mode); |
293 | } |
294 | |
295 | |
296 | // REMOVE USER MODE |
297 | void 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 | |
311 | void IrcChannel::removeUserMode(const QString &nick, const QString &mode) |
312 | { |
313 | removeUserMode(network()->ircUser(nick), mode); |
314 | } |
315 | |
316 | |
317 | // INIT SET USER MODES |
318 | QVariantMap 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 | |
330 | void 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 | |
344 | QVariantMap 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 | |
384 | void 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 | |
414 | void 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 | |
424 | void 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 |
477 | void 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 | |
507 | void 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 | |
535 | bool 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 | |
556 | QString 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 | |
577 | QStringList 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 | |
591 | QString 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 | |