use KTcpSocket in the server object
[konversation:xdarklights-konversation.git] / src / irc / server.cpp
1 // -*- mode: c++; c-file-style: "bsd"; c-basic-offset: 4; tabs-width: 4; indent-tabs-mode: nil -*-
2
3 /*
4   This program is free software; you can redistribute it and/or modify
5   it under the terms of the GNU General Public License as published by
6   the Free Software Foundation; either version 2 of the License, or
7   (at your option) any later version.
8 */
9
10 /*
11   Copyright (C) 2002 Dario Abatianni <eisfuchs@tigress.com>
12   Copyright (C) 2005 Ismail Donmez <ismail@kde.org>
13   Copyright (C) 2005-2006 Peter Simonsson <psn@linux.se>
14   Copyright (C) 2006-2008 Eli J. MacKenzie <argonel at gmail.com>
15   Copyright (C) 2005-2008 Eike Hein <hein@kde.org>
16 */
17
18 #include "server.h"
19 #include "ircqueue.h"
20 #include "query.h"
21 #include "channel.h"
22 #include "application.h"
23 #include "connectionmanager.h"
24 #include "dcccommon.h"
25 #include "transfermanager.h"
26 #include "transfersend.h"
27 #include "transferrecv.h"
28 #include <chat.h>
29 #include "recipientdialog.h"
30 #include "nick.h"
31 #include "irccharsets.h"
32 #include "viewcontainer.h"
33 #include "statuspanel.h"
34 #include "rawlog.h"
35 #include "channellistpanel.h"
36 #include "scriptlauncher.h"
37 #include "servergroupsettings.h"
38 #include "addressbook.h"
39 #include "serverison.h"
40 #include "common.h"
41 #include "notificationhandler.h"
42 #include "awaymanager.h"
43
44 #include <QRegExp>
45 #include <QTextCodec>
46 #include <QDateTime>
47 #include <QStringListModel>
48
49 #include <KLocale>
50 #include <KFileDialog>
51 #include <KInputDialog>
52 #include <KWindowSystem>
53
54 #include <solid/networking.h>
55
56 #include <kio/sslui.h>
57
58 using namespace Konversation;
59
60 int Server::m_availableConnectionId = 0;
61
62 Server::Server(QObject* parent, ConnectionSettings& settings) : QObject(parent)
63 {
64     m_connectionId = m_availableConnectionId;
65     m_availableConnectionId++;
66
67     setConnectionSettings(settings);
68
69     m_connectionState = Konversation::SSNeverConnected;
70
71     for (int i=0; i <= Application::instance()->countOfQueues(); i++)
72     {
73         //QList<int> r=Preferences::queueRate(i);
74         IRCQueue *q=new IRCQueue(this, Application::instance()->staticrates[i]); //FIXME these are supposed to be in the rc
75         m_queues.append(q);
76     }
77
78     m_processingIncoming = false;
79     m_identifyMsg = false;
80     m_autoIdentifyLock = false;
81     m_autoJoin = false;
82
83     m_nickIndices.clear();
84     m_nickIndices.append(0);
85
86     m_nickListModel = new QStringListModel(this);
87
88     m_currentLag = -1;
89     m_rawLog = 0;
90     m_channelListPanel = 0;
91     m_serverISON = 0;
92     m_away = false;
93     m_socket = 0;
94     m_prevISONList = QStringList();
95     m_bytesReceived = 0;
96     m_encodedBytesSent=0;
97     m_bytesSent=0;
98     m_linesSent=0;
99     // TODO fold these into a QMAP, and these need to be reset to RFC values if this server object is reused.
100     m_serverNickPrefixModes = "ovh";
101     m_serverNickPrefixes = "@+%";
102     m_banAddressListModes = 'b'; // {RFC-1459, draft-brocklesby-irc-isupport} -> pick one
103     m_channelPrefixes = "#&";
104     m_modesCount = 3;
105     m_showSSLConfirmation = true;
106
107     setObjectName(QString::fromLatin1("server_") + settings.name());
108
109     setNickname(settings.initialNick());
110     obtainNickInfo(getNickname());
111
112     m_statusView = getViewContainer()->addStatusView(this);
113
114     if (Preferences::self()->rawLog())
115         addRawLog(false);
116
117     m_inputFilter.setServer(this);
118     m_outputFilter = new Konversation::OutputFilter(this);
119     m_scriptLauncher = new ScriptLauncher(this);
120
121     // For /msg query completion
122     m_completeQueryPosition = 0;
123
124     updateAutoJoin(settings.oneShotChannelList());
125
126     if (!getIdentity()->getShellCommand().isEmpty())
127         QTimer::singleShot(0, this, SLOT(doPreShellCommand()));
128     else
129         QTimer::singleShot(0, this, SLOT(connectToIRCServer()));
130
131     initTimers();
132
133     if (getIdentity()->getShellCommand().isEmpty())
134         connectSignals();
135     // TODO FIXME this disappeared in a merge, ensure it should have
136     updateConnectionState(Konversation::SSNeverConnected);
137
138     connect(Konversation::Addressbook::self()->getAddressBook(), SIGNAL(addressBookChanged(AddressBook *)), this, SLOT(updateNickInfoAddressees()));
139     connect(Konversation::Addressbook::self(), SIGNAL(addresseesChanged()), this, SLOT(updateNickInfoAddressees()));
140
141     m_nickInfoChangedTimer = new QTimer(this);
142     m_nickInfoChangedTimer->setSingleShot(true);
143     m_nickInfoChangedTimer->setInterval(3000);
144     connect(m_nickInfoChangedTimer, SIGNAL(timeout()), this, SLOT(sendNickInfoChangedSignals()));
145
146     m_channelNickChangedTimer = new QTimer(this);
147     m_channelNickChangedTimer->setSingleShot(true);
148     m_channelNickChangedTimer->setInterval(1000);
149     connect(m_channelNickChangedTimer, SIGNAL(timeout()), this, SLOT(sendChannelNickChangedSignals()));
150 }
151
152 Server::~Server()
153 {
154     //send queued messages
155     kDebug() << "Server::~Server(" << getServerName() << ")";
156
157     // Delete helper object.
158     delete m_serverISON;
159     m_serverISON = 0;
160
161     // clear nicks online
162     emit nicksNowOnline(this,QStringList(),true);
163
164     // Make sure no signals get sent to a soon to be dying Server Window
165     if (m_socket)
166     {
167         m_socket->blockSignals(true);
168         m_socket->deleteLater();
169     }
170
171     if (m_statusView) delete m_statusView;
172
173     closeRawLog();
174     closeChannelListPanel();
175
176     qDeleteAll(m_channelList);
177     m_channelList.clear();
178
179     qDeleteAll(m_queryList);
180     m_queryList.clear();
181
182     // Delete all the NickInfos and ChannelNick structures.
183     m_allNicks.clear();
184
185     ChannelMembershipMap::ConstIterator it;
186
187     for ( it = m_joinedChannels.constBegin(); it != m_joinedChannels.constEnd(); ++it )
188         delete it.value();
189     m_joinedChannels.clear();
190
191     for ( it = m_unjoinedChannels.constBegin(); it != m_unjoinedChannels.constEnd(); ++it )
192         delete it.value();
193     m_unjoinedChannels.clear();
194
195     m_queryNicks.clear();
196
197     //Delete the queues
198     qDeleteAll(m_queues);
199
200     emit destroyed(m_connectionId);
201
202     kDebug() << "~Server done";
203 }
204
205 //... so called to match the ChatWindow derivatives.
206 bool Server::closeYourself(bool)
207 {
208     QTimer::singleShot(0, m_statusView, SLOT(serverSaysClose()));
209     return true;
210 }
211
212 void Server::doPreShellCommand()
213 {
214     QString command = getIdentity()->getShellCommand();
215     getStatusView()->appendServerMessage(i18n("Info"),"Running preconfigured command...");
216
217     connect(&m_preShellCommand,SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(preShellCommandExited(int, QProcess::ExitStatus)));
218     connect(&m_preShellCommand,SIGNAL(error(QProcess::ProcessError)), this, SLOT(preShellCommandError(QProcess::ProcessError)));
219
220     const QStringList commandList = command.split(' ');
221
222     for (QStringList::ConstIterator it = commandList.begin(); it != commandList.end(); ++it)
223         m_preShellCommand << *it;
224
225     m_preShellCommand.start();
226     if (m_preShellCommand.state() == QProcess::NotRunning) preShellCommandExited(m_preShellCommand.exitCode(), m_preShellCommand.exitStatus());
227 }
228
229 void Server::initTimers()
230 {
231     m_notifyTimer.setObjectName("notify_timer");
232     m_notifyTimer.setSingleShot(true);
233     m_incomingTimer.setObjectName("incoming_timer");
234 }
235
236 void Server::connectSignals()
237 {
238     // Timers
239     connect(&m_incomingTimer, SIGNAL(timeout()), this, SLOT(processIncomingData()));
240     connect(&m_notifyTimer, SIGNAL(timeout()), this, SLOT(notifyTimeout()));
241     connect(&m_pingResponseTimer, SIGNAL(timeout()), this, SLOT(updateLongPongLag()));
242
243     // OutputFilter
244     connect(getOutputFilter(), SIGNAL(requestDccSend()), this,SLOT(requestDccSend()));
245     connect(getOutputFilter(), SIGNAL(requestDccSend(const QString&)), this, SLOT(requestDccSend(const QString&)));
246     connect(getOutputFilter(), SIGNAL(multiServerCommand(const QString&, const QString&)),
247         this, SLOT(sendMultiServerCommand(const QString&, const QString&)));
248     connect(getOutputFilter(), SIGNAL(reconnectServer()), this, SLOT(reconnect()));
249     connect(getOutputFilter(), SIGNAL(disconnectServer()), this, SLOT(disconnect()));
250     connect(getOutputFilter(), SIGNAL(openDccSend(const QString &, KUrl)), this, SLOT(addDccSend(const QString &, KUrl)));
251     connect(getOutputFilter(), SIGNAL(openDccChat(const QString &)), this, SLOT(openDccChat(const QString &)));
252     connect(getOutputFilter(), SIGNAL(openDccWBoard(const QString &)), this, SLOT(openDccWBoard(const QString &)));
253     connect(getOutputFilter(), SIGNAL(acceptDccGet(const QString&, const QString&)),
254         this, SLOT(acceptDccGet(const QString&, const QString&)));
255     connect(getOutputFilter(), SIGNAL(sendToAllChannels(const QString&)), this, SLOT(sendToAllChannels(const QString&)));
256     connect(getOutputFilter(), SIGNAL(banUsers(const QStringList&,const QString&,const QString&)),
257         this, SLOT(requestBan(const QStringList&,const QString&,const QString&)));
258     connect(getOutputFilter(), SIGNAL(unbanUsers(const QString&,const QString&)),
259         this, SLOT(requestUnban(const QString&,const QString&)));
260     connect(getOutputFilter(), SIGNAL(openRawLog(bool)), this, SLOT(addRawLog(bool)));
261     connect(getOutputFilter(), SIGNAL(closeRawLog()), this, SLOT(closeRawLog()));
262     connect(getOutputFilter(), SIGNAL(encodingChanged()), this, SLOT(updateEncoding()));
263
264     Application* konvApp = static_cast<Application*>(kapp);
265     connect(getOutputFilter(), SIGNAL(connectTo(Konversation::ConnectionFlag, const QString&,
266                 const QString&, const QString&, const QString&, const QString&, bool)),
267             konvApp->getConnectionManager(), SLOT(connectTo(Konversation::ConnectionFlag,
268                 const QString&, const QString&, const QString&, const QString&, const QString&, bool)));
269                 connect(konvApp->getDccTransferManager(), SIGNAL(newDccTransferQueued(Konversation::DCC::Transfer*)),
270                         this, SLOT(slotNewDccTransferItemQueued(Konversation::DCC::Transfer*)));
271
272     connect(konvApp, SIGNAL(appearanceChanged()), this, SLOT(startNotifyTimer()));
273
274    // ViewContainer
275     connect(this, SIGNAL(showView(ChatWindow*)), getViewContainer(), SLOT(showView(ChatWindow*)));
276     connect(this, SIGNAL(addDccPanel()), getViewContainer(), SLOT(addDccPanel()));
277     connect(this, SIGNAL(addDccChat(Konversation::DCC::Chat*)),
278             getViewContainer(), SLOT(addDccChat(Konversation::DCC::Chat*)));
279     connect(this, SIGNAL(serverLag(Server*, int)), getViewContainer(), SIGNAL(updateStatusBarLagLabel(Server*, int)));
280     connect(this, SIGNAL(tooLongLag(Server*, int)), getViewContainer(), SIGNAL(setStatusBarLagLabelTooLongLag(Server*, int)));
281     connect(this, SIGNAL(resetLag()), getViewContainer(), SIGNAL(resetStatusBarLagLabel()));
282     connect(getOutputFilter(), SIGNAL(showView(ChatWindow*)), getViewContainer(), SLOT(showView(ChatWindow*)));
283     connect(getOutputFilter(), SIGNAL(openKonsolePanel()), getViewContainer(), SLOT(addKonsolePanel()));
284     connect(getOutputFilter(), SIGNAL(openChannelList(const QString&, bool)), getViewContainer(), SLOT(openChannelList(const QString&, bool)));
285     connect(getOutputFilter(), SIGNAL(closeDccPanel()), getViewContainer(), SLOT(closeDccPanel()));
286     connect(getOutputFilter(), SIGNAL(addDccPanel()), getViewContainer(), SLOT(addDccPanel()));
287
288     // Inputfilter
289     connect(&m_inputFilter, SIGNAL(addDccChat(const QString&,const QStringList&)),
290             this, SLOT(addDccChat(const QString&,const QStringList&)));
291     connect(&m_inputFilter, SIGNAL(rejectDccChat(const QString&)),
292             this, SLOT(rejectDccChat(const QString&)));
293     connect(&m_inputFilter, SIGNAL(startReverseDccChat(const QString&,const QStringList&)),
294             this, SLOT(startReverseDccChat(const QString&,const QStringList&)));
295     connect(&m_inputFilter, SIGNAL(welcome(const QString&)), this, SLOT(connectionEstablished(const QString&)));
296     connect(&m_inputFilter, SIGNAL(notifyResponse(const QString&)), this, SLOT(notifyResponse(const QString&)));
297     connect(&m_inputFilter, SIGNAL(startReverseDccSendTransfer(const QString&,const QStringList&)),
298         this, SLOT(startReverseDccSendTransfer(const QString&,const QStringList&)));
299     connect(&m_inputFilter, SIGNAL(addDccGet(const QString&, const QStringList&)),
300         this, SLOT(addDccGet(const QString&, const QStringList&)));
301     connect(&m_inputFilter, SIGNAL(resumeDccGetTransfer(const QString&, const QStringList&)),
302         this, SLOT(resumeDccGetTransfer(const QString&, const QStringList&)));
303     connect(&m_inputFilter, SIGNAL(resumeDccSendTransfer(const QString&, const QStringList&)),
304         this, SLOT(resumeDccSendTransfer(const QString&, const QStringList&)));
305     connect(&m_inputFilter, SIGNAL(rejectDccSendTransfer(const QString&, const QStringList&)),
306         this, SLOT(rejectDccSendTransfer(const QString&, const QStringList&)));
307     connect(&m_inputFilter, SIGNAL(userhost(const QString&,const QString&,bool,bool)),
308         this, SLOT(userhost(const QString&,const QString&,bool,bool)) );
309     connect(&m_inputFilter, SIGNAL(topicAuthor(const QString&,const QString&,QDateTime)),
310         this, SLOT(setTopicAuthor(const QString&,const QString&,QDateTime)) );
311     connect(&m_inputFilter, SIGNAL(endOfWho(const QString&)),
312         this, SLOT(endOfWho(const QString&)) );
313     connect(&m_inputFilter, SIGNAL(invitation(const QString&,const QString&)),
314         this,SLOT(invitation(const QString&,const QString&)) );
315     connect(&m_inputFilter, SIGNAL(addToChannelList(const QString&, int, const QString& )),
316         this, SLOT(addToChannelList(const QString&, int, const QString& )));
317
318     // Status View
319     connect(this, SIGNAL(serverOnline(bool)), getStatusView(), SLOT(serverOnline(bool)));
320
321     // Scripts
322     connect(getOutputFilter(), SIGNAL(launchScript(const QString&, const QString&)),
323         m_scriptLauncher, SLOT(launchScript(const QString&, const QString&)));
324     connect(m_scriptLauncher, SIGNAL(scriptNotFound(const QString&)),
325         this, SLOT(scriptNotFound(const QString&)));
326     connect(m_scriptLauncher, SIGNAL(scriptExecutionError(const QString&)),
327         this, SLOT(scriptExecutionError(const QString&)));
328
329     // Stats
330     connect(this, SIGNAL(sentStat(int, int)), SLOT(collectStats(int, int)));
331 }
332
333 int Server::getPort()
334 {
335     return getConnectionSettings().server().port();
336 }
337
338 int Server::getLag()  const
339 {
340     return m_currentLag;
341 }
342
343 bool Server::getAutoJoin()  const
344 {
345     return m_autoJoin;
346 }
347
348 void Server::setAutoJoin(bool on)
349 {
350     m_autoJoin = on;
351 }
352
353 void Server::preShellCommandExited(int exitCode, QProcess::ExitStatus exitStatus)
354 {
355     Q_UNUSED(exitCode);
356     if (exitStatus == QProcess::NormalExit)
357         getStatusView()->appendServerMessage(i18n("Info"),"Process executed successfully!");
358     else
359         getStatusView()->appendServerMessage(i18n("Warning"),"There was a problem while executing the command!");
360
361     connectToIRCServer();
362     connectSignals();
363 }
364
365 void Server::preShellCommandError(QProcess::ProcessError error)
366 {
367     Q_UNUSED(error);
368
369     getStatusView()->appendServerMessage(i18n("Warning"),"There was a problem while executing the command!");
370
371     connectToIRCServer();
372     connectSignals();
373 }
374
375 void Server::connectToIRCServer()
376 {
377     if (!isConnected())
378     {
379 // Reenable check when it works reliably for all backends
380 //         if(Solid::Networking::status() != Solid::Networking::Connected)
381 //         {
382 //             updateConnectionState(Konversation::SSInvoluntarilyDisconnected);
383 //             return;
384 //         }
385
386         updateConnectionState(Konversation::SSConnecting);
387
388         m_autoIdentifyLock = false;
389         m_ownIpByUserhost.clear();
390
391         resetQueues();
392
393         // This is needed to support server groups with mixed SSL and nonSSL servers
394         delete m_socket;
395         m_socket = 0;
396         if (m_referenceNicklist != getIdentity()->getNicknameList())
397             m_nickListModel->setStringList(getIdentity()->getNicknameList());
398         resetNickSelection();
399
400         m_socket = new KTcpSocket();
401         m_socket->setObjectName("serverSocket");
402
403         connect(m_socket, SIGNAL(error(KTcpSocket::Error)), SLOT(broken(KTcpSocket::Error)) );
404         connect(m_socket, SIGNAL(readyRead()), SLOT(incoming()));
405         connect(m_socket, SIGNAL(disconnected()), SLOT(closed()));
406
407         connect(m_socket, SIGNAL(hostFound()), SLOT (hostFound()));
408
409         // connect() will do a async lookup too
410         if(!getConnectionSettings().server().SSLEnabled())
411         {
412             connect(m_socket, SIGNAL(connected()), SLOT (ircServerConnectionSuccess()));
413             m_socket->connectToHost(getConnectionSettings().server().host(), getConnectionSettings().server().port());
414         }
415         else
416         {
417             connect(m_socket, SIGNAL(encrypted()), SLOT (ircServerConnectionSuccess()));
418             connect(m_socket, SIGNAL(sslErrors(const QList<KSslError>&)), SLOT(sslError(const QList<KSslError>&)));
419             
420             m_socket->connectToHostEncrypted(getConnectionSettings().server().host(), getConnectionSettings().server().port());
421         }
422
423         // set up the connection details
424         setPrefixes(m_serverNickPrefixModes, m_serverNickPrefixes);
425         getStatusView()->appendServerMessage(i18n("Info"),i18n("Looking for server %1 (port <numid>%2</numid>) ...",
426             getConnectionSettings().server().host(),
427             QString::number(getConnectionSettings().server().port())));
428         // reset InputFilter (auto request info, /WHO request info)
429         m_inputFilter.reset();
430     }
431     else
432         kDebug() << "connectToIRCServer() called while already connected: This should never happen.";
433 }
434
435 void Server::showSSLDialog()
436 {
437         //TODO
438         /*
439           SSLSocket* sslsocket = dynamic_cast<SSLSocket*>(m_socket);
440
441           if (sslsocket) sslsocket->showInfoDialog();
442         */
443 }
444
445 // set available channel types according to 005 RPL_ISUPPORT
446 void Server::setChannelTypes(const QString &pre)
447 {
448     m_channelPrefixes = pre;
449 }
450
451 QString Server::getChannelTypes() const
452 {
453     return m_channelPrefixes;
454 }
455
456 // set max number of channel modes with parameter according to 005 RPL_ISUPPORT
457 void Server::setModesCount(int count)
458 {
459     m_modesCount = count;
460 }
461
462 int Server::getModesCount()
463 {
464     return m_modesCount;
465 }
466
467 // set user mode prefixes according to non-standard 005-Reply (see inputfilter.cpp)
468 void Server::setPrefixes(const QString &modes, const QString& prefixes)
469 {
470     // NOTE: serverModes is QString::null, if server did not supply the
471     // modes which relates to the network's nick-prefixes
472     m_serverNickPrefixModes = modes;
473     m_serverNickPrefixes = prefixes;
474 }
475
476 void Server::setChanModes(QString modes)
477 {
478     QStringList abcd = modes.split(',');
479     m_banAddressListModes = abcd.value(0);
480 }
481
482 // return a nickname without possible mode character at the beginning
483 void Server::mangleNicknameWithModes(QString& nickname,bool& isAdmin,bool& isOwner,
484 bool& isOp,bool& isHalfop,bool& hasVoice)
485 {
486     isAdmin = false;
487     isOwner = false;
488     isOp = false;
489     isHalfop = false;
490     hasVoice = false;
491
492     int modeIndex;
493
494     if (nickname.isEmpty()) return;
495
496     while ((modeIndex = m_serverNickPrefixes.indexOf(nickname[0])) != -1)
497     {
498         if(nickname.isEmpty())
499             return;
500         nickname = nickname.mid(1);
501         // cut off the prefix
502         bool recognisedMode = false;
503         // determine, whether status is like op or like voice
504         while((modeIndex)<int(m_serverNickPrefixes.length()) && !recognisedMode)
505         {
506             switch(m_serverNickPrefixes[modeIndex].toLatin1())
507             {
508                 case '*':                         // admin (EUIRC)
509                 {
510                     isAdmin = true;
511                     recognisedMode = true;
512                     break;
513                 }
514                 case '&':                         // admin (unrealircd)
515                 {
516                     isAdmin = true;
517                     recognisedMode = true;
518                     break;
519                 }
520                 case '!':                         // channel owner (RFC2811)
521                 {
522                     isOwner = true;
523                     recognisedMode = true;
524                     break;
525                 }
526                 case '~':                         // channel owner (unrealircd)
527                 {
528                     isOwner = true;
529                     recognisedMode = true;
530                     break;
531                 }
532                 case '@':                         // channel operator (RFC1459)
533                 {
534                     isOp = true;
535                     recognisedMode = true;
536                     break;
537                 }
538                 case '%':                         // halfop
539                 {
540                     isHalfop = true;
541                     recognisedMode = true;
542                     break;
543                 }
544                 case '+':                         // voiced (RFC1459)
545                 {
546                     hasVoice = true;
547                     recognisedMode = true;
548                     break;
549                 }
550                 default:
551                 {
552                     ++modeIndex;
553                     break;
554                 }
555             }                                     //switch to recognise the mode.
556         }                                         // loop through the modes to find one recognised
557     }                                             // loop through the name
558 }
559
560 void Server::hostFound()
561 {
562     getStatusView()->appendServerMessage(i18n("Info"),i18n("Server found, connecting..."));
563 }
564
565 void Server::ircServerConnectionSuccess()
566 {
567         emit sslConnected(this);
568     getConnectionSettings().setReconnectCount(0);
569
570     Konversation::ServerSettings serverSettings = getConnectionSettings().server();
571
572     connect(this, SIGNAL(nicknameChanged(const QString&)), getStatusView(), SLOT(setNickname(const QString&)));
573     getStatusView()->appendServerMessage(i18n("Info"),i18n("Connected; logging in..."));
574
575     QString connectString = "USER " +
576         getIdentity()->getIdent() +
577         " 8 * :" +                                // 8 = +i; 4 = +w
578         getIdentity()->getRealName();
579
580     QStringList ql;
581     if (!serverSettings.password().isEmpty())
582         ql << "PASS " + serverSettings.password();
583
584     ql << "NICK "+getNickname();
585     ql << connectString;
586     queueList(ql, HighPriority);
587
588     setNickname(getNickname());
589 }
590
591 void Server::broken(KTcpSocket::Error error)
592 {
593     Q_UNUSED(error);
594     kDebug() << "Connection broken " << m_socket->errorString() << "!";
595
596     m_socket->blockSignals(true);
597
598     resetQueues();
599
600     m_notifyTimer.stop();
601     m_pingResponseTimer.stop();
602     m_inputFilter.setLagMeasuring(false);
603     m_currentLag = -1;
604
605     // HACK Only show one nick change dialog at connection time.
606     // This hack is a bit nasty as it assumes that the only KDialog
607     // child of the statusview will be the nick change dialog.
608     if (getStatusView())
609     {
610         KDialog* nickChangeDialog = getStatusView()->findChild<KDialog*>();
611
612         if (nickChangeDialog) nickChangeDialog->reject();
613     }
614
615     emit resetLag();
616     emit nicksNowOnline(this,QStringList(),true);
617
618     updateAutoJoin();
619
620     if (getConnectionState() != Konversation::SSDeliberatelyDisconnected)
621     {
622         static_cast<Application*>(kapp)->notificationHandler()->connectionFailure(getStatusView(), getServerName());
623
624         QString error = i18n("Connection to server %1 (port <numid>%2</numid>) lost: %3.",
625             getConnectionSettings().server().host(),
626             QString::number(getConnectionSettings().server().port()),
627             m_socket->errorString());
628
629         getStatusView()->appendServerMessage(i18n("Error"), error);
630
631         updateConnectionState(Konversation::SSInvoluntarilyDisconnected);
632     }
633 }
634
635 bool Server::askUserToIgnoreSslErrors()
636 {
637     bool retVal = false;
638     
639     // we are called by sslError
640     // sslError is a slot, if it's signal is emitted multiple times
641     // then we only want to show the dialog once
642     if ( m_showSSLConfirmation )
643     {
644         // we don't want to show any further SSL confirmation dialogs
645         m_showSSLConfirmation = false;
646         
647         // ask the user if he wants to ignore SSL errors
648         // in case the user wants to make the rule he chose (for example: always allow) persistent
649         // this will not show a dialog (but it will return "sslErrorsIgnored = true")
650         retVal = KIO::SslUi::askIgnoreSslErrors( m_socket, KIO::SslUi::RecallAndStoreRules );
651         
652         // as we're done now we can show further SSL dialogs
653         m_showSSLConfirmation = true;
654     }
655     
656     return retVal;
657 }
658
659 void Server::sslError( const QList<KSslError>& errors )
660 {
661     // ask the user if he wants to ignore the errors
662     if ( askUserToIgnoreSslErrors() )
663     {
664         // the user has chosen to ignore SSL errors
665         m_socket->ignoreSslErrors();
666         
667         // show a warning in the chat window that the SSL certificate failed the authenticity check
668         QString error = i18n("The SSL certificate for the the server %1 (port <numid>%2</numid>) failed the authenticity check.",
669                             getConnectionSettings().server().host(),
670                             QString::number(getConnectionSettings().server().port()));
671                             
672         getStatusView()->appendServerMessage(i18n("SSL Connection Warning"), error);
673     }
674     else
675     {
676         QString errorReason;
677         
678         for (int i = 0; i < errors.size(); ++i)
679         {
680             errorReason += errors.at(i).errorString() + ' ';
681         }
682         
683         // TODO: this message should be adjusted. it's possible that the user refused the invalid SSL certificate.
684         QString error = i18n("Could not connect to %1 (port <numid>%2</numid>) using SSL encryption. Maybe the server does not support SSL, or perhaps you have the wrong port? %3",
685             getConnectionSettings().server().host(),
686             QString::number(getConnectionSettings().server().port()),
687             errorReason);
688         
689         getStatusView()->appendServerMessage(i18n("SSL Connection Error"), error);
690         
691         emit sslInitFailure();
692     }
693 }
694
695 // Will be called from InputFilter as soon as the Welcome message was received
696 void Server::connectionEstablished(const QString& ownHost)
697 {
698     // Some servers don't include the userhost in RPL_WELCOME, so we
699     // need to use RPL_USERHOST to get ahold of our IP later on
700     if (!ownHost.isEmpty())
701         QHostInfo::lookupHost(ownHost, this, SLOT(gotOwnResolvedHostByWelcome(const QHostInfo&)));
702
703     updateConnectionState(Konversation::SSConnected);
704
705     // Make a helper object to build ISON (notify) list and map offline nicks to addressbook.
706     // TODO: Give the object a kick to get it started?
707     m_serverISON = new ServerISON(this);
708     // get first notify very early
709     startNotifyTimer(1000);
710     // Register with services
711     registerWithServices();
712     // get own ip by userhost
713     requestUserhost(getNickname());
714
715     // Start the PINGPONG match
716     QTimer::singleShot(1000 /*1 sec*/, this, SLOT(sendPing()));
717
718     // Recreate away state if we were set away prior to a reconnect.
719     if (m_away)
720     {
721         // Correct server's beliefs about its away state.
722         m_away = false;
723         requestAway(m_awayReason);
724     }
725 }
726
727 void Server::registerWithServices()
728 {
729     if (getIdentity() && !getIdentity()->getBot().isEmpty()
730         && !getIdentity()->getPassword().isEmpty()
731         && !m_autoIdentifyLock)
732     {
733         queue("PRIVMSG "+getIdentity()->getBot()+" :identify "+getIdentity()->getPassword(), HighPriority);
734
735         m_autoIdentifyLock = true;
736     }
737 }
738
739 //FIXME operator[] inserts an empty T& so each destination might just as well have its own key storage
740 QByteArray Server::getKeyForRecipient(const QString& recipient) const
741 {
742     return m_keyHash[recipient.toLower()];
743 }
744
745 void Server::setKeyForRecipient(const QString& recipient, const QByteArray& key)
746 {
747     m_keyHash[recipient.toLower()] = key;
748 }
749
750 void Server::gotOwnResolvedHostByWelcome(const QHostInfo& res)
751 {
752     if (res.error() == QHostInfo::NoError && !res.addresses().isEmpty())
753         m_ownIpByWelcome = res.addresses().first().toString();
754     else
755         kDebug() << "Got error: " << res.errorString();
756 }
757
758 void Server::quitServer()
759 {
760     // Make clear this is deliberate even if the QUIT never actually goes through the queue
761     // (i.e. this is not redundant with _send_internal()'s updateConnectionState() call for
762     // a QUIT).
763     updateConnectionState(Konversation::SSDeliberatelyDisconnected);
764
765     if (!m_socket) return;
766
767     QString command(Preferences::self()->commandChar()+"QUIT");
768     Konversation::OutputFilterResult result = getOutputFilter()->parse(getNickname(),command, QString());
769     queue(result.toServer, HighPriority);
770
771     flushQueues();
772
773     // Close the socket to allow a dead connection to be reconnected before the socket timeout.
774     m_socket->close();
775
776     getStatusView()->appendServerMessage(i18n("Info"), i18n("Disconnected from %1 (port <numid>%2</numid>).",
777         getConnectionSettings().server().host(),
778         QString::number(getConnectionSettings().server().port())));
779 }
780
781 void Server::notifyAction(const QString& nick)
782 {
783     // parse wildcards (toParse,nickname,channelName,nickList,parameter)
784     QString out = parseWildcards(Preferences::self()->notifyDoubleClickAction(),
785         getNickname(),
786         QString(),
787         QString(),
788         nick,
789         QString());
790
791     // Send all strings, one after another
792     QStringList outList = out.split('\n', QString::SkipEmptyParts);
793     for (int index=0; index<outList.count(); ++index)
794     {
795         Konversation::OutputFilterResult result = getOutputFilter()->parse(getNickname(),outList[index],QString());
796         queue(result.toServer);
797     }                                             // endfor
798 }
799
800 void Server::notifyResponse(const QString& nicksOnline)
801 {
802     bool nicksOnlineChanged = false;
803     QStringList actualList = nicksOnline.split(' ', QString::SkipEmptyParts);
804     QString lcActual = ' ' + nicksOnline + ' ';
805     QString lcPrevISON = ' ' + (m_prevISONList.join(" ")) + ' ';
806
807     QStringList::iterator it;
808
809     //Are any nicks gone offline
810     for (it = m_prevISONList.begin(); it != m_prevISONList.end(); ++it)
811     {
812         if (!lcActual.contains(' ' + (*it) + ' ', Qt::CaseInsensitive))
813         {
814             setNickOffline(*it);
815             nicksOnlineChanged = true;
816         }
817     }
818
819     //Are any nicks gone online
820     for (it = actualList.begin(); it != actualList.end(); ++it)
821     {
822         if (!lcPrevISON.contains(' ' + (*it) + ' ', Qt::CaseInsensitive)) {
823             setWatchedNickOnline(*it);
824             nicksOnlineChanged = true;
825         }
826     }
827
828     // Note: The list emitted in this signal *does* include nicks in joined channels.
829     emit nicksNowOnline(this, actualList, nicksOnlineChanged);
830
831     m_prevISONList = actualList;
832
833     // Next round
834     startNotifyTimer();
835 }
836
837 void Server::startNotifyTimer(int msec)
838 {
839     // make sure the timer gets started properly in case we have reconnected
840     m_notifyTimer.stop();
841
842     if (msec == 0) msec = Preferences::self()->notifyDelay()*1000;
843
844     // start the timer in one shot mode
845     if (Preferences::self()->useNotify())
846         m_notifyTimer.start(msec);
847 }
848
849 void Server::notifyTimeout()
850 {
851     // Notify delay time is over, send ISON request if desired
852     if (Preferences::self()->useNotify())
853     {
854         // But only if there actually are nicks in the notify list
855         QString list = getISONListString();
856
857         if (!list.isEmpty()) queue("ISON "+list, LowPriority);
858
859     }
860 }
861
862 void Server::autoCommandsAndChannels()
863 {
864     if (getServerGroup() && !getServerGroup()->connectCommands().isEmpty())
865     {
866         QString connectCommands = getServerGroup()->connectCommands();
867
868         if (!getNickname().isEmpty())
869             connectCommands.replace("%nick", getNickname());
870
871         QStringList connectCommandsList = connectCommands.split(';', QString::SkipEmptyParts);
872         QStringList::iterator iter;
873
874         for (iter = connectCommandsList.begin(); iter != connectCommandsList.end(); ++iter)
875         {
876             QString output(*iter);
877             output = output.simplified();
878             getOutputFilter()->replaceAliases(output);
879             Konversation::OutputFilterResult result = getOutputFilter()->parse(getNickname(),output,QString());
880             queue(result.toServer);
881         }
882     }
883
884     if (getAutoJoin())
885     {
886         for ( QStringList::Iterator it = m_autoJoinCommands.begin(); it != m_autoJoinCommands.end(); ++it )
887             queue((*it));
888     }
889
890     if (!m_connectionSettings.oneShotChannelList().isEmpty())
891     {
892         QStringList oneShotJoin = generateJoinCommand(m_connectionSettings.oneShotChannelList());
893         for ( QStringList::Iterator it = oneShotJoin.begin(); it != oneShotJoin.end(); ++it )
894         {
895             queue((*it));
896         }
897         m_connectionSettings.clearOneShotChannelList();
898     }
899 }
900
901 /** Create a set of indices into the nickname list of the current identity based on the current nickname.
902  *
903  * The index list is only used if the current nickname is not available. If the nickname is in the identity,
904  * we do not want to retry it. If the nickname is not in the identity, it is considered to be at position -1.
905  */
906 void Server::resetNickSelection()
907 {
908     m_nickIndices.clear();
909     //for equivalence testing in case the identity gets changed underneath us
910     m_referenceNicklist = getIdentity()->getNicknameList();
911     //where in this identities nicklist will we have started?
912     int start = m_referenceNicklist.indexOf(getNickname());
913     int len = m_referenceNicklist.count();
914
915     //we first use this list of indices *after* we've already tried the current nick, which we don't want
916     //to retry if we wrapped, so exclude its index here
917     //if it wasn't in the list, we get -1 back, so then we *want* to include 0
918     for (int i=start+1; i<len; i++)
919         m_nickIndices.append(i);
920     //now, from the beginning of the list, to the item before start
921     for (int i=0; i<start; i++)
922         m_nickIndices.append(i);
923 }
924
925 QString Server::getNextNickname()
926 {
927      //if the identity changed underneath us (likely impossible), start over
928     if (m_referenceNicklist != getIdentity()->getNicknameList())
929         resetNickSelection();
930
931     QString newNick;
932
933     if (!m_nickIndices.isEmpty())
934     {
935         newNick = getIdentity()->getNickname(m_nickIndices.takeFirst());
936     }
937     else
938     {
939         QString inputText = i18n("No nicknames from the \"%1\" identity were accepted by the connection \"%2\".\nPlease enter a new one or press Cancel to disconnect:", getIdentity()->getName(), getDisplayName());
940         newNick = KInputDialog::getText(i18n("Nickname error"), inputText,
941                                         QString(), 0, getStatusView());
942     }
943     return newNick;
944 }
945
946 void Server::processIncomingData()
947 {
948     m_incomingTimer.stop();
949
950     if (!m_inputBuffer.isEmpty() && !m_processingIncoming)
951     {
952         m_processingIncoming = true;
953         QString front(m_inputBuffer.front());
954         m_inputBuffer.pop_front();
955         m_inputFilter.parseLine(front);
956         m_processingIncoming = false;
957
958         if (!m_inputBuffer.isEmpty()) m_incomingTimer.start(0);
959     }
960 }
961
962 void Server::incoming()
963 {
964     //if (getConnectionSettings().server().SSLEnabled())
965     //    emit sslConnected(this);
966
967
968     //if (len <= 0 && getConnectionSettings().server().SSLEnabled())
969     //    return;
970
971     // split buffer to lines
972     QList<QByteArray> bufferLines;
973     while (m_socket->canReadLine())
974     {
975         QByteArray line(m_socket->readLine());
976         //remove \n blowfish doesn't like it
977         int i = line.size()-1;
978         while (i >= 0 && (line[i]=='\n' || line[i]=='\r')) // since euIRC gets away with sending just \r, bet someone sends \n\r?
979         {
980             i--;
981         }
982         line.truncate(i+1);
983
984         if (line.size() > 0)
985             bufferLines.append(line);
986     }
987
988     while(!bufferLines.isEmpty())
989     {
990         // Pre parsing is needed in case encryption/decryption is needed
991         // BEGIN set channel encoding if specified
992         QString senderNick;
993         bool isServerMessage = false;
994         QString channelKey;
995         QTextCodec* codec = getIdentity()->getCodec();
996         QByteArray first = bufferLines.first();
997
998         QStringList lineSplit = codec->toUnicode(first).split(' ', QString::SkipEmptyParts);
999
1000         if( lineSplit.count() >= 1 )
1001         {
1002             if( lineSplit[0][0] == ':' )          // does this message have a prefix?
1003             {
1004                 if( !lineSplit[0].contains('!') ) // is this a server(global) message?
1005                     isServerMessage = true;
1006                 else
1007                     senderNick = lineSplit[0].mid(1, lineSplit[0].indexOf('!')-1);
1008
1009                 lineSplit.removeFirst();          // remove prefix
1010             }
1011         }
1012         else
1013         {
1014             // The line contained only spaces (other than CRLF, removed above)
1015             // and thus there's nothing more we can do with it.
1016             bufferLines.removeFirst();
1017             continue;
1018         }
1019
1020         // BEGIN pre-parse to know where the message belongs to
1021         QString command = lineSplit[0].toLower();
1022         if( isServerMessage )
1023         {
1024             if( lineSplit.count() >= 3 )
1025             {
1026                 if( command == "332" )            // RPL_TOPIC
1027                     channelKey = lineSplit[2];
1028                 if( command == "372" )            // RPL_MOTD
1029                     channelKey = ":server";
1030             }
1031         }
1032         else                                      // NOT a global message
1033         {
1034             if( lineSplit.count() >= 2 )
1035             {
1036                 // query
1037                 if( ( command == "privmsg" ||
1038                     command == "notice"  ) &&
1039                     lineSplit[1] == getNickname() )
1040                 {
1041                     channelKey = senderNick;
1042                 }
1043                 // channel message
1044                 else if( command == "privmsg" ||
1045                     command == "notice"  ||
1046                     command == "join"    ||
1047                     command == "kick"    ||
1048                     command == "part"    ||
1049                     command == "topic"   )
1050                 {
1051                     channelKey = lineSplit[1];
1052                 }
1053             }
1054         }
1055         // END pre-parse to know where the message belongs to
1056         // Decrypt if necessary
1057
1058         //send to raw log before decryption
1059         if(m_rawLog)
1060             m_rawLog->appendRaw("&gt;&gt; " + QString(first).remove(QChar(0xFDD0)).remove(QChar(0xFDD1)).replace('&',"&amp;").replace('<',"&lt;").replace('>',"&gt;").replace(QRegExp("\\s"), "&nbsp;"));
1061
1062         #ifdef HAVE_QCA2
1063         QByteArray cKey = getKeyForRecipient(channelKey);
1064         if(!cKey.isEmpty())
1065         {
1066             if(command == "privmsg")
1067             {
1068                 //only send encrypted text to decrypter
1069                 int index = first.indexOf(":",first.indexOf(":")+1);
1070                 if(this->identifyMsgEnabled()) // Workaround braindead Freenode prefixing messages with +
1071                     ++index;
1072                 QByteArray backup = first.mid(0,index+1);
1073
1074                 if(getChannelByName(channelKey) && getChannelByName(channelKey)->getCipher()->setKey(cKey))
1075                     first = getChannelByName(channelKey)->getCipher()->decrypt(first.mid(index+1));
1076                 else if(getQueryByName(channelKey) && getQueryByName(channelKey)->getCipher()->setKey(cKey))
1077                     first = getQueryByName(channelKey)->getCipher()->decrypt(first.mid(index+1));
1078
1079                 first.prepend(backup);
1080             }
1081             else if(command == "332" || command == "topic")
1082             {
1083                 //only send encrypted text to decrypter
1084                 int index = first.indexOf(":",first.indexOf(":")+1);
1085                 QByteArray backup = first.mid(0,index+1);
1086
1087                 if(getChannelByName(channelKey) && getChannelByName(channelKey)->getCipher()->setKey(cKey))
1088                     first = getChannelByName(channelKey)->getCipher()->decryptTopic(first.mid(index+1));
1089                 else if(getQueryByName(channelKey) && getQueryByName(channelKey)->getCipher()->setKey(cKey))
1090                     first = getQueryByName(channelKey)->getCipher()->decryptTopic(first.mid(index+1));
1091
1092                 first.prepend(backup);
1093             }
1094         }
1095         #endif
1096         bool isUtf8 = Konversation::isUtf8(first);
1097
1098         if (isUtf8)
1099             m_inputBuffer << QString::fromUtf8(first);
1100         else
1101         {
1102             // check setting
1103             QString channelEncoding;
1104             if( !channelKey.isEmpty() )
1105             {
1106                 if(getServerGroup())
1107                     channelEncoding = Preferences::channelEncoding(getServerGroup()->id(), channelKey);
1108                 else
1109                     channelEncoding = Preferences::channelEncoding(getDisplayName(), channelKey);
1110             }
1111             // END set channel encoding if specified
1112
1113             if( !channelEncoding.isEmpty() )
1114                 codec = Konversation::IRCCharsets::self()->codecForName(channelEncoding);
1115
1116             // if channel encoding is utf-8 and the string is definitely not utf-8
1117             // then try latin-1
1118             if (codec->mibEnum() == 106)
1119                 codec = QTextCodec::codecForMib( 4 /* iso-8859-1 */ );
1120
1121             m_inputBuffer << codec->toUnicode(first);
1122         }
1123
1124         bufferLines.removeFirst();
1125
1126         // Qt uses 0xFDD0 and 0xFDD1 to mark the beginning and end of text frames. Remove
1127         // these here to avoid fatal errors encountered in QText* and the event loop pro-
1128         // cessing.
1129         sterilizeUnicode(m_inputBuffer.back());
1130
1131         //FIXME: This has nothing to do with bytes, and it's not raw received bytes either. Bogus number.
1132         //m_bytesReceived+=m_inputBuffer.back().length();
1133     }
1134
1135     if( !m_incomingTimer.isActive() && !m_processingIncoming )
1136         m_incomingTimer.start(0);
1137 }
1138
1139 /** Calculate how long this message premable will be.
1140
1141     This is necessary because the irc server will clip messages so that the
1142     client receives a maximum of 512 bytes at once.
1143 */
1144 int Server::getPreLength(const QString& command, const QString& dest)
1145 {
1146     NickInfoPtr info = getNickInfo(getNickname());
1147     int hostMaskLength = 0;
1148
1149     if(info)
1150         hostMaskLength = info->getHostmask().length();
1151
1152     //:Sho_!i=ehs1@konversation/developer/hein PRIVMSG #konversation :and then back to it
1153
1154     //<colon>$nickname<!>$hostmask<space>$command<space>$destination<space><colon>$message<cr><lf>
1155     int x= 512 - 8 - (m_nickname.length() + hostMaskLength + command.length() + dest.length());
1156
1157     return x;
1158 }
1159
1160 //Commands greater than 1 have localizeable text:         0   1    2       3      4    5    6
1161 static QStringList outcmds = QString("WHO QUIT PRIVMSG NOTICE KICK PART TOPIC").split(QChar(' '));
1162
1163 int Server::_send_internal(QString outputLine)
1164 {
1165     QStringList outputLineSplit = outputLine.split(' ', QString::SkipEmptyParts);
1166
1167     int outboundCommand = -1;
1168     if (!outputLineSplit.isEmpty()) {
1169         //Lets cache the uppercase command so we don't miss or reiterate too much
1170         outboundCommand = outcmds.indexOf(outputLineSplit[0].toUpper());
1171     }
1172
1173     if (outputLine.at(outputLine.length()-1) == '\n')
1174     {
1175         kDebug() << "found \\n on " << outboundCommand;
1176         outputLine.resize(outputLine.length()-1);
1177     }
1178
1179     // remember the first arg of /WHO to identify responses
1180     if (outboundCommand == 0 && outputLineSplit.count() >= 2) //"WHO"
1181         m_inputFilter.addWhoRequest(outputLineSplit[1]);
1182     else if (outboundCommand == 1) //"QUIT"
1183         updateConnectionState(Konversation::SSDeliberatelyDisconnected);
1184
1185     // set channel encoding if specified
1186     QString channelCodecName;
1187
1188     //[ PRIVMSG | NOTICE | KICK | PART | TOPIC ] target :message
1189     if (outputLineSplit.count() > 2 && outboundCommand > 1)
1190     {
1191         if(getServerGroup()) // if we're connecting via a servergroup
1192             channelCodecName=Preferences::channelEncoding(getServerGroup()->id(), outputLineSplit[1]);
1193         else //if we're connecting to a server manually
1194             channelCodecName=Preferences::channelEncoding(getDisplayName(), outputLineSplit[1]);
1195     }
1196     QTextCodec* codec = 0;
1197     if (channelCodecName.isEmpty())
1198         codec = getIdentity()->getCodec();
1199     else
1200         codec = Konversation::IRCCharsets::self()->codecForName(channelCodecName);
1201
1202     // Some codecs don't work with a negative value. This is a bug in Qt 3.
1203     // ex.: JIS7, eucJP, SJIS
1204     //int outlen=-1;
1205
1206     //leaving this done twice for now, I'm uncertain of the implications of not encoding other commands
1207     QByteArray encoded = outputLine.toUtf8();
1208     if(codec)
1209         encoded = codec->fromUnicode(outputLine);
1210
1211     #ifdef HAVE_QCA2
1212     QString cipherKey;
1213     if (outputLineSplit.count() > 2 && outboundCommand > 1)
1214         cipherKey = getKeyForRecipient(outputLineSplit.at(1));
1215     if (!cipherKey.isEmpty())
1216     {
1217         int colon = outputLine.indexOf(':');
1218         if (colon > -1)
1219         {
1220             colon++;
1221
1222             QString pay(outputLine.mid(colon));
1223             //only encode the actual user text, IRCD *should* desire only ASCII 31 < x < 127 for protocol elements
1224             QByteArray payload = pay.toUtf8();
1225
1226             if(codec)
1227                 payload=codec->fromUnicode(pay);
1228             //apparently channel name isn't a protocol element...
1229             QByteArray dest = codec->fromUnicode(outputLineSplit.at(1));
1230
1231             if (outboundCommand == 2 || outboundCommand == 6) // outboundCommand == 3
1232             {
1233                 bool doit = true;
1234                 if (outboundCommand == 2)
1235                 {
1236                     //if its a privmsg and a ctcp but not an action, don't encrypt
1237                     //not interpreting `payload` in case encoding bollixed it
1238                     if (outputLineSplit.at(2).startsWith(QLatin1String(":\x01")) && outputLineSplit.at(2) != ":\x01""ACTION")
1239                         doit = false;
1240                 }
1241                 if (doit)
1242                 {
1243                     QString target = outputLineSplit.at(1);
1244
1245                     if(getChannelByName(target) && getChannelByName(target)->getCipher()->setKey(cipherKey.toLocal8Bit()))
1246                         getChannelByName(target)->getCipher()->encrypt(payload);
1247                     else if(getQueryByName(target) && getQueryByName(target)->getCipher()->setKey(cipherKey.toLocal8Bit()))
1248                         getQueryByName(target)->getCipher()->encrypt(payload);
1249
1250                     encoded = outputLineSplit.at(0).toAscii();
1251                     kDebug() << payload << "\n" << payload.data();
1252                     //two lines because the compiler insists on using the wrong operator+
1253                     encoded += ' ' + dest + " :" + payload;
1254                 }
1255             }
1256         }
1257     }
1258     #endif
1259     encoded += '\n';
1260     qint64 sout = m_socket->write(encoded, encoded.length());
1261
1262     if (m_rawLog)
1263         m_rawLog->appendRaw("&lt;&lt; " + encoded.replace('&',"&amp;").replace('<',"&lt;").replace('>',"&gt;"));
1264
1265     return sout;
1266 }
1267
1268 void Server::toServer(QString&s, IRCQueue* q)
1269 {
1270
1271     int sizesent = _send_internal(s);
1272     emit sentStat(s.length(), sizesent, q); //tell the queues what we sent
1273     //tell everyone else
1274     emit sentStat(s.length(), sizesent);
1275 }
1276
1277 void Server::collectStats(int bytes, int encodedBytes)
1278 {
1279     m_bytesSent += bytes;
1280     m_encodedBytesSent += encodedBytes;
1281     m_linesSent++;
1282 }
1283
1284 bool Server::validQueue(QueuePriority priority)
1285 {
1286    if (priority >=0 && priority <= Application::instance()->countOfQueues())
1287        return true;
1288    return false;
1289 }
1290
1291 bool Server::queue(const QString& line, QueuePriority priority)
1292 {
1293     if (!line.isEmpty() && validQueue(priority))
1294     {
1295         IRCQueue& out=*m_queues[priority];
1296         out.enqueue(line);
1297         return true;
1298     }
1299     return false;
1300 }
1301
1302 bool Server::queueList(const QStringList& buffer, QueuePriority priority)
1303 {
1304     if (buffer.isEmpty() || !validQueue(priority))
1305         return false;
1306
1307     IRCQueue& out=*(m_queues[priority]);
1308
1309     for(int i=0;i<buffer.count();i++)
1310     {
1311         QString line(buffer.at(i));
1312         if (!line.isEmpty())
1313             out.enqueue(line);
1314     }
1315     return true;
1316 }
1317
1318 void Server::resetQueues()
1319 {
1320     for (int i=0; i <= Application::instance()->countOfQueues(); i++)
1321         m_queues[i]->reset();
1322 }
1323
1324 //this could flood you off, but you're leaving anyway...
1325 void Server::flushQueues()
1326 {
1327     int cue;
1328     do
1329     {
1330         cue=-1;
1331         int wait=0;
1332         for (int i=1; i <= Application::instance()->countOfQueues(); i++) //slow queue can rot
1333         {
1334             IRCQueue *queue=m_queues[i];
1335             //higher queue indices have higher priorty, higher queue priority wins tie
1336             if (!queue->isEmpty() && queue->currentWait()>=wait)
1337             {
1338                 cue=i;
1339                 wait=queue->currentWait();
1340             }
1341         }
1342         if (cue>-1)
1343             m_queues[cue]->sendNow();
1344     } while (cue>-1);
1345 }
1346
1347 void Server::closed()
1348 {
1349     broken(m_socket->error());
1350 }
1351
1352 void Server::dbusRaw(const QString& command)
1353 {
1354     if(command.startsWith(Preferences::self()->commandChar()))
1355     {
1356         queue(command.section(Preferences::self()->commandChar(), 1));
1357     }
1358     else
1359         queue(command);
1360 }
1361
1362 void Server::dbusSay(const QString& target,const QString& command)
1363 {
1364     if(isAChannel(target))
1365     {
1366         Channel* channel=getChannelByName(target);
1367         if(channel) channel->sendChannelText(command);
1368     }
1369     else
1370     {
1371         Query* query = getQueryByName(target);
1372         if(query==0)
1373         {
1374             NickInfoPtr nickinfo = obtainNickInfo(target);
1375             query=addQuery(nickinfo, true);
1376         }
1377         if(query)
1378         {
1379             if(!command.isEmpty())
1380                 query->sendQueryText(command);
1381             else
1382             {
1383                 query->adjustFocus();
1384                 getViewContainer()->getWindow()->show();
1385                 KWindowSystem::demandAttention(getViewContainer()->getWindow()->winId());
1386                 KWindowSystem::activateWindow(getViewContainer()->getWindow()->winId());
1387             }
1388         }
1389     }
1390 }
1391
1392 void Server::dbusInfo(const QString& string)
1393 {
1394     appendMessageToFrontmost(i18n("D-Bus"),string);
1395 }
1396
1397 void Server::ctcpReply(const QString &receiver,const QString &text)
1398 {
1399     queue("NOTICE "+receiver+" :"+'\x01'+text+'\x01');
1400 }
1401
1402 // Given a nickname, returns NickInfo object.   0 if not found.
1403 NickInfoPtr Server::getNickInfo(const QString& nickname)
1404 {
1405     QString lcNickname(nickname.toLower());
1406     if (m_allNicks.contains(lcNickname))
1407     {
1408         NickInfoPtr nickinfo = m_allNicks[lcNickname];
1409         Q_ASSERT(nickinfo);
1410         return nickinfo;
1411     }
1412     else
1413         return NickInfoPtr(); //! TODO FIXME null null null
1414 }
1415
1416 // Given a nickname, returns an existing NickInfo object, or creates a new NickInfo object.
1417 // Returns pointer to the found or created NickInfo object.
1418 NickInfoPtr Server::obtainNickInfo(const QString& nickname)
1419 {
1420     NickInfoPtr nickInfo = getNickInfo(nickname);
1421     if (!nickInfo)
1422     {
1423         nickInfo = new NickInfo(nickname, this);
1424         m_allNicks.insert(QString(nickname.toLower()), nickInfo);
1425     }
1426     return nickInfo;
1427 }
1428
1429 const NickInfoMap* Server::getAllNicks() { return &m_allNicks; }
1430
1431 // Returns the list of members for a channel in the joinedChannels list.
1432 // 0 if channel is not in the joinedChannels list.
1433 // Using code must not alter the list.
1434 const ChannelNickMap *Server::getJoinedChannelMembers(const QString& channelName) const
1435 {
1436     QString lcChannelName = channelName.toLower();
1437     if (m_joinedChannels.contains(lcChannelName))
1438         return m_joinedChannels[lcChannelName];
1439     else
1440         return 0;
1441 }
1442
1443 // Returns the list of members for a channel in the unjoinedChannels list.
1444 // 0 if channel is not in the unjoinedChannels list.
1445 // Using code must not alter the list.
1446 const ChannelNickMap *Server::getUnjoinedChannelMembers(const QString& channelName) const
1447 {
1448     QString lcChannelName = channelName.toLower();
1449     if (m_unjoinedChannels.contains(lcChannelName))
1450         return m_unjoinedChannels[lcChannelName];
1451     else
1452         return 0;
1453 }
1454
1455 // Searches the Joined and Unjoined lists for the given channel and returns the member list.
1456 // 0 if channel is not in either list.
1457 // Using code must not alter the list.
1458 const ChannelNickMap *Server::getChannelMembers(const QString& channelName) const
1459 {
1460     const ChannelNickMap *members = getJoinedChannelMembers(channelName);
1461     if (members)
1462         return members;
1463     else
1464         return getUnjoinedChannelMembers(channelName);
1465 }
1466
1467 // Returns pointer to the ChannelNick (mode and pointer to NickInfo) for a given channel and nickname.
1468 // 0 if not found.
1469 ChannelNickPtr Server::getChannelNick(const QString& channelName, const QString& nickname)
1470 {
1471     QString lcNickname = nickname.toLower();
1472     const ChannelNickMap *channelNickMap = getChannelMembers(channelName);
1473     if (channelNickMap)
1474     {
1475         if (channelNickMap->contains(lcNickname))
1476             return (*channelNickMap)[lcNickname];
1477         else
1478             return ChannelNickPtr(); //! TODO FIXME null null null
1479     }
1480     else
1481     {
1482         return ChannelNickPtr(); //! TODO FIXME null null null
1483     }
1484 }
1485
1486 // Updates a nickname in a channel.  If not on the joined or unjoined lists, and nick
1487 // is in the watch list, adds the channel and nick to the unjoinedChannels list.
1488 // If mode != 99, sets the mode for the nick in the channel.
1489 // Returns the NickInfo object if nick is on any lists, otherwise 0.
1490 ChannelNickPtr Server::setChannelNick(const QString& channelName, const QString& nickname, unsigned int mode)
1491 {
1492     QString lcNickname = nickname.toLower();
1493     // If already on a list, update mode.
1494     ChannelNickPtr channelNick = getChannelNick(channelName, lcNickname);
1495     if (!channelNick)
1496     {
1497         // Get watch list from preferences.
1498         QString watchlist=getWatchListString();
1499         // Create a lower case nick list from the watch list.
1500         QStringList watchLowerList = watchlist.toLower().split(' ', QString::SkipEmptyParts);
1501         // If on the watch list, add channel and nick to unjoinedChannels list.
1502         if (watchLowerList.contains(lcNickname))
1503         {
1504             channelNick = addNickToUnjoinedChannelsList(channelName, nickname);
1505             channelNick->setMode(mode);
1506         }
1507         else return ChannelNickPtr(); //! TODO FIXME null null null
1508     }
1509
1510     if (mode != 99) channelNick->setMode(mode);
1511     return channelNick;
1512 }
1513
1514 // Returns a list of all the joined channels that a nick is in.
1515 QStringList Server::getNickJoinedChannels(const QString& nickname)
1516 {
1517     QString lcNickname = nickname.toLower();
1518     QStringList channellist;
1519     ChannelMembershipMap::ConstIterator channel;
1520     for( channel = m_joinedChannels.constBegin(); channel != m_joinedChannels.constEnd(); ++channel )
1521     {
1522         if (channel.value()->contains(lcNickname)) channellist.append(channel.key());
1523     }
1524     return channellist;
1525 }
1526
1527 // Returns a list of all the channels (joined or unjoined) that a nick is in.
1528 QStringList Server::getNickChannels(const QString& nickname)
1529 {
1530     QString lcNickname = nickname.toLower();
1531     QStringList channellist;
1532     ChannelMembershipMap::ConstIterator channel;
1533     for( channel = m_joinedChannels.constBegin(); channel != m_joinedChannels.constEnd(); ++channel )
1534     {
1535         if (channel.value()->contains(lcNickname)) channellist.append(channel.key());
1536     }
1537     for( channel = m_unjoinedChannels.constBegin(); channel != m_unjoinedChannels.constEnd(); ++channel )
1538     {
1539         if (channel.value()->contains(lcNickname)) channellist.append(channel.key());
1540     }
1541     return channellist;
1542 }
1543
1544 bool Server::isNickOnline(const QString &nickname)
1545 {
1546     NickInfoPtr nickInfo = getNickInfo(nickname);
1547     return (!nickInfo.isNull());
1548 }
1549
1550 QString Server::getOwnIpByNetworkInterface()
1551 {
1552     return m_socket->localAddress().toString();
1553 }
1554
1555 QString Server::getOwnIpByServerMessage()
1556 {
1557     if(!m_ownIpByWelcome.isEmpty())
1558         return m_ownIpByWelcome;
1559     else if(!m_ownIpByUserhost.isEmpty())
1560         return m_ownIpByUserhost;
1561     else
1562         return QString();
1563 }
1564
1565 Query* Server::addQuery(const NickInfoPtr & nickInfo, bool weinitiated)
1566 {
1567     QString nickname = nickInfo->getNickname();
1568     // Only create new query object if there isn't already one with the same name
1569     Query* query = getQueryByName(nickname);
1570
1571     if (!query)
1572     {
1573         QString lcNickname = nickname.toLower();
1574         query = getViewContainer()->addQuery(this, nickInfo, weinitiated);
1575
1576         query->indicateAway(m_away);
1577
1578         connect(query, SIGNAL(sendFile(const QString&)),this, SLOT(requestDccSend(const QString&)));
1579         connect(this, SIGNAL(serverOnline(bool)), query, SLOT(serverOnline(bool)));
1580
1581         // Append query to internal list
1582         m_queryList.append(query);
1583
1584         m_queryNicks.insert(lcNickname, nickInfo);
1585
1586         if (!weinitiated)
1587             static_cast<Application*>(kapp)->notificationHandler()->query(query, nickname);
1588     }
1589
1590     // try to get hostmask if there's none yet
1591     if (query->getNickInfo()->getHostmask().isEmpty()) requestUserhost(nickname);
1592
1593     Q_ASSERT(query);
1594
1595     return query;
1596 }
1597
1598 void Server::closeQuery(const QString &name)
1599 {
1600     Query* query = getQueryByName(name);
1601     removeQuery(query);
1602
1603     // Update NickInfo.  If no longer on any lists, delete it altogether, but
1604     // only if not on the watch list.  ISON replies will determine whether the NickInfo
1605     // is deleted altogether in that case.
1606     QString lcNickname = name.toLower();
1607     m_queryNicks.remove(lcNickname);
1608     if (!isWatchedNick(name)) deleteNickIfUnlisted(name);
1609 }
1610
1611 void Server::closeChannel(const QString& name)
1612 {
1613     kDebug() << "Server::closeChannel(" << name << ")";
1614     Channel* channelToClose = getChannelByName(name);
1615
1616     if(channelToClose)
1617     {
1618         Konversation::OutputFilterResult result = getOutputFilter()->parse(getNickname(),
1619             Preferences::self()->commandChar() + "PART", name);
1620         queue(result.toServer);
1621     }
1622 }
1623
1624 void Server::requestChannelList()
1625 {
1626     m_inputFilter.setAutomaticRequest("LIST", QString(), true);
1627     queue(QString("LIST"));
1628 }
1629
1630 void Server::requestWhois(const QString& nickname)
1631 {
1632     m_inputFilter.setAutomaticRequest("WHOIS", nickname, true);
1633     queue("WHOIS "+nickname, LowPriority);
1634 }
1635
1636 void Server::requestWho(const QString& channel)
1637 {
1638     m_inputFilter.setAutomaticRequest("WHO", channel, true);
1639     queue("WHO "+channel, LowPriority);
1640 }
1641
1642 void Server::requestUserhost(const QString& nicks)
1643 {
1644     const QStringList nicksList = nicks.split(' ', QString::SkipEmptyParts);
1645     for(QStringList::ConstIterator it=nicksList.constBegin() ; it!=nicksList.constEnd() ; ++it)
1646         m_inputFilter.setAutomaticRequest("USERHOST", *it, true);
1647     queue("USERHOST "+nicks, LowPriority);
1648 }
1649
1650 void Server::requestTopic(const QString& channel)
1651 {
1652     m_inputFilter.setAutomaticRequest("TOPIC", channel, true);
1653     queue("TOPIC "+channel, LowPriority);
1654 }
1655
1656 void Server::resolveUserhost(const QString& nickname)
1657 {
1658     m_inputFilter.setAutomaticRequest("WHOIS", nickname, true);
1659     m_inputFilter.setAutomaticRequest("DNS", nickname, true);
1660     queue("WHOIS "+nickname, LowPriority); //FIXME when is this really used?
1661 }
1662
1663 void Server::requestBan(const QStringList& users,const QString& channel,const QString& a_option)
1664 {
1665     QString hostmask;
1666     QString option=a_option.toLower();
1667
1668     Channel* targetChannel=getChannelByName(channel);
1669
1670     for(int index=0;index<users.count();index++)
1671     {
1672         // first, set the ban mask to the specified nick
1673         QString mask=users[index];
1674         // did we specify an option?
1675         if(!option.isEmpty())
1676         {
1677             // try to find specified nick on the channel
1678             Nick* targetNick=targetChannel->getNickByName(mask);
1679             // if we found the nick try to find their hostmask
1680             if(targetNick)
1681             {
1682                 QString hostmask=targetNick->getChannelNick()->getHostmask();
1683                 // if we found the hostmask, add it to the ban mask
1684                 if(!hostmask.isEmpty())
1685                 {
1686                     mask=targetNick->getChannelNick()->getNickname()+'!'+hostmask;
1687
1688                     // adapt ban mask to the option given
1689                     if(option=="host")
1690                         mask="*!*@*."+hostmask.section('.',1);
1691                     else if(option=="domain")
1692                         mask="*!*@"+hostmask.section('@',1);
1693                     else if(option=="userhost")
1694                         mask="*!"+hostmask.section('@',0,0)+"@*."+hostmask.section('.',1);
1695                     else if(option=="userdomain")
1696                         mask="*!"+hostmask.section('@',0,0)+'@'+hostmask.section('@',1);
1697                 }
1698             }
1699         }
1700
1701         Konversation::OutputFilterResult result = getOutputFilter()->execBan(mask,channel);
1702         queue(result.toServer);
1703     }
1704 }
1705
1706 void Server::requestUnban(const QString& mask,const QString& channel)
1707 {
1708     Konversation::OutputFilterResult result = getOutputFilter()->execUnban(mask,channel);
1709     queue(result.toServer);
1710 }
1711
1712 void Server::requestDccSend()
1713 {
1714     requestDccSend(QString());
1715 }
1716
1717 void Server::sendURIs(const KUrl::List& uris, const QString& nick)
1718 {
1719     foreach(const KUrl& uri, uris)
1720          addDccSend(nick, uri);
1721 }
1722
1723 void Server::requestDccSend(const QString &a_recipient)
1724 {
1725     QString recipient(a_recipient);
1726     // if we don't have a recipient yet, let the user select one
1727     if (recipient.isEmpty())
1728     {
1729         recipient = recipientNick();
1730     }
1731
1732     // do we have a recipient *now*?
1733     if(!recipient.isEmpty())
1734     {
1735         KUrl::List fileURLs=KFileDialog::getOpenUrls(
1736             KUrl(),
1737             QString(),
1738             getViewContainer()->getWindow(),
1739             i18n("Select File(s) to Send to %1", recipient)
1740         );
1741         KUrl::List::const_iterator it;
1742         for ( it = fileURLs.constBegin() ; it != fileURLs.constEnd() ; ++it )
1743         {
1744             addDccSend( recipient, *it );
1745         }
1746     }
1747 }
1748
1749 void Server::slotNewDccTransferItemQueued(DCC::Transfer* transfer)
1750 {
1751     if (transfer->getConnectionId() == connectionId() )
1752     {
1753         kDebug() << "connecting slots for " << transfer->getFileName() << " [" << transfer->getType() << "]";
1754         if ( transfer->getType() == DCC::Transfer::Receive )
1755         {
1756             connect( transfer, SIGNAL( done( Konversation::DCC::Transfer* ) ), this, SLOT( dccGetDone( Konversation::DCC::Transfer* ) ) );
1757             connect( transfer, SIGNAL( statusChanged( Konversation::DCC::Transfer*, int, int ) ), this, SLOT( dccStatusChanged( Konversation::DCC::Transfer*, int, int ) ) );
1758         }
1759         else
1760         {
1761             connect( transfer, SIGNAL( done( Konversation::DCC::Transfer* ) ), this, SLOT( dccSendDone( Konversation::DCC::Transfer* ) ) );
1762             connect( transfer, SIGNAL( statusChanged( Konversation::DCC::Transfer*, int, int ) ), this, SLOT( dccStatusChanged( Konversation::DCC::Transfer*, int, int ) ) );
1763         }
1764     }
1765 }
1766
1767 void Server::addDccSend(const QString &recipient,KUrl fileURL, const QString &altFileName, quint64 fileSize)
1768 {
1769     if (!fileURL.isValid())
1770     {
1771         return;
1772     }
1773
1774     // We already checked that the file exists in output filter / requestDccSend() resp.
1775     DCC::TransferSend* newDcc = Application::instance()->getDccTransferManager()->newUpload();
1776
1777     newDcc->setConnectionId(connectionId());
1778
1779     newDcc->setPartnerNick(recipient);
1780     newDcc->setFileURL(fileURL);
1781     if (!altFileName.isEmpty())
1782         newDcc->setFileName(altFileName);
1783     if (fileSize != 0)
1784         newDcc->setFileSize(fileSize);
1785
1786     emit addDccPanel();
1787
1788     if (newDcc->queue())
1789         newDcc->start();
1790 }
1791
1792 QString Server::recoverDccFileName(const QStringList & dccArguments, int offset) const
1793 {
1794     QString fileName;
1795     if(dccArguments.count() > offset + 1)
1796     {
1797         kDebug() << "recover filename";
1798         const int argumentOffsetSize = dccArguments.size() - offset;
1799         for (int i = 0; i < argumentOffsetSize; ++i)
1800         {
1801             fileName += dccArguments.at(i);
1802             //if not last element, append a space
1803             if (i < (argumentOffsetSize - 1))
1804             {
1805                 fileName += ' ';
1806             }
1807         }
1808     }
1809     else
1810     {
1811         fileName = dccArguments.at(0);
1812     }
1813
1814     return cleanDccFileName(fileName);
1815 }
1816
1817 QString Server::cleanDccFileName(const QString& filename) const
1818 {
1819     QString cleanFileName = filename;
1820
1821     //we want a clean filename to get rid of the mass """filename"""
1822     //NOTE: if a filename starts really with a ", it is escaped -> \" (2 chars)
1823     //      but most clients don't support that and just replace it with a _
1824     while (cleanFileName.startsWith('\"') && cleanFileName.endsWith('\"'))
1825     {
1826         cleanFileName = cleanFileName.mid(1, cleanFileName.length() - 2);
1827     }
1828
1829     return cleanFileName;
1830 }
1831
1832 quint16 Server::stringToPort(const QString &port, bool *ok)
1833 {
1834     bool toUintOk = false;
1835     uint uPort32 = port.toUInt(&toUintOk);
1836     // ports over 65535 are invalid, someone sends us bad data
1837     if (!toUintOk || uPort32 > USHRT_MAX)
1838     {
1839         if (ok)
1840         {
1841             *ok = false;
1842         }
1843     }
1844     else
1845     {
1846         if (ok)
1847         {
1848             *ok = true;
1849         }
1850     }
1851     return (quint16)uPort32;
1852 }
1853
1854 QString Server::recipientNick() const
1855 {
1856     QStringList nickList;
1857
1858     // fill nickList with all nicks we know about
1859     foreach (Channel* lookChannel, m_channelList)
1860     {
1861         foreach (Nick* lookNick, lookChannel->getNickList())
1862         {
1863             if (!nickList.contains(lookNick->getChannelNick()->getNickname()))
1864                 nickList.append(lookNick->getChannelNick()->getNickname());
1865         }
1866     }
1867
1868     // add Queries as well, but don't insert duplicates
1869     foreach (Query* lookQuery, m_queryList)
1870     {
1871         if(!nickList.contains(lookQuery->getName())) nickList.append(lookQuery->getName());
1872     }
1873     QStringListModel model;
1874     model.setStringList(nickList);
1875     return DCC::RecipientDialog::getNickname(getViewContainer()->getWindow(), &model);
1876 }
1877
1878 void Server::addDccGet(const QString &sourceNick, const QStringList &dccArguments)
1879 {
1880     //filename ip port filesize [token]
1881     QString ip;
1882     quint16 port;
1883     QString fileName;
1884     quint64 fileSize;
1885     QString token;
1886     const int argumentSize = dccArguments.count();
1887     bool ok = true;
1888
1889     if (dccArguments.at(argumentSize - 3) == "0") //port==0, for passive send, ip can't be 0
1890     {
1891         //filename ip port(0) filesize token
1892         fileName = recoverDccFileName(dccArguments, 4); //ip port filesize token
1893         ip = DCC::DccCommon::numericalIpToTextIp( dccArguments.at(argumentSize - 4) ); //-1 index, -1 token, -1 port, -1 filesize
1894         port = 0;
1895         fileSize = dccArguments.at(argumentSize - 2).toULongLong(); //-1 index, -1 token
1896         token = dccArguments.at(argumentSize - 1); //-1 index
1897     }
1898     else
1899     {
1900         //filename ip port filesize
1901         ip = DCC::DccCommon::numericalIpToTextIp( dccArguments.at(argumentSize - 3) ); //-1 index, -1 filesize
1902         fileName = recoverDccFileName(dccArguments, 3); //ip port filesize
1903         fileSize = dccArguments.at(argumentSize - 1).toULongLong(); //-1 index
1904         port = stringToPort(dccArguments.at(argumentSize - 2), &ok); //-1 index, -1 filesize
1905     }
1906
1907     if (!ok)
1908     {
1909         appendMessageToFrontmost(i18n("Error"),
1910                                  i18nc("%1=nickname","Received invalid DCC SEND request from %1.",
1911                                        sourceNick));
1912         return;
1913     }
1914
1915     DCC::TransferRecv* newDcc = Application::instance()->getDccTransferManager()->newDownload();
1916
1917     newDcc->setConnectionId(connectionId());
1918     newDcc->setPartnerNick(sourceNick);
1919
1920     newDcc->setPartnerIp(ip);
1921     newDcc->setPartnerPort(port);
1922     newDcc->setFileName(fileName);
1923     newDcc->setFileSize(fileSize);
1924     // Reverse DCC
1925     if (!token.isEmpty())
1926     {
1927         newDcc->setReverse(true, token);
1928     }
1929
1930     kDebug() << "ip: " << ip;
1931     kDebug() << "port: " << port;
1932     kDebug() << "filename: " << fileName;
1933     kDebug() << "filesize: " << fileSize;
1934     kDebug() << "token: " << token;
1935
1936     //emit after data was set
1937     emit addDccPanel();
1938
1939     if ( newDcc->queue() )
1940     {
1941         appendMessageToFrontmost( i18n( "DCC" ),
1942                                   i18n( "%1 offers to send you \"%2\" (%3)...",
1943                                         newDcc->getPartnerNick(),
1944                                         fileName,
1945                                         ( newDcc->getFileSize() == 0 ) ? i18n( "unknown size" ) : KIO::convertSize( newDcc->getFileSize() ) ) );
1946
1947         if (Preferences::self()->dccAutoGet())
1948             newDcc->start();
1949     }
1950 }
1951
1952 void Server::addDccChat(const QString& sourceNick, const QStringList& dccArguments)
1953 {
1954     //chat ip port [token]
1955     QString ip;
1956     quint16 port = 0;
1957     QString token;
1958     bool reverse = false;
1959     const int argumentSize = dccArguments.count();
1960     bool ok = true;
1961     QString extension;
1962
1963     extension = dccArguments.at(0);
1964     ip = DCC::DccCommon::numericalIpToTextIp(dccArguments.at(1));
1965
1966     if (argumentSize == 3)
1967     {
1968         //extension ip port
1969         port = stringToPort(dccArguments.at(2), &ok);
1970     }
1971     else if (argumentSize == 4)
1972     {
1973         //extension ip port(0) token
1974         token = dccArguments.at(3);
1975         reverse = true;
1976     }
1977
1978     if (!ok)
1979     {
1980         appendMessageToFrontmost(i18n("Error"),
1981                                  i18nc("%1=nickname","Received invalid DCC CHAT request from %1.",
1982                                        sourceNick));
1983         return;
1984     }
1985
1986     DCC::Chat* newChat = Application::instance()->getDccTransferManager()->newChat();
1987
1988     newChat->setConnectionId(connectionId());
1989     newChat->setPartnerNick(sourceNick);
1990     newChat->setOwnNick(getNickname());
1991
1992     kDebug() << "ip: " << ip;
1993     kDebug() << "port: " << port;
1994     kDebug() << "token: " << token;
1995     kDebug() << "extension: " << extension;
1996
1997     newChat->setPartnerIp(ip);
1998     newChat->setPartnerPort(port);
1999     newChat->setReverse(reverse, token);
2000     newChat->setSelfOpened(false);
2001     newChat->setExtension(extension);
2002
2003     emit addDccChat(newChat);
2004     newChat->start();
2005 }
2006
2007 void Server::openDccChat(const QString& nickname)
2008 {
2009     kDebug();
2010     QString recipient(nickname);
2011     // if we don't have a recipient yet, let the user select one
2012     if (recipient.isEmpty())
2013     {
2014         recipient = recipientNick();
2015     }
2016
2017     // do we have a recipient *now*?
2018     if (!recipient.isEmpty())
2019     {
2020         DCC::Chat* newChat = Application::instance()->getDccTransferManager()->newChat();
2021         newChat->setConnectionId(connectionId());
2022         newChat->setPartnerNick(recipient);
2023         newChat->setOwnNick(getNickname());
2024         newChat->setSelfOpened(true);
2025         emit addDccChat(newChat);
2026         newChat->start();
2027     }
2028 }
2029
2030 void Server::openDccWBoard(const QString& nickname)
2031 {
2032     kDebug();
2033     QString recipient(nickname);
2034     // if we don't have a recipient yet, let the user select one
2035     if (recipient.isEmpty())
2036     {
2037         recipient = recipientNick();
2038     }
2039
2040     // do we have a recipient *now*?
2041     if (!recipient.isEmpty())
2042     {
2043         DCC::Chat* newChat = Application::instance()->getDccTransferManager()->newChat();
2044         newChat->setConnectionId(connectionId());
2045         newChat->setPartnerNick(recipient);
2046         newChat->setOwnNick(getNickname());
2047         // Set extension before emiting addDccChat
2048         newChat->setExtension(DCC::Chat::Whiteboard);
2049         newChat->setSelfOpened(true);
2050         emit addDccChat(newChat);
2051         newChat->start();
2052     }
2053 }
2054
2055 void Server::requestDccChat(const QString& partnerNick, const QString& extension, const QString& numericalOwnIp, quint16 ownPort)
2056 {
2057     Konversation::OutputFilterResult result = getOutputFilter()->requestDccChat(partnerNick, extension, numericalOwnIp,ownPort);
2058     queue(result.toServer);
2059 }
2060
2061 void Server::acceptDccGet(const QString& nick, const QString& file)
2062 {
2063     Application::instance()->getDccTransferManager()->acceptDccGet(m_connectionId, nick, file);
2064 }
2065
2066 void Server::dccSendRequest(const QString &partner, const QString &fileName, const QString &address, quint16 port, quint64 size)
2067 {
2068     Konversation::OutputFilterResult result = getOutputFilter()->sendRequest(partner,fileName,address,port,size);
2069     queue(result.toServer);
2070
2071     appendMessageToFrontmost( i18n( "DCC" ),
2072                               i18n( "Asking %1 to accept upload of \"%2\" (%3)...",
2073                                     partner,
2074                                     cleanDccFileName(fileName),
2075                                     ( size == 0 ) ? i18n( "unknown size" ) : KIO::convertSize( size ) ) );
2076 }
2077
2078 void Server::dccPassiveSendRequest(const QString& recipient,const QString& fileName,const QString& address,quint64 size,const QString& token)
2079 {
2080     Konversation::OutputFilterResult result = getOutputFilter()->passiveSendRequest(recipient,fileName,address,size,token);
2081     queue(result.toServer);
2082
2083     appendMessageToFrontmost( i18n( "DCC" ),
2084                               i18n( "Asking %1 to accept passive upload of \"%2\" (%3)...",
2085                                     recipient,
2086                                     cleanDccFileName(fileName),
2087                                     ( size == 0 ) ? i18n( "unknown size" ) : KIO::convertSize( size ) ) );
2088 }
2089
2090 void Server::dccPassiveChatRequest(const QString& recipient, const QString& extension, const QString& address, const QString& token)
2091 {
2092     Konversation::OutputFilterResult result = getOutputFilter()->passiveChatRequest(recipient, extension, address, token);
2093     queue(result.toServer);
2094
2095     appendMessageToFrontmost(i18n("DCC"),
2096                              i18nc("%1=name, %2=dcc extension, chat or wboard for example","Asking %1 to accept %2...", recipient, extension));
2097 }
2098
2099 void Server::dccPassiveResumeGetRequest(const QString& sender,const QString& fileName,quint16 port,KIO::filesize_t startAt,const QString &token)
2100 {
2101     Konversation::OutputFilterResult result = getOutputFilter()->resumePassiveRequest(sender,fileName,port,startAt,token);;
2102     queue(result.toServer);
2103 }
2104
2105 void Server::dccResumeGetRequest(const QString &sender, const QString &fileName, quint16 port, KIO::filesize_t startAt)
2106 {
2107     Konversation::OutputFilterResult result = getOutputFilter()->resumeRequest(sender,fileName,port,startAt);;
2108     queue(result.toServer);
2109 }
2110
2111 void Server::dccReverseSendAck(const QString& partnerNick,const QString& fileName,const QString& ownAddress,quint16 ownPort,quint64 size,const QString& reverseToken)
2112 {
2113     Konversation::OutputFilterResult result = getOutputFilter()->acceptPassiveSendRequest(partnerNick,fileName,ownAddress,ownPort,size,reverseToken);
2114     queue(result.toServer);
2115 }
2116
2117 void Server::dccReverseChatAck(const QString& partnerNick, const QString& extension, const QString& ownAddress, quint16 ownPort, const QString& reverseToken)
2118 {
2119     Konversation::OutputFilterResult result = getOutputFilter()->acceptPassiveChatRequest(partnerNick, extension, ownAddress, ownPort, reverseToken);
2120     queue(result.toServer);
2121 }
2122
2123 void Server::dccRejectSend(const QString& partnerNick, const QString& fileName)
2124 {
2125     Konversation::OutputFilterResult result = getOutputFilter()->rejectDccSend(partnerNick,fileName);
2126     queue(result.toServer);
2127 }
2128
2129 void Server::dccRejectChat(const QString& partnerNick, const QString& extension)
2130 {
2131     Konversation::OutputFilterResult result = getOutputFilter()->rejectDccChat(partnerNick, extension);
2132     queue(result.toServer);
2133 }
2134
2135 void Server::startReverseDccChat(const QString &sourceNick, const QStringList &dccArguments)
2136 {
2137     kDebug();
2138     DCC::TransferManager* dtm = Application::instance()->getDccTransferManager();
2139
2140     bool ok = true;
2141     QString partnerIP = DCC::DccCommon::numericalIpToTextIp(dccArguments.at(1));
2142     quint16 port = stringToPort(dccArguments.at(2), &ok);
2143     QString token = dccArguments.at(3);
2144
2145     kDebug() << "ip: " << partnerIP;
2146     kDebug() << "port: " << port;
2147     kDebug() << "token: " << token;
2148
2149     if (!ok || dtm->startReverseChat(connectionId(), sourceNick,
2150                                     partnerIP, port, token) == 0)
2151     {
2152         // DTM could not find a matched item
2153         appendMessageToFrontmost(i18n("Error"),
2154                                  i18nc("%1 = nickname",
2155                                        "Received invalid passive DCC chat acceptance message from %1.",
2156                                        sourceNick));
2157     }
2158 }
2159
2160 void Server::startReverseDccSendTransfer(const QString& sourceNick,const QStringList& dccArguments)
2161 {
2162     kDebug();
2163     DCC::TransferManager* dtm = Application::instance()->getDccTransferManager();
2164
2165     bool ok = true;
2166     const int argumentSize = dccArguments.size();
2167     QString partnerIP = DCC::DccCommon::numericalIpToTextIp( dccArguments.at(argumentSize - 4) ); //dccArguments[1] ) );
2168     quint16 port = stringToPort(dccArguments.at(argumentSize - 3), &ok);
2169     QString token = dccArguments.at(argumentSize - 1);
2170     quint64 fileSize = dccArguments.at(argumentSize - 2).toULongLong();
2171     QString fileName = recoverDccFileName(dccArguments, 4); //ip port filesize token
2172
2173     kDebug() << "ip: " << partnerIP;
2174     kDebug() << "port: " << port;
2175     kDebug() << "filename: " << fileName;
2176     kDebug() << "filesize: " << fileSize;
2177     kDebug() << "token: " << token;
2178
2179     if (!ok ||
2180         dtm->startReverseSending(connectionId(), sourceNick,
2181                                  fileName,  // filename
2182                                  partnerIP,  // partner IP
2183                                  port,  // partner port
2184                                  fileSize,  // filesize
2185                                  token  // Reverse DCC token
2186          ) == 0)
2187     {
2188         // DTM could not find a matched item
2189         appendMessageToFrontmost(i18n("Error"),
2190                                  i18nc("%1 = file name, %2 = nickname",
2191                                        "Received invalid passive DCC send acceptance message for \"%1\" from %2.",
2192                                        fileName,
2193                                        sourceNick));
2194     }
2195 }
2196
2197 void Server::resumeDccGetTransfer(const QString &sourceNick, const QStringList &dccArguments)
2198 {
2199     DCC::TransferManager* dtm = Application::instance()->getDccTransferManager();
2200
2201     //filename port position [token]
2202     QString fileName;
2203     quint64 position;
2204     quint16 ownPort;
2205     bool ok = true;
2206     const int argumentSize = dccArguments.count();
2207
2208     if (dccArguments.at(argumentSize - 3) == "0") //-1 index, -1 token, -1 pos
2209     {
2210         fileName = recoverDccFileName(dccArguments, 3); //port position token
2211         ownPort = 0;
2212         position = dccArguments.at(argumentSize - 2).toULongLong(); //-1 index, -1 token
2213     }
2214     else
2215     {
2216         fileName = recoverDccFileName(dccArguments, 2); //port position
2217         ownPort = stringToPort(dccArguments.at(argumentSize - 2), &ok); //-1 index, -1 pos
2218         position = dccArguments.at(argumentSize - 1).toULongLong(); //-1 index
2219     }
2220     //do we need the token here?
2221
2222     DCC::TransferRecv* dccTransfer = 0;
2223     if (ok)
2224     {
2225         dccTransfer = dtm->resumeDownload(connectionId(), sourceNick, fileName, ownPort, position);
2226     }
2227
2228     if (dccTransfer)
2229     {
2230         appendMessageToFrontmost(i18n("DCC"),
2231                                  i18nc("%1 = file name, %2 = nickname of sender, %3 = percentage of file size, %4 = file size",
2232                                        "Resuming download of \"%1\" from %2 starting at %3% of %4...",
2233                                        fileName,
2234                                        sourceNick,
2235                                        QString::number( dccTransfer->getProgress()),
2236                                        (dccTransfer->getFileSize() == 0) ? i18n("unknown size") : KIO::convertSize(dccTransfer->getFileSize())));
2237     }
2238     else
2239     {
2240         appendMessageToFrontmost(i18n("Error"),
2241                                  i18nc("%1 = file name, %2 = nickname",
2242                                        "Received invalid resume acceptance message for \"%1\" from %2.",
2243                                        fileName,
2244                                        sourceNick));
2245     }
2246 }
2247
2248 void Server::resumeDccSendTransfer(const QString &sourceNick, const QStringList &dccArguments)
2249 {
2250     DCC::TransferManager* dtm = Application::instance()->getDccTransferManager();
2251
2252     bool passiv = false;
2253     QString fileName;
2254     quint64 position;
2255     QString token;
2256     quint16 ownPort;
2257     bool ok = true;
2258     const int argumentSize = dccArguments.count();
2259
2260     //filename port filepos [token]
2261     if (dccArguments.at( argumentSize - 3) == "0")
2262     {
2263         //filename port filepos token
2264         passiv = true;
2265         ownPort = 0;
2266         token = dccArguments.at( argumentSize - 1); // -1 index
2267         position = dccArguments.at( argumentSize - 2).toULongLong(); // -1 index, -1 token
2268         fileName = recoverDccFileName(dccArguments, 3); //port filepos token
2269     }
2270     else
2271     {
2272         //filename port filepos
2273         ownPort = stringToPort(dccArguments.at(argumentSize - 2), &ok); //-1 index, -1 filesize
2274         position = dccArguments.at( argumentSize - 1).toULongLong(); // -1 index
2275         fileName = recoverDccFileName(dccArguments, 2); //port filepos
2276     }
2277
2278     DCC::TransferSend* dccTransfer = 0;
2279     if (ok)
2280     {
2281         dccTransfer = dtm->resumeUpload(connectionId(), sourceNick, fileName, ownPort, position);
2282     }
2283
2284     if (dccTransfer)
2285     {
2286         appendMessageToFrontmost(i18n("DCC"),
2287                                  i18nc("%1 = file name, %2 = nickname of recipient, %3 = percentage of file size, %4 = file size",
2288                                        "Resuming upload of \"%1\" to %2 starting at %3% of %4...",
2289                                        fileName,
2290                                        sourceNick,
2291                                        QString::number(dccTransfer->getProgress()),
2292                                        (dccTransfer->getFileSize() == 0) ? i18n("unknown size") : KIO::convertSize(dccTransfer->getFileSize())));
2293
2294         // fileName can't have " here
2295         if (fileName.contains(' '))
2296             fileName = '\"'+fileName+'\"';
2297
2298         // FIXME: this operation should be done by TransferManager
2299         Konversation::OutputFilterResult result;
2300         if (passiv)
2301             result = getOutputFilter()->acceptPassiveResumeRequest( sourceNick, fileName, ownPort, position, token );
2302         else
2303             result = getOutputFilter()->acceptResumeRequest( sourceNick, fileName, ownPort, position );
2304         queue( result.toServer );
2305     }
2306     else
2307     {
2308         appendMessageToFrontmost(i18n("Error"),
2309                                  i18nc("%1 = file name, %2 = nickname",
2310                                        "Received invalid resume request for \"%1\" from %2.",
2311                                        fileName,
2312                                        sourceNick));
2313     }
2314 }
2315
2316 void Server::rejectDccSendTransfer(const QString &sourceNick, const QStringList &dccArguments)
2317 {
2318     DCC::TransferManager* dtm = Application::instance()->getDccTransferManager();
2319
2320     //filename
2321     QString fileName = recoverDccFileName(dccArguments, 0);
2322
2323     DCC::TransferSend* dccTransfer = dtm->rejectSend(connectionId(), sourceNick, fileName);
2324
2325     if (!dccTransfer)
2326     {
2327         appendMessageToFrontmost(i18n("Error"),
2328                                  i18nc("%1 = file name, %2 = nickname",
2329                                        "Received invalid reject request for \"%1\" from %2.",
2330                                        fileName,
2331                                        sourceNick));
2332     }
2333 }
2334
2335 void Server::rejectDccChat(const QString& sourceNick)
2336 {
2337     DCC::TransferManager* dtm = Application::instance()->getDccTransferManager();
2338
2339     DCC::Chat* dccChat = dtm->rejectChat(connectionId(), sourceNick);
2340
2341     if (!dccChat)
2342     {
2343         appendMessageToFrontmost(i18n("Error"),
2344                                  i18nc("%1 = nickname",
2345                                        "Received invalid reject request from %1.",
2346                                        sourceNick));
2347     }
2348 }
2349
2350 void Server::dccGetDone(DCC::Transfer* item)
2351 {
2352     if (!item)
2353         return;
2354
2355     if(item->getStatus() == DCC::Transfer::Done)
2356     {
2357         appendMessageToFrontmost(i18n("DCC"), i18nc("%1 = file name, %2 = nickname of sender",
2358             "Download of \"%1\" from %2 finished.", item->getFileName(), item->getPartnerNick()));
2359     }
2360     else if(item->getStatus() == DCC::Transfer::Failed)
2361     {
2362         appendMessageToFrontmost(i18n("DCC"), i18nc("%1 = file name, %2 = nickname of sender",
2363             "Download of \"%1\" from %2 failed. Reason: %3.", item->getFileName(),
2364             item->getPartnerNick(), item->getStatusDetail()));
2365     }
2366 }
2367
2368 void Server::dccSendDone(DCC::Transfer* item)
2369 {
2370     if (!item)
2371         return;
2372
2373     if(item->getStatus() == DCC::Transfer::Done)
2374         appendMessageToFrontmost(i18n("DCC"), i18nc("%1 = file name, %2 = nickname of recipient",
2375             "Upload of \"%1\" to %2 finished.", item->getFileName(), item->getPartnerNick()));
2376     else if(item->getStatus() == DCC::Transfer::Failed)
2377         appendMessageToFrontmost(i18n("DCC"), i18nc("%1 = file name, %2 = nickname of recipient",
2378             "Upload of \"%1\" to %2 failed. Reason: %3.", item->getFileName(), item->getPartnerNick(),
2379             item->getStatusDetail()));
2380 }
2381
2382 void Server::dccStatusChanged(DCC::Transfer *item, int newStatus, int oldStatus)
2383 {
2384     if(!item)
2385         return;
2386
2387     if ( item->getType() == DCC::Transfer::Send )
2388     {
2389         // when resuming, a message about the receiver's acceptance has been shown already, so suppress this message
2390         if ( newStatus == DCC::Transfer::Transferring && oldStatus == DCC::Transfer::WaitingRemote && !item->isResumed() )
2391             appendMessageToFrontmost( i18n( "DCC" ), i18nc( "%1 = file name, %2 nickname of recipient",
2392                 "Sending \"%1\" to %2...", item->getFileName(), item->getPartnerNick() ) );
2393     }
2394     else  // type == Receive
2395     {
2396         if ( newStatus == DCC::Transfer::Transferring && !item->isResumed() )
2397         {
2398             appendMessageToFrontmost( i18n( "DCC" ),
2399                                         i18nc( "%1 = file name, %2 = file size, %3 = nickname of sender", "Downloading \"%1\" (%2) from %3...",
2400                                               item->getFileName(),
2401                                             ( item->getFileSize() == 0 ) ? i18n( "unknown size" ) : KIO::convertSize( item->getFileSize() ),
2402                                             item->getPartnerNick() ) );
2403         }
2404     }
2405 }
2406
2407 void Server::removeQuery(Query* query)
2408 {
2409     m_queryList.removeOne(query);
2410     query->deleteLater();
2411 }
2412
2413 void Server::sendJoinCommand(const QString& name, const QString& password)
2414 {
2415     Konversation::OutputFilterResult result = getOutputFilter()->parse(getNickname(),
2416         Preferences::self()->commandChar() + "JOIN " + name + ' ' + password, QString());
2417     queue(result.toServer);
2418 }
2419
2420 void Server::joinChannel(const QString& name, const QString& hostmask)
2421 {
2422     // (re-)join channel, open a new panel if needed
2423     Channel* channel = getChannelByName(name);
2424
2425     if (!channel)
2426     {
2427         channel=getViewContainer()->addChannel(this,name);
2428         Q_ASSERT(channel);
2429         channel->setNickname(getNickname());
2430         channel->indicateAway(m_away);
2431
2432         if (getServerGroup())
2433         {
2434             Konversation::ChannelSettings channelSettings = getServerGroup()->channelByNameFromHistory(name);
2435             channel->setNotificationsEnabled(channelSettings.enableNotifications());
2436             getServerGroup()->appendChannelHistory(channelSettings);
2437         }
2438
2439         m_channelList.append(channel);
2440
2441         connect(channel,SIGNAL (sendFile()),this,SLOT (requestDccSend()) );
2442         connect(this, SIGNAL(nicknameChanged(const QString&)), channel, SLOT(setNickname(const QString&)));
2443     }
2444     // Move channel from unjoined (if present) to joined list and add our own nickname to the joined list.
2445     ChannelNickPtr channelNick = addNickToJoinedChannelsList(name, getNickname());
2446
2447     if ((channelNick->getHostmask() != hostmask ) && !hostmask.isEmpty())
2448     {
2449         NickInfoPtr nickInfo = channelNick->getNickInfo();
2450         nickInfo->setHostmask(hostmask);
2451     }
2452
2453     channel->joinNickname(channelNick);
2454 }
2455
2456 void Server::removeChannel(Channel* channel)
2457 {
2458     // Update NickInfo.
2459     removeJoinedChannel(channel->getName());
2460
2461     if (getServerGroup())
2462     {
2463         Konversation::ChannelSettings channelSettings = getServerGroup()->channelByNameFromHistory(channel->getName());
2464         channelSettings.setNotificationsEnabled(channel->notificationsEnabled());
2465         getServerGroup()->appendChannelHistory(channelSettings);
2466     }
2467
2468     m_channelList.removeOne(channel);
2469 }
2470
2471 void Server::updateChannelMode(const QString &updater, const QString &channelName, char mode, bool plus, const QString &parameter)
2472 {
2473
2474     Channel* channel=getChannelByName(channelName);
2475
2476     if(channel)                                   //Let the channel be verbose to the screen about the change, and update channelNick
2477         channel->updateMode(updater, mode, plus, parameter);
2478     // TODO: What is mode character for owner?
2479     // Answer from JOHNFLUX - I think that admin is the same as owner.  Channel.h has owner as "a"
2480     // "q" is the likely answer.. UnrealIRCd and euIRCd use it.
2481     // TODO these need to become dynamic
2482     QString userModes="vhoqa";                    // voice halfop op owner admin
2483     int modePos = userModes.indexOf(mode);
2484     if (modePos > 0)
2485     {
2486         ChannelNickPtr updateeNick = getChannelNick(channelName, parameter);
2487         if(!updateeNick)
2488         {
2489 /*
2490             if(parameter.isEmpty())
2491             {
2492                 kDebug() << "in updateChannelMode, a nick with no-name has had their mode '" << mode << "' changed to (" <<plus << ") in channel '" << channelName << "' by " << updater << ".  How this happened, I have no idea.  Please report this message to irc #konversation if you want to be helpful." << endl << "Ignoring the error and continuing.";
2493                                                   //this will get their attention.
2494                 kDebug() << kBacktrace();
2495             }
2496             else
2497             {
2498                 kDebug() << "in updateChannelMode, could not find updatee nick " << parameter << " for channel " << channelName;
2499                 kDebug() << "This could indicate an obscure race condition that is safely being handled (like the mode of someone changed and they quit almost simulatanously, or it could indicate an internal error.";
2500             }
2501 */
2502             //TODO Do we need to add this nick?
2503             return;
2504         }
2505
2506         updateeNick->setMode(mode, plus);
2507
2508         // Note that channel will be moved to joined list if necessary.
2509         addNickToJoinedChannelsList(channelName, parameter);
2510     }
2511
2512     // Update channel ban list.
2513     if (mode == 'b')
2514     {
2515         if (plus)
2516         {
2517             QDateTime when;
2518             addBan(channelName, QString("%1 %2 %3").arg(parameter).arg(updater).arg(QDateTime::currentDateTime().toTime_t()));
2519         } else {
2520             removeBan(channelName, parameter);
2521         }
2522     }
2523 }
2524
2525 void Server::updateChannelModeWidgets(const QString &channelName, char mode, const QString &parameter)
2526 {
2527     Channel* channel=getChannelByName(channelName);
2528     if(channel) channel->updateModeWidgets(mode,true,parameter);
2529 }
2530
2531 void Server::updateChannelQuickButtons()
2532 {
2533     foreach (Channel* channel, m_channelList)
2534     {
2535         channel->updateQuickButtons(Preferences::quickButtonList());
2536     }
2537 }
2538
2539 Channel* Server::getChannelByName(const QString& name)
2540 {
2541     // Convert wanted channel name to lowercase
2542     QString wanted = name.toLower();
2543
2544     // Traverse through list to find the channel named "name"
2545     foreach (Channel* lookChannel, m_channelList)
2546     {
2547         if (lookChannel->getName().toLower()==wanted) return lookChannel;
2548     }
2549     // No channel by that name found? Return 0. Happens on first channel join
2550     return 0;
2551 }
2552
2553 Query* Server::getQueryByName(const QString& name)
2554 {
2555     // Convert wanted query name to lowercase
2556     QString wanted = name.toLower();
2557
2558     // Traverse through list to find the query with "name"
2559     foreach (Query* lookQuery, m_queryList)
2560     {
2561         if(lookQuery->getName().toLower()==wanted) return lookQuery;
2562     }
2563     // No query by that name found? Must be a new query request. Return 0
2564     return 0;
2565 }
2566
2567 void Server::resetNickList(const QString& channelName)
2568 {
2569     Channel* outChannel=getChannelByName(channelName);
2570     if(outChannel) outChannel->resetNickList();
2571 }
2572
2573 void Server::addPendingNickList(const QString& channelName,const QStringList& nickList)
2574 {
2575     Channel* outChannel=getChannelByName(channelName);
2576     if(outChannel) outChannel->addPendingNickList(nickList);
2577 }
2578
2579 // Adds a nickname to the joinedChannels list.
2580 // Creates new NickInfo if necessary.
2581 // If needed, moves the channel from the unjoined list to the joined list.
2582 // Returns the NickInfo for the nickname.
2583 ChannelNickPtr Server::addNickToJoinedChannelsList(const QString& channelName, const QString& nickname)
2584 {
2585     bool doChannelJoinedSignal = false;
2586     bool doWatchedNickChangedSignal = false;
2587     bool doChannelMembersChangedSignal = false;
2588     QString lcNickname(nickname.toLower());
2589     // Create NickInfo if not already created.
2590     NickInfoPtr nickInfo = getNickInfo(nickname);
2591     if (!nickInfo)
2592     {
2593         nickInfo = new NickInfo(nickname, this);
2594         m_allNicks.insert(lcNickname, nickInfo);
2595         doWatchedNickChangedSignal = isWatchedNick(nickname);
2596     }
2597     // if nickinfo already exists update nickname, in case we created the nickinfo based
2598     // on e.g. an incorrectly capitalized ISON request
2599     else
2600         nickInfo->setNickname(nickname);
2601
2602     // Move the channel from unjoined list (if present) to joined list.
2603     QString lcChannelName = channelName.toLower();
2604     ChannelNickMap *channel;
2605     if (m_unjoinedChannels.contains(lcChannelName))
2606     {
2607         channel = m_unjoinedChannels[lcChannelName];
2608         m_unjoinedChannels.remove(lcChannelName);
2609         m_joinedChannels.insert(lcChannelName, channel);
2610         doChannelJoinedSignal = true;
2611     }
2612     else
2613     {
2614         // Create a new list in the joined channels if not already present.
2615         if (!m_joinedChannels.contains(lcChannelName))
2616         {
2617             channel = new ChannelNickMap;
2618             m_joinedChannels.insert(lcChannelName, channel);
2619             doChannelJoinedSignal = true;
2620         }