Quassel IRC  Pre-Release
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros
ircchannel.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * Copyright (C) 2005-2015 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 
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 
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 than 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);
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 {
152  SYNC(ARG(password))
153 }
154 
155 void IrcChannel::setEncrypted(bool encrypted)
156 {
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 joins are propagated 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 
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 joins 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 
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 datastructures
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 {
480 
481  switch (modeType) {
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 {
510 
511  switch (modeType) {
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 {
538 
539  switch (modeType) {
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 {
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 {
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 
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 }