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 <QTextCodec> |
22 | |
23 | #include "network.h" |
24 | |
25 | QTextCodec *Network::_defaultCodecForServer = 0; |
26 | QTextCodec *Network::_defaultCodecForEncoding = 0; |
27 | QTextCodec *Network::_defaultCodecForDecoding = 0; |
28 | |
29 | // ==================== |
30 | // Public: |
31 | // ==================== |
32 | INIT_SYNCABLE_OBJECT(Network) |
33 | Network::Network(const NetworkId &networkid, QObject *parent) |
34 | : SyncableObject(parent), |
35 | _proxy(0), |
36 | _networkId(networkid), |
37 | _identity(0), |
38 | _myNick(QString()), |
39 | _latency(0), |
40 | _networkName(QString("<not initialized>" )), |
41 | _currentServer(QString()), |
42 | _connected(false), |
43 | _connectionState(Disconnected), |
44 | _prefixes(QString()), |
45 | _prefixModes(QString()), |
46 | _useRandomServer(false), |
47 | _useAutoIdentify(false), |
48 | _useSasl(false), |
49 | _useAutoReconnect(false), |
50 | _autoReconnectInterval(60), |
51 | _autoReconnectRetries(10), |
52 | _unlimitedReconnectRetries(false), |
53 | _codecForServer(0), |
54 | _codecForEncoding(0), |
55 | _codecForDecoding(0), |
56 | _autoAwayActive(false) |
57 | { |
58 | setObjectName(QString::number(networkid.toInt())); |
59 | } |
60 | |
61 | |
62 | Network::~Network() |
63 | { |
64 | emit aboutToBeDestroyed(); |
65 | } |
66 | |
67 | |
68 | bool Network::isChannelName(const QString &channelname) const |
69 | { |
70 | if (channelname.isEmpty()) |
71 | return false; |
72 | |
73 | if (supports("CHANTYPES" )) |
74 | return support("CHANTYPES" ).contains(channelname[0]); |
75 | else |
76 | return QString("#&!+" ).contains(channelname[0]); |
77 | } |
78 | |
79 | |
80 | NetworkInfo Network::networkInfo() const |
81 | { |
82 | NetworkInfo info; |
83 | info.networkName = networkName(); |
84 | info.networkId = networkId(); |
85 | info.identity = identity(); |
86 | info.codecForServer = codecForServer(); |
87 | info.codecForEncoding = codecForEncoding(); |
88 | info.codecForDecoding = codecForDecoding(); |
89 | info.serverList = serverList(); |
90 | info.useRandomServer = useRandomServer(); |
91 | info.perform = perform(); |
92 | info.useAutoIdentify = useAutoIdentify(); |
93 | info.autoIdentifyService = autoIdentifyService(); |
94 | info.autoIdentifyPassword = autoIdentifyPassword(); |
95 | info.useSasl = useSasl(); |
96 | info.saslAccount = saslAccount(); |
97 | info.saslPassword = saslPassword(); |
98 | info.useAutoReconnect = useAutoReconnect(); |
99 | info.autoReconnectInterval = autoReconnectInterval(); |
100 | info.autoReconnectRetries = autoReconnectRetries(); |
101 | info.unlimitedReconnectRetries = unlimitedReconnectRetries(); |
102 | info.rejoinChannels = rejoinChannels(); |
103 | return info; |
104 | } |
105 | |
106 | |
107 | void Network::setNetworkInfo(const NetworkInfo &info) |
108 | { |
109 | // we don't set our ID! |
110 | if (!info.networkName.isEmpty() && info.networkName != networkName()) setNetworkName(info.networkName); |
111 | if (info.identity > 0 && info.identity != identity()) setIdentity(info.identity); |
112 | if (info.codecForServer != codecForServer()) setCodecForServer(QTextCodec::codecForName(info.codecForServer)); |
113 | if (info.codecForEncoding != codecForEncoding()) setCodecForEncoding(QTextCodec::codecForName(info.codecForEncoding)); |
114 | if (info.codecForDecoding != codecForDecoding()) setCodecForDecoding(QTextCodec::codecForName(info.codecForDecoding)); |
115 | if (info.serverList.count()) setServerList(toVariantList(info.serverList)); // FIXME compare components |
116 | if (info.useRandomServer != useRandomServer()) setUseRandomServer(info.useRandomServer); |
117 | if (info.perform != perform()) setPerform(info.perform); |
118 | if (info.useAutoIdentify != useAutoIdentify()) setUseAutoIdentify(info.useAutoIdentify); |
119 | if (info.autoIdentifyService != autoIdentifyService()) setAutoIdentifyService(info.autoIdentifyService); |
120 | if (info.autoIdentifyPassword != autoIdentifyPassword()) setAutoIdentifyPassword(info.autoIdentifyPassword); |
121 | if (info.useSasl != useSasl()) setUseSasl(info.useSasl); |
122 | if (info.saslAccount != saslAccount()) setSaslAccount(info.saslAccount); |
123 | if (info.saslPassword != saslPassword()) setSaslPassword(info.saslPassword); |
124 | if (info.useAutoReconnect != useAutoReconnect()) setUseAutoReconnect(info.useAutoReconnect); |
125 | if (info.autoReconnectInterval != autoReconnectInterval()) setAutoReconnectInterval(info.autoReconnectInterval); |
126 | if (info.autoReconnectRetries != autoReconnectRetries()) setAutoReconnectRetries(info.autoReconnectRetries); |
127 | if (info.unlimitedReconnectRetries != unlimitedReconnectRetries()) setUnlimitedReconnectRetries(info.unlimitedReconnectRetries); |
128 | if (info.rejoinChannels != rejoinChannels()) setRejoinChannels(info.rejoinChannels); |
129 | } |
130 | |
131 | |
132 | QString Network::prefixToMode(const QString &prefix) const |
133 | { |
134 | if (prefixes().contains(prefix)) |
135 | return QString(prefixModes()[prefixes().indexOf(prefix)]); |
136 | else |
137 | return QString(); |
138 | } |
139 | |
140 | |
141 | QString Network::modeToPrefix(const QString &mode) const |
142 | { |
143 | if (prefixModes().contains(mode)) |
144 | return QString(prefixes()[prefixModes().indexOf(mode)]); |
145 | else |
146 | return QString(); |
147 | } |
148 | |
149 | |
150 | QStringList Network::nicks() const |
151 | { |
152 | // we don't use _ircUsers.keys() since the keys may be |
153 | // not up to date after a nick change |
154 | QStringList nicks; |
155 | foreach(IrcUser *ircuser, _ircUsers.values()) { |
156 | nicks << ircuser->nick(); |
157 | } |
158 | return nicks; |
159 | } |
160 | |
161 | |
162 | QString Network::prefixes() const |
163 | { |
164 | if (_prefixes.isNull()) |
165 | determinePrefixes(); |
166 | |
167 | return _prefixes; |
168 | } |
169 | |
170 | |
171 | QString Network::prefixModes() const |
172 | { |
173 | if (_prefixModes.isNull()) |
174 | determinePrefixes(); |
175 | |
176 | return _prefixModes; |
177 | } |
178 | |
179 | |
180 | // example Unreal IRCD: CHANMODES=beI,kfL,lj,psmntirRcOAQKVCuzNSMTG |
181 | Network::ChannelModeType Network::channelModeType(const QString &mode) |
182 | { |
183 | if (mode.isEmpty()) |
184 | return NOT_A_CHANMODE; |
185 | |
186 | QString chanmodes = support("CHANMODES" ); |
187 | if (chanmodes.isEmpty()) |
188 | return NOT_A_CHANMODE; |
189 | |
190 | ChannelModeType modeType = A_CHANMODE; |
191 | for (int i = 0; i < chanmodes.count(); i++) { |
192 | if (chanmodes[i] == mode[0]) |
193 | break; |
194 | else if (chanmodes[i] == ',') |
195 | modeType = (ChannelModeType)(modeType << 1); |
196 | } |
197 | if (modeType > D_CHANMODE) { |
198 | qWarning() << "Network" << networkId() << "supplied invalid CHANMODES:" << chanmodes; |
199 | modeType = NOT_A_CHANMODE; |
200 | } |
201 | return modeType; |
202 | } |
203 | |
204 | |
205 | QString Network::support(const QString ¶m) const |
206 | { |
207 | QString support_ = param.toUpper(); |
208 | if (_supports.contains(support_)) |
209 | return _supports[support_]; |
210 | else |
211 | return QString(); |
212 | } |
213 | |
214 | |
215 | IrcUser *Network::newIrcUser(const QString &hostmask, const QVariantMap &initData) |
216 | { |
217 | QString nick(nickFromMask(hostmask).toLower()); |
218 | if (!_ircUsers.contains(nick)) { |
219 | IrcUser *ircuser = ircUserFactory(hostmask); |
220 | if (!initData.isEmpty()) { |
221 | ircuser->fromVariantMap(initData); |
222 | ircuser->setInitialized(); |
223 | } |
224 | |
225 | if (proxy()) |
226 | proxy()->synchronize(ircuser); |
227 | else |
228 | qWarning() << "unable to synchronize new IrcUser" << hostmask << "forgot to call Network::setProxy(SignalProxy *)?" ; |
229 | |
230 | connect(ircuser, SIGNAL(nickSet(QString)), this, SLOT(ircUserNickChanged(QString))); |
231 | |
232 | _ircUsers[nick] = ircuser; |
233 | |
234 | // This method will be called with a nick instead of hostmask by setInitIrcUsersAndChannels(). |
235 | // Not a problem because initData contains all we need; however, making sure here to get the real |
236 | // hostmask out of the IrcUser afterwards. |
237 | QString mask = ircuser->hostmask(); |
238 | SYNC_OTHER(addIrcUser, ARG(mask)); |
239 | // emit ircUserAdded(mask); |
240 | emit ircUserAdded(ircuser); |
241 | } |
242 | |
243 | return _ircUsers[nick]; |
244 | } |
245 | |
246 | |
247 | IrcUser *Network::ircUser(QString nickname) const |
248 | { |
249 | nickname = nickname.toLower(); |
250 | if (_ircUsers.contains(nickname)) |
251 | return _ircUsers[nickname]; |
252 | else |
253 | return 0; |
254 | } |
255 | |
256 | |
257 | void Network::removeIrcUser(IrcUser *ircuser) |
258 | { |
259 | QString nick = _ircUsers.key(ircuser); |
260 | if (nick.isNull()) |
261 | return; |
262 | |
263 | _ircUsers.remove(nick); |
264 | disconnect(ircuser, 0, this, 0); |
265 | ircuser->deleteLater(); |
266 | } |
267 | |
268 | |
269 | void Network::removeIrcChannel(IrcChannel *channel) |
270 | { |
271 | QString chanName = _ircChannels.key(channel); |
272 | if (chanName.isNull()) |
273 | return; |
274 | |
275 | _ircChannels.remove(chanName); |
276 | disconnect(channel, 0, this, 0); |
277 | channel->deleteLater(); |
278 | } |
279 | |
280 | |
281 | void Network::removeChansAndUsers() |
282 | { |
283 | QList<IrcUser *> users = ircUsers(); |
284 | _ircUsers.clear(); |
285 | QList<IrcChannel *> channels = ircChannels(); |
286 | _ircChannels.clear(); |
287 | |
288 | qDeleteAll(users); |
289 | qDeleteAll(channels); |
290 | } |
291 | |
292 | |
293 | IrcChannel *Network::newIrcChannel(const QString &channelname, const QVariantMap &initData) |
294 | { |
295 | if (!_ircChannels.contains(channelname.toLower())) { |
296 | IrcChannel *channel = ircChannelFactory(channelname); |
297 | if (!initData.isEmpty()) { |
298 | channel->fromVariantMap(initData); |
299 | channel->setInitialized(); |
300 | } |
301 | |
302 | if (proxy()) |
303 | proxy()->synchronize(channel); |
304 | else |
305 | qWarning() << "unable to synchronize new IrcChannel" << channelname << "forgot to call Network::setProxy(SignalProxy *)?" ; |
306 | |
307 | _ircChannels[channelname.toLower()] = channel; |
308 | |
309 | SYNC_OTHER(addIrcChannel, ARG(channelname)) |
310 | // emit ircChannelAdded(channelname); |
311 | emit ircChannelAdded(channel); |
312 | } |
313 | return _ircChannels[channelname.toLower()]; |
314 | } |
315 | |
316 | |
317 | IrcChannel *Network::ircChannel(QString channelname) const |
318 | { |
319 | channelname = channelname.toLower(); |
320 | if (_ircChannels.contains(channelname)) |
321 | return _ircChannels[channelname]; |
322 | else |
323 | return 0; |
324 | } |
325 | |
326 | |
327 | QByteArray Network::defaultCodecForServer() |
328 | { |
329 | if (_defaultCodecForServer) |
330 | return _defaultCodecForServer->name(); |
331 | return QByteArray(); |
332 | } |
333 | |
334 | |
335 | void Network::setDefaultCodecForServer(const QByteArray &name) |
336 | { |
337 | _defaultCodecForServer = QTextCodec::codecForName(name); |
338 | } |
339 | |
340 | |
341 | QByteArray Network::defaultCodecForEncoding() |
342 | { |
343 | if (_defaultCodecForEncoding) |
344 | return _defaultCodecForEncoding->name(); |
345 | return QByteArray(); |
346 | } |
347 | |
348 | |
349 | void Network::setDefaultCodecForEncoding(const QByteArray &name) |
350 | { |
351 | _defaultCodecForEncoding = QTextCodec::codecForName(name); |
352 | } |
353 | |
354 | |
355 | QByteArray Network::defaultCodecForDecoding() |
356 | { |
357 | if (_defaultCodecForDecoding) |
358 | return _defaultCodecForDecoding->name(); |
359 | return QByteArray(); |
360 | } |
361 | |
362 | |
363 | void Network::setDefaultCodecForDecoding(const QByteArray &name) |
364 | { |
365 | _defaultCodecForDecoding = QTextCodec::codecForName(name); |
366 | } |
367 | |
368 | |
369 | QByteArray Network::codecForServer() const |
370 | { |
371 | if (_codecForServer) |
372 | return _codecForServer->name(); |
373 | return QByteArray(); |
374 | } |
375 | |
376 | |
377 | void Network::setCodecForServer(const QByteArray &name) |
378 | { |
379 | setCodecForServer(QTextCodec::codecForName(name)); |
380 | } |
381 | |
382 | |
383 | void Network::setCodecForServer(QTextCodec *codec) |
384 | { |
385 | _codecForServer = codec; |
386 | QByteArray codecName = codecForServer(); |
387 | SYNC_OTHER(setCodecForServer, ARG(codecName)) |
388 | emit configChanged(); |
389 | } |
390 | |
391 | |
392 | QByteArray Network::codecForEncoding() const |
393 | { |
394 | if (_codecForEncoding) |
395 | return _codecForEncoding->name(); |
396 | return QByteArray(); |
397 | } |
398 | |
399 | |
400 | void Network::setCodecForEncoding(const QByteArray &name) |
401 | { |
402 | setCodecForEncoding(QTextCodec::codecForName(name)); |
403 | } |
404 | |
405 | |
406 | void Network::setCodecForEncoding(QTextCodec *codec) |
407 | { |
408 | _codecForEncoding = codec; |
409 | QByteArray codecName = codecForEncoding(); |
410 | SYNC_OTHER(setCodecForEncoding, ARG(codecName)) |
411 | emit configChanged(); |
412 | } |
413 | |
414 | |
415 | QByteArray Network::codecForDecoding() const |
416 | { |
417 | if (_codecForDecoding) |
418 | return _codecForDecoding->name(); |
419 | else return QByteArray(); |
420 | } |
421 | |
422 | |
423 | void Network::setCodecForDecoding(const QByteArray &name) |
424 | { |
425 | setCodecForDecoding(QTextCodec::codecForName(name)); |
426 | } |
427 | |
428 | |
429 | void Network::setCodecForDecoding(QTextCodec *codec) |
430 | { |
431 | _codecForDecoding = codec; |
432 | QByteArray codecName = codecForDecoding(); |
433 | SYNC_OTHER(setCodecForDecoding, ARG(codecName)) |
434 | emit configChanged(); |
435 | } |
436 | |
437 | |
438 | // FIXME use server encoding if appropriate |
439 | QString Network::decodeString(const QByteArray &text) const |
440 | { |
441 | if (_codecForDecoding) |
442 | return ::decodeString(text, _codecForDecoding); |
443 | else return ::decodeString(text, _defaultCodecForDecoding); |
444 | } |
445 | |
446 | |
447 | QByteArray Network::encodeString(const QString &string) const |
448 | { |
449 | if (_codecForEncoding) { |
450 | return _codecForEncoding->fromUnicode(string); |
451 | } |
452 | if (_defaultCodecForEncoding) { |
453 | return _defaultCodecForEncoding->fromUnicode(string); |
454 | } |
455 | return string.toLatin1(); |
456 | } |
457 | |
458 | |
459 | QString Network::decodeServerString(const QByteArray &text) const |
460 | { |
461 | if (_codecForServer) |
462 | return ::decodeString(text, _codecForServer); |
463 | else |
464 | return ::decodeString(text, _defaultCodecForServer); |
465 | } |
466 | |
467 | |
468 | QByteArray Network::encodeServerString(const QString &string) const |
469 | { |
470 | if (_codecForServer) { |
471 | return _codecForServer->fromUnicode(string); |
472 | } |
473 | if (_defaultCodecForServer) { |
474 | return _defaultCodecForServer->fromUnicode(string); |
475 | } |
476 | return string.toLatin1(); |
477 | } |
478 | |
479 | |
480 | // ==================== |
481 | // Public Slots: |
482 | // ==================== |
483 | void Network::setNetworkName(const QString &networkName) |
484 | { |
485 | _networkName = networkName; |
486 | SYNC(ARG(networkName)) |
487 | emit networkNameSet(networkName); |
488 | emit configChanged(); |
489 | } |
490 | |
491 | |
492 | void Network::setCurrentServer(const QString ¤tServer) |
493 | { |
494 | _currentServer = currentServer; |
495 | SYNC(ARG(currentServer)) |
496 | emit currentServerSet(currentServer); |
497 | } |
498 | |
499 | |
500 | void Network::setConnected(bool connected) |
501 | { |
502 | if (_connected == connected) |
503 | return; |
504 | |
505 | _connected = connected; |
506 | if (!connected) { |
507 | setMyNick(QString()); |
508 | setCurrentServer(QString()); |
509 | removeChansAndUsers(); |
510 | } |
511 | SYNC(ARG(connected)) |
512 | emit connectedSet(connected); |
513 | } |
514 | |
515 | |
516 | //void Network::setConnectionState(ConnectionState state) { |
517 | void Network::setConnectionState(int state) |
518 | { |
519 | _connectionState = (ConnectionState)state; |
520 | //qDebug() << "netstate" << networkId() << networkName() << state; |
521 | SYNC(ARG(state)) |
522 | emit connectionStateSet(_connectionState); |
523 | } |
524 | |
525 | |
526 | void Network::setMyNick(const QString &nickname) |
527 | { |
528 | _myNick = nickname; |
529 | if (!_myNick.isEmpty() && !ircUser(myNick())) { |
530 | newIrcUser(myNick()); |
531 | } |
532 | SYNC(ARG(nickname)) |
533 | emit myNickSet(nickname); |
534 | } |
535 | |
536 | |
537 | void Network::setLatency(int latency) |
538 | { |
539 | if (_latency == latency) |
540 | return; |
541 | _latency = latency; |
542 | SYNC(ARG(latency)) |
543 | } |
544 | |
545 | |
546 | void Network::setIdentity(IdentityId id) |
547 | { |
548 | _identity = id; |
549 | SYNC(ARG(id)) |
550 | emit identitySet(id); |
551 | emit configChanged(); |
552 | } |
553 | |
554 | |
555 | void Network::setServerList(const QVariantList &serverList) |
556 | { |
557 | _serverList = fromVariantList<Server>(serverList); |
558 | SYNC(ARG(serverList)) |
559 | emit configChanged(); |
560 | } |
561 | |
562 | |
563 | void Network::setUseRandomServer(bool use) |
564 | { |
565 | _useRandomServer = use; |
566 | SYNC(ARG(use)) |
567 | emit configChanged(); |
568 | } |
569 | |
570 | |
571 | void Network::setPerform(const QStringList &perform) |
572 | { |
573 | _perform = perform; |
574 | SYNC(ARG(perform)) |
575 | emit configChanged(); |
576 | } |
577 | |
578 | |
579 | void Network::setUseAutoIdentify(bool use) |
580 | { |
581 | _useAutoIdentify = use; |
582 | SYNC(ARG(use)) |
583 | emit configChanged(); |
584 | } |
585 | |
586 | |
587 | void Network::setAutoIdentifyService(const QString &service) |
588 | { |
589 | _autoIdentifyService = service; |
590 | SYNC(ARG(service)) |
591 | emit configChanged(); |
592 | } |
593 | |
594 | |
595 | void Network::setAutoIdentifyPassword(const QString &password) |
596 | { |
597 | _autoIdentifyPassword = password; |
598 | SYNC(ARG(password)) |
599 | emit configChanged(); |
600 | } |
601 | |
602 | |
603 | void Network::setUseSasl(bool use) |
604 | { |
605 | _useSasl = use; |
606 | SYNC(ARG(use)) |
607 | emit configChanged(); |
608 | } |
609 | |
610 | |
611 | void Network::setSaslAccount(const QString &account) |
612 | { |
613 | _saslAccount = account; |
614 | SYNC(ARG(account)) |
615 | emit configChanged(); |
616 | } |
617 | |
618 | |
619 | void Network::setSaslPassword(const QString &password) |
620 | { |
621 | _saslPassword = password; |
622 | SYNC(ARG(password)) |
623 | emit configChanged(); |
624 | } |
625 | |
626 | |
627 | void Network::setUseAutoReconnect(bool use) |
628 | { |
629 | _useAutoReconnect = use; |
630 | SYNC(ARG(use)) |
631 | emit configChanged(); |
632 | } |
633 | |
634 | |
635 | void Network::setAutoReconnectInterval(quint32 interval) |
636 | { |
637 | _autoReconnectInterval = interval; |
638 | SYNC(ARG(interval)) |
639 | emit configChanged(); |
640 | } |
641 | |
642 | |
643 | void Network::setAutoReconnectRetries(quint16 retries) |
644 | { |
645 | _autoReconnectRetries = retries; |
646 | SYNC(ARG(retries)) |
647 | emit configChanged(); |
648 | } |
649 | |
650 | |
651 | void Network::setUnlimitedReconnectRetries(bool unlimited) |
652 | { |
653 | _unlimitedReconnectRetries = unlimited; |
654 | SYNC(ARG(unlimited)) |
655 | emit configChanged(); |
656 | } |
657 | |
658 | |
659 | void Network::setRejoinChannels(bool rejoin) |
660 | { |
661 | _rejoinChannels = rejoin; |
662 | SYNC(ARG(rejoin)) |
663 | emit configChanged(); |
664 | } |
665 | |
666 | |
667 | void Network::addSupport(const QString ¶m, const QString &value) |
668 | { |
669 | if (!_supports.contains(param)) { |
670 | _supports[param] = value; |
671 | SYNC(ARG(param), ARG(value)) |
672 | } |
673 | } |
674 | |
675 | |
676 | void Network::removeSupport(const QString ¶m) |
677 | { |
678 | if (_supports.contains(param)) { |
679 | _supports.remove(param); |
680 | SYNC(ARG(param)) |
681 | } |
682 | } |
683 | |
684 | |
685 | QVariantMap Network::initSupports() const |
686 | { |
687 | QVariantMap supports; |
688 | QHashIterator<QString, QString> iter(_supports); |
689 | while (iter.hasNext()) { |
690 | iter.next(); |
691 | supports[iter.key()] = iter.value(); |
692 | } |
693 | return supports; |
694 | } |
695 | |
696 | |
697 | // There's potentially a lot of users and channels, so it makes sense to optimize the format of this. |
698 | // Rather than sending a thousand maps with identical keys, we convert this into one map containing lists |
699 | // where each list index corresponds to a particular IrcUser. This saves sending the key names a thousand times. |
700 | // Benchmarks have shown space savings of around 56%, resulting in saving several MBs worth of data on sync |
701 | // (without compression) with a decent amount of IrcUsers. |
702 | QVariantMap Network::initIrcUsersAndChannels() const |
703 | { |
704 | QVariantMap usersAndChannels; |
705 | |
706 | if (_ircUsers.count()) { |
707 | QHash<QString, QVariantList> users; |
708 | QHash<QString, IrcUser *>::const_iterator it = _ircUsers.begin(); |
709 | QHash<QString, IrcUser *>::const_iterator end = _ircUsers.end(); |
710 | while (it != end) { |
711 | const QVariantMap &map = it.value()->toVariantMap(); |
712 | QVariantMap::const_iterator mapiter = map.begin(); |
713 | while (mapiter != map.end()) { |
714 | users[mapiter.key()] << mapiter.value(); |
715 | ++mapiter; |
716 | } |
717 | ++it; |
718 | } |
719 | // Can't have a container with a value type != QVariant in a QVariant :( |
720 | // However, working directly on a QVariantMap is awkward for appending, thus the detour via the hash above. |
721 | QVariantMap userMap; |
722 | foreach(const QString &key, users.keys()) |
723 | userMap[key] = users[key]; |
724 | usersAndChannels["Users" ] = userMap; |
725 | } |
726 | |
727 | if (_ircChannels.count()) { |
728 | QHash<QString, QVariantList> channels; |
729 | QHash<QString, IrcChannel *>::const_iterator it = _ircChannels.begin(); |
730 | QHash<QString, IrcChannel *>::const_iterator end = _ircChannels.end(); |
731 | while (it != end) { |
732 | const QVariantMap &map = it.value()->toVariantMap(); |
733 | QVariantMap::const_iterator mapiter = map.begin(); |
734 | while (mapiter != map.end()) { |
735 | channels[mapiter.key()] << mapiter.value(); |
736 | ++mapiter; |
737 | } |
738 | ++it; |
739 | } |
740 | QVariantMap channelMap; |
741 | foreach(const QString &key, channels.keys()) |
742 | channelMap[key] = channels[key]; |
743 | usersAndChannels["Channels" ] = channelMap; |
744 | } |
745 | |
746 | return usersAndChannels; |
747 | } |
748 | |
749 | |
750 | void Network::initSetIrcUsersAndChannels(const QVariantMap &usersAndChannels) |
751 | { |
752 | Q_ASSERT(proxy()); |
753 | if (isInitialized()) { |
754 | qWarning() << "Network" << networkId() << "received init data for users and channels although there already are known users or channels!" ; |
755 | return; |
756 | } |
757 | |
758 | // toMap() and toList() are cheap, so we can avoid copying to lists... |
759 | // However, we really have to make sure to never accidentally detach from the shared data! |
760 | |
761 | const QVariantMap &users = usersAndChannels["Users" ].toMap(); |
762 | |
763 | // sanity check |
764 | int count = users["nick" ].toList().count(); |
765 | foreach(const QString &key, users.keys()) { |
766 | if (users[key].toList().count() != count) { |
767 | qWarning() << "Received invalid usersAndChannels init data, sizes of attribute lists don't match!" ; |
768 | return; |
769 | } |
770 | } |
771 | |
772 | // now create the individual IrcUsers |
773 | for(int i = 0; i < count; i++) { |
774 | QVariantMap map; |
775 | foreach(const QString &key, users.keys()) |
776 | map[key] = users[key].toList().at(i); |
777 | newIrcUser(map["nick" ].toString(), map); // newIrcUser() properly handles the hostmask being just the nick |
778 | } |
779 | |
780 | // same thing for IrcChannels |
781 | const QVariantMap &channels = usersAndChannels["Channels" ].toMap(); |
782 | |
783 | // sanity check |
784 | count = channels["name" ].toList().count(); |
785 | foreach(const QString &key, channels.keys()) { |
786 | if (channels[key].toList().count() != count) { |
787 | qWarning() << "Received invalid usersAndChannels init data, sizes of attribute lists don't match!" ; |
788 | return; |
789 | } |
790 | } |
791 | // now create the individual IrcChannels |
792 | for(int i = 0; i < count; i++) { |
793 | QVariantMap map; |
794 | foreach(const QString &key, channels.keys()) |
795 | map[key] = channels[key].toList().at(i); |
796 | newIrcChannel(map["name" ].toString(), map); |
797 | } |
798 | } |
799 | |
800 | |
801 | void Network::initSetSupports(const QVariantMap &supports) |
802 | { |
803 | QMapIterator<QString, QVariant> iter(supports); |
804 | while (iter.hasNext()) { |
805 | iter.next(); |
806 | addSupport(iter.key(), iter.value().toString()); |
807 | } |
808 | } |
809 | |
810 | |
811 | IrcUser *Network::updateNickFromMask(const QString &mask) |
812 | { |
813 | QString nick(nickFromMask(mask).toLower()); |
814 | IrcUser *ircuser; |
815 | |
816 | if (_ircUsers.contains(nick)) { |
817 | ircuser = _ircUsers[nick]; |
818 | ircuser->updateHostmask(mask); |
819 | } |
820 | else { |
821 | ircuser = newIrcUser(mask); |
822 | } |
823 | return ircuser; |
824 | } |
825 | |
826 | |
827 | void Network::ircUserNickChanged(QString newnick) |
828 | { |
829 | QString oldnick = _ircUsers.key(qobject_cast<IrcUser *>(sender())); |
830 | |
831 | if (oldnick.isNull()) |
832 | return; |
833 | |
834 | if (newnick.toLower() != oldnick) _ircUsers[newnick.toLower()] = _ircUsers.take(oldnick); |
835 | |
836 | if (myNick().toLower() == oldnick) |
837 | setMyNick(newnick); |
838 | } |
839 | |
840 | |
841 | void Network::emitConnectionError(const QString &errorMsg) |
842 | { |
843 | emit connectionError(errorMsg); |
844 | } |
845 | |
846 | |
847 | // ==================== |
848 | // Private: |
849 | // ==================== |
850 | void Network::determinePrefixes() const |
851 | { |
852 | // seems like we have to construct them first |
853 | QString prefix = support("PREFIX" ); |
854 | |
855 | if (prefix.startsWith("(" ) && prefix.contains(")" )) { |
856 | _prefixes = prefix.section(")" , 1); |
857 | _prefixModes = prefix.mid(1).section(")" , 0, 0); |
858 | } |
859 | else { |
860 | QString defaultPrefixes("~&@%+" ); |
861 | QString defaultPrefixModes("qaohv" ); |
862 | |
863 | if (prefix.isEmpty()) { |
864 | _prefixes = defaultPrefixes; |
865 | _prefixModes = defaultPrefixModes; |
866 | return; |
867 | } |
868 | // clear the existing modes, just in case we're run multiple times |
869 | _prefixes = QString(); |
870 | _prefixModes = QString(); |
871 | |
872 | // we just assume that in PREFIX are only prefix chars stored |
873 | for (int i = 0; i < defaultPrefixes.size(); i++) { |
874 | if (prefix.contains(defaultPrefixes[i])) { |
875 | _prefixes += defaultPrefixes[i]; |
876 | _prefixModes += defaultPrefixModes[i]; |
877 | } |
878 | } |
879 | // check for success |
880 | if (!_prefixes.isNull()) |
881 | return; |
882 | |
883 | // well... our assumption was obviously wrong... |
884 | // check if it's only prefix modes |
885 | for (int i = 0; i < defaultPrefixes.size(); i++) { |
886 | if (prefix.contains(defaultPrefixModes[i])) { |
887 | _prefixes += defaultPrefixes[i]; |
888 | _prefixModes += defaultPrefixModes[i]; |
889 | } |
890 | } |
891 | // now we've done all we've could... |
892 | } |
893 | } |
894 | |
895 | |
896 | /************************************************************************ |
897 | * NetworkInfo |
898 | ************************************************************************/ |
899 | |
900 | NetworkInfo::NetworkInfo() |
901 | : networkId(0), |
902 | identity(1), |
903 | useRandomServer(false), |
904 | useAutoIdentify(false), |
905 | autoIdentifyService("NickServ" ), |
906 | useSasl(false), |
907 | useAutoReconnect(true), |
908 | autoReconnectInterval(60), |
909 | autoReconnectRetries(20), |
910 | unlimitedReconnectRetries(false), |
911 | rejoinChannels(true) |
912 | { |
913 | } |
914 | |
915 | |
916 | bool NetworkInfo::operator==(const NetworkInfo &other) const |
917 | { |
918 | if (networkId != other.networkId) return false; |
919 | if (networkName != other.networkName) return false; |
920 | if (identity != other.identity) return false; |
921 | if (codecForServer != other.codecForServer) return false; |
922 | if (codecForEncoding != other.codecForEncoding) return false; |
923 | if (codecForDecoding != other.codecForDecoding) return false; |
924 | if (serverList != other.serverList) return false; |
925 | if (useRandomServer != other.useRandomServer) return false; |
926 | if (perform != other.perform) return false; |
927 | if (useAutoIdentify != other.useAutoIdentify) return false; |
928 | if (autoIdentifyService != other.autoIdentifyService) return false; |
929 | if (autoIdentifyPassword != other.autoIdentifyPassword) return false; |
930 | if (useSasl != other.useSasl) return false; |
931 | if (saslAccount != other.saslAccount) return false; |
932 | if (saslPassword != other.saslPassword) return false; |
933 | if (useAutoReconnect != other.useAutoReconnect) return false; |
934 | if (autoReconnectInterval != other.autoReconnectInterval) return false; |
935 | if (autoReconnectRetries != other.autoReconnectRetries) return false; |
936 | if (unlimitedReconnectRetries != other.unlimitedReconnectRetries) return false; |
937 | if (rejoinChannels != other.rejoinChannels) return false; |
938 | return true; |
939 | } |
940 | |
941 | |
942 | bool NetworkInfo::operator!=(const NetworkInfo &other) const |
943 | { |
944 | return !(*this == other); |
945 | } |
946 | |
947 | |
948 | QDataStream &operator<<(QDataStream &out, const NetworkInfo &info) |
949 | { |
950 | QVariantMap i; |
951 | i["NetworkId" ] = QVariant::fromValue<NetworkId>(info.networkId); |
952 | i["NetworkName" ] = info.networkName; |
953 | i["Identity" ] = QVariant::fromValue<IdentityId>(info.identity); |
954 | i["CodecForServer" ] = info.codecForServer; |
955 | i["CodecForEncoding" ] = info.codecForEncoding; |
956 | i["CodecForDecoding" ] = info.codecForDecoding; |
957 | i["ServerList" ] = toVariantList(info.serverList); |
958 | i["UseRandomServer" ] = info.useRandomServer; |
959 | i["Perform" ] = info.perform; |
960 | i["UseAutoIdentify" ] = info.useAutoIdentify; |
961 | i["AutoIdentifyService" ] = info.autoIdentifyService; |
962 | i["AutoIdentifyPassword" ] = info.autoIdentifyPassword; |
963 | i["UseSasl" ] = info.useSasl; |
964 | i["SaslAccount" ] = info.saslAccount; |
965 | i["SaslPassword" ] = info.saslPassword; |
966 | i["UseAutoReconnect" ] = info.useAutoReconnect; |
967 | i["AutoReconnectInterval" ] = info.autoReconnectInterval; |
968 | i["AutoReconnectRetries" ] = info.autoReconnectRetries; |
969 | i["UnlimitedReconnectRetries" ] = info.unlimitedReconnectRetries; |
970 | i["RejoinChannels" ] = info.rejoinChannels; |
971 | out << i; |
972 | return out; |
973 | } |
974 | |
975 | |
976 | QDataStream &operator>>(QDataStream &in, NetworkInfo &info) |
977 | { |
978 | QVariantMap i; |
979 | in >> i; |
980 | info.networkId = i["NetworkId" ].value<NetworkId>(); |
981 | info.networkName = i["NetworkName" ].toString(); |
982 | info.identity = i["Identity" ].value<IdentityId>(); |
983 | info.codecForServer = i["CodecForServer" ].toByteArray(); |
984 | info.codecForEncoding = i["CodecForEncoding" ].toByteArray(); |
985 | info.codecForDecoding = i["CodecForDecoding" ].toByteArray(); |
986 | info.serverList = fromVariantList<Network::Server>(i["ServerList" ].toList()); |
987 | info.useRandomServer = i["UseRandomServer" ].toBool(); |
988 | info.perform = i["Perform" ].toStringList(); |
989 | info.useAutoIdentify = i["UseAutoIdentify" ].toBool(); |
990 | info.autoIdentifyService = i["AutoIdentifyService" ].toString(); |
991 | info.autoIdentifyPassword = i["AutoIdentifyPassword" ].toString(); |
992 | info.useSasl = i["UseSasl" ].toBool(); |
993 | info.saslAccount = i["SaslAccount" ].toString(); |
994 | info.saslPassword = i["SaslPassword" ].toString(); |
995 | info.useAutoReconnect = i["UseAutoReconnect" ].toBool(); |
996 | info.autoReconnectInterval = i["AutoReconnectInterval" ].toUInt(); |
997 | info.autoReconnectRetries = i["AutoReconnectRetries" ].toInt(); |
998 | info.unlimitedReconnectRetries = i["UnlimitedReconnectRetries" ].toBool(); |
999 | info.rejoinChannels = i["RejoinChannels" ].toBool(); |
1000 | return in; |
1001 | } |
1002 | |
1003 | |
1004 | QDebug operator<<(QDebug dbg, const NetworkInfo &i) |
1005 | { |
1006 | dbg.nospace() << "(id = " << i.networkId << " name = " << i.networkName << " identity = " << i.identity |
1007 | << " codecForServer = " << i.codecForServer << " codecForEncoding = " << i.codecForEncoding << " codecForDecoding = " << i.codecForDecoding |
1008 | << " serverList = " << i.serverList << " useRandomServer = " << i.useRandomServer << " perform = " << i.perform |
1009 | << " useAutoIdentify = " << i.useAutoIdentify << " autoIdentifyService = " << i.autoIdentifyService << " autoIdentifyPassword = " << i.autoIdentifyPassword |
1010 | << " useSasl = " << i.useSasl << " saslAccount = " << i.saslAccount << " saslPassword = " << i.saslPassword |
1011 | << " useAutoReconnect = " << i.useAutoReconnect << " autoReconnectInterval = " << i.autoReconnectInterval |
1012 | << " autoReconnectRetries = " << i.autoReconnectRetries << " unlimitedReconnectRetries = " << i.unlimitedReconnectRetries |
1013 | << " rejoinChannels = " << i.rejoinChannels << ")" ; |
1014 | return dbg.space(); |
1015 | } |
1016 | |
1017 | |
1018 | QDataStream &operator<<(QDataStream &out, const Network::Server &server) |
1019 | { |
1020 | QVariantMap serverMap; |
1021 | serverMap["Host" ] = server.host; |
1022 | serverMap["Port" ] = server.port; |
1023 | serverMap["Password" ] = server.password; |
1024 | serverMap["UseSSL" ] = server.useSsl; |
1025 | serverMap["sslVersion" ] = server.sslVersion; |
1026 | serverMap["UseProxy" ] = server.useProxy; |
1027 | serverMap["ProxyType" ] = server.proxyType; |
1028 | serverMap["ProxyHost" ] = server.proxyHost; |
1029 | serverMap["ProxyPort" ] = server.proxyPort; |
1030 | serverMap["ProxyUser" ] = server.proxyUser; |
1031 | serverMap["ProxyPass" ] = server.proxyPass; |
1032 | out << serverMap; |
1033 | return out; |
1034 | } |
1035 | |
1036 | |
1037 | QDataStream &operator>>(QDataStream &in, Network::Server &server) |
1038 | { |
1039 | QVariantMap serverMap; |
1040 | in >> serverMap; |
1041 | server.host = serverMap["Host" ].toString(); |
1042 | server.port = serverMap["Port" ].toUInt(); |
1043 | server.password = serverMap["Password" ].toString(); |
1044 | server.useSsl = serverMap["UseSSL" ].toBool(); |
1045 | server.sslVersion = serverMap["sslVersion" ].toInt(); |
1046 | server.useProxy = serverMap["UseProxy" ].toBool(); |
1047 | server.proxyType = serverMap["ProxyType" ].toInt(); |
1048 | server.proxyHost = serverMap["ProxyHost" ].toString(); |
1049 | server.proxyPort = serverMap["ProxyPort" ].toUInt(); |
1050 | server.proxyUser = serverMap["ProxyUser" ].toString(); |
1051 | server.proxyPass = serverMap["ProxyPass" ].toString(); |
1052 | return in; |
1053 | } |
1054 | |
1055 | |
1056 | bool Network::Server::operator==(const Server &other) const |
1057 | { |
1058 | if (host != other.host) return false; |
1059 | if (port != other.port) return false; |
1060 | if (password != other.password) return false; |
1061 | if (useSsl != other.useSsl) return false; |
1062 | if (sslVersion != other.sslVersion) return false; |
1063 | if (useProxy != other.useProxy) return false; |
1064 | if (proxyType != other.proxyType) return false; |
1065 | if (proxyHost != other.proxyHost) return false; |
1066 | if (proxyPort != other.proxyPort) return false; |
1067 | if (proxyUser != other.proxyUser) return false; |
1068 | if (proxyPass != other.proxyPass) return false; |
1069 | return true; |
1070 | } |
1071 | |
1072 | |
1073 | bool Network::Server::operator!=(const Server &other) const |
1074 | { |
1075 | return !(*this == other); |
1076 | } |
1077 | |
1078 | |
1079 | QDebug operator<<(QDebug dbg, const Network::Server &server) |
1080 | { |
1081 | dbg.nospace() << "Server(host = " << server.host << ":" << server.port << ", useSsl = " << server.useSsl << ")" ; |
1082 | return dbg.space(); |
1083 | } |
1084 | |