1 /***************************************************************************
2 * Copyright (C) 2005-08 by the Quassel Project *
3 * devel@quassel-irc.org *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) version 3. *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 ***************************************************************************/
20 #include "networkconnection.h"
22 #include <QMetaObject>
23 #include <QMetaMethod>
28 #include "coresession.h"
30 #include "ircchannel.h"
35 #include "ircserverhandler.h"
36 #include "userinputhandler.h"
37 #include "ctcphandler.h"
39 NetworkConnection::NetworkConnection(Network *network, CoreSession *session) : QObject(network),
40 _connectionState(Network::Disconnected),
42 _coreSession(session),
43 _ircServerHandler(new IrcServerHandler(this)),
44 _userInputHandler(new UserInputHandler(this)),
45 _ctcpHandler(new CtcpHandler(this)),
46 _autoReconnectCount(0)
48 _autoReconnectTimer.setSingleShot(true);
50 // TODO make configurable
51 _whoTimer.setInterval(60 * 1000);
52 _whoTimer.setSingleShot(false);
54 connect(&_autoReconnectTimer, SIGNAL(timeout()), this, SLOT(doAutoReconnect()));
55 connect(&_whoTimer, SIGNAL(timeout()), this, SLOT(sendWho()));
57 connect(network, SIGNAL(currentServerSet(const QString &)), this, SLOT(networkInitialized(const QString &)));
58 connect(network, SIGNAL(useAutoReconnectSet(bool)), this, SLOT(autoReconnectSettingsChanged()));
59 connect(network, SIGNAL(autoReconnectIntervalSet(quint32)), this, SLOT(autoReconnectSettingsChanged()));
60 connect(network, SIGNAL(autoReconnectRetriesSet(quint16)), this, SLOT(autoReconnectSettingsChanged()));
62 connect(&socket, SIGNAL(connected()), this, SLOT(socketConnected()));
63 connect(&socket, SIGNAL(disconnected()), this, SLOT(socketDisconnected()));
64 connect(&socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(socketError(QAbstractSocket::SocketError)));
65 connect(&socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(socketStateChanged(QAbstractSocket::SocketState)));
66 connect(&socket, SIGNAL(readyRead()), this, SLOT(socketHasData()));
68 connect(_ircServerHandler, SIGNAL(nickChanged(const QString &, const QString &)),
69 this, SLOT(nickChanged(const QString &, const QString &)));
72 NetworkConnection::~NetworkConnection() {
73 if(connectionState() != Network::Disconnected && connectionState() != Network::Reconnecting)
75 delete _ircServerHandler;
76 delete _userInputHandler;
80 bool NetworkConnection::isConnected() const {
81 // return socket.state() == QAbstractSocket::ConnectedState;
82 return connectionState() == Network::Initialized;
85 Network::ConnectionState NetworkConnection::connectionState() const {
86 return _connectionState;
89 void NetworkConnection::setConnectionState(Network::ConnectionState state) {
90 _connectionState = state;
91 network()->setConnectionState(state);
92 emit connectionStateChanged(state);
95 NetworkId NetworkConnection::networkId() const {
96 return network()->networkId();
99 QString NetworkConnection::networkName() const {
100 return network()->networkName();
103 Identity *NetworkConnection::identity() const {
104 return coreSession()->identity(network()->identity());
107 Network *NetworkConnection::network() const {
111 CoreSession *NetworkConnection::coreSession() const {
115 IrcServerHandler *NetworkConnection::ircServerHandler() const {
116 return _ircServerHandler;
119 UserInputHandler *NetworkConnection::userInputHandler() const {
120 return _userInputHandler;
123 CtcpHandler *NetworkConnection::ctcpHandler() const {
127 QString NetworkConnection::serverDecode(const QByteArray &string) const {
128 return network()->decodeServerString(string);
131 QString NetworkConnection::channelDecode(const QString &bufferName, const QByteArray &string) const {
132 if(!bufferName.isEmpty()) {
133 IrcChannel *channel = network()->ircChannel(bufferName);
134 if(channel) return channel->decodeString(string);
136 return network()->decodeString(string);
139 QString NetworkConnection::userDecode(const QString &userNick, const QByteArray &string) const {
140 IrcUser *user = network()->ircUser(userNick);
141 if(user) return user->decodeString(string);
142 return network()->decodeString(string);
145 QByteArray NetworkConnection::serverEncode(const QString &string) const {
146 return network()->encodeServerString(string);
149 QByteArray NetworkConnection::channelEncode(const QString &bufferName, const QString &string) const {
150 if(!bufferName.isEmpty()) {
151 IrcChannel *channel = network()->ircChannel(bufferName);
152 if(channel) return channel->encodeString(string);
154 return network()->encodeString(string);
157 QByteArray NetworkConnection::userEncode(const QString &userNick, const QString &string) const {
158 IrcUser *user = network()->ircUser(userNick);
159 if(user) return user->encodeString(string);
160 return network()->encodeString(string);
163 void NetworkConnection::autoReconnectSettingsChanged() {
164 if(!network()->useAutoReconnect()) {
165 _autoReconnectTimer.stop();
166 _autoReconnectCount = 0;
168 _autoReconnectTimer.setInterval(network()->autoReconnectInterval() * 1000);
169 if(_autoReconnectCount != 0) {
170 if(network()->unlimitedReconnectRetries()) _autoReconnectCount = -1;
171 else _autoReconnectCount = network()->autoReconnectRetries();
176 void NetworkConnection::connectToIrc(bool reconnecting) {
177 if(!reconnecting && network()->useAutoReconnect() && _autoReconnectCount == 0) {
178 _autoReconnectTimer.setInterval(network()->autoReconnectInterval() * 1000);
179 if(network()->unlimitedReconnectRetries()) _autoReconnectCount = -1;
180 else _autoReconnectCount = network()->autoReconnectRetries();
182 QVariantList serverList = network()->serverList();
183 Identity *identity = coreSession()->identity(network()->identity());
184 if(!serverList.count()) {
185 qWarning() << "Server list empty, ignoring connect request!";
189 qWarning() << "Invalid identity configures, ignoring connect request!";
192 // TODO implement cycling / random servers
193 QString host = serverList[0].toMap()["Host"].toString();
194 quint16 port = serverList[0].toMap()["Port"].toUInt();
195 displayStatusMsg(tr("Connecting to %1:%2...").arg(host).arg(port));
196 displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("Connecting to %1:%2...").arg(host).arg(port));
197 socket.connectToHost(host, port);
200 void NetworkConnection::networkInitialized(const QString ¤tServer) {
201 if(currentServer.isEmpty()) return;
203 if(network()->useAutoReconnect() && !network()->unlimitedReconnectRetries()) {
204 _autoReconnectCount = network()->autoReconnectRetries(); // reset counter
209 // now we are initialized
210 setConnectionState(Network::Initialized);
211 network()->setConnected(true);
212 emit connected(networkId());
217 void NetworkConnection::sendPerform() {
218 BufferInfo statusBuf = Core::bufferInfo(coreSession()->user(), network()->networkId(), BufferInfo::StatusBuffer);
220 if(network()->useAutoIdentify() && !network()->autoIdentifyService().isEmpty() && !network()->autoIdentifyPassword().isEmpty()) {
221 userInputHandler()->handleMsg(statusBuf, QString("%1 IDENTIFY %2").arg(network()->autoIdentifyService(), network()->autoIdentifyPassword()));
224 foreach(QString line, network()->perform()) {
225 if(!line.isEmpty()) userInput(statusBuf, line);
228 // rejoin channels we've been in
229 QStringList channels, keys;
230 foreach(QString chan, network()->persistentChannels().keys()) {
231 QString key = network()->persistentChannels()[chan];
233 channels.prepend(chan); keys.prepend(key);
235 channels.append(chan);
238 QString joinString = QString("%1 %2").arg(channels.join(",")).arg(keys.join(",")).trimmed();
239 if(!joinString.isEmpty()) userInputHandler()->handleJoin(statusBuf, joinString);
242 void NetworkConnection::disconnectFromIrc() {
243 _autoReconnectTimer.stop();
244 _autoReconnectCount = 0;
245 displayMsg(Message::Server, BufferInfo::StatusBuffer, "", tr("Disconnecting."));
246 if(socket.state() < QAbstractSocket::ConnectedState) {
247 setConnectionState(Network::Disconnected);
248 socketDisconnected();
249 } else socket.disconnectFromHost();
252 void NetworkConnection::socketHasData() {
253 while(socket.canReadLine()) {
254 QByteArray s = socket.readLine().trimmed();
255 ircServerHandler()->handleServerMsg(s);
259 void NetworkConnection::socketError(QAbstractSocket::SocketError) {
260 qDebug() << qPrintable(tr("Could not connect to %1 (%2)").arg(network()->networkName(), socket.errorString()));
261 emit connectionError(socket.errorString());
262 emit displayMsg(Message::Error, BufferInfo::StatusBuffer, "", tr("Connection failure: %1").arg(socket.errorString()));
263 network()->emitConnectionError(socket.errorString());
264 if(socket.state() < QAbstractSocket::ConnectedState) {
265 setConnectionState(Network::Disconnected);
266 socketDisconnected();
268 //qDebug() << "exiting...";
272 void NetworkConnection::socketConnected() {
273 //emit connected(networkId()); initialize first!
274 Identity *identity = coreSession()->identity(network()->identity());
276 qWarning() << "Identity invalid!";
280 putRawLine(serverEncode(QString("NICK :%1").arg(identity->nicks()[0]))); // FIXME: try more nicks if error occurs
281 putRawLine(serverEncode(QString("USER %1 8 * :%2").arg(identity->ident(), identity->realName())));
284 void NetworkConnection::socketStateChanged(QAbstractSocket::SocketState socketState) {
285 Network::ConnectionState state;
286 switch(socketState) {
287 case QAbstractSocket::UnconnectedState:
288 state = Network::Disconnected;
290 case QAbstractSocket::HostLookupState:
291 case QAbstractSocket::ConnectingState:
292 state = Network::Connecting;
294 case QAbstractSocket::ConnectedState:
295 state = Network::Initializing;
297 case QAbstractSocket::ClosingState:
298 state = Network::Disconnecting;
301 state = Network::Disconnected;
303 setConnectionState(state);
306 void NetworkConnection::socketDisconnected() {
308 network()->setConnected(false);
309 emit disconnected(networkId());
310 if(_autoReconnectCount == 0) emit quitRequested(networkId());
312 setConnectionState(Network::Reconnecting);
313 if(_autoReconnectCount == network()->autoReconnectRetries()) doAutoReconnect(); // first try is immediate
314 else _autoReconnectTimer.start();
318 void NetworkConnection::doAutoReconnect() {
319 if(connectionState() != Network::Disconnected && connectionState() != Network::Reconnecting) {
320 qWarning() << "NetworkConnection::doAutoReconnect(): Cannot reconnect while not being disconnected!";
323 if(_autoReconnectCount > 0) _autoReconnectCount--;
327 // FIXME switch to BufferId
328 void NetworkConnection::userInput(BufferInfo buf, QString msg) {
329 userInputHandler()->handleUserInput(buf, msg);
332 void NetworkConnection::putRawLine(QByteArray s) {
337 void NetworkConnection::putCmd(const QString &cmd, const QVariantList ¶ms, const QByteArray &prefix) {
339 if(!prefix.isEmpty())
340 msg += ":" + prefix + " ";
341 msg += cmd.toUpper().toAscii();
343 for(int i = 0; i < params.size() - 1; i++) {
344 msg += " " + params[i].toByteArray();
346 if(!params.isEmpty())
347 msg += " :" + params.last().toByteArray();
352 void NetworkConnection::sendWho() {
353 foreach(QString chan, network()->channels()) {
354 putRawLine("WHO " + serverEncode(chan));
358 void NetworkConnection::addChannelKey(const QString &channel, const QString &key) {
359 if(key.isEmpty()) removeChannelKey(channel);
360 else _channelKeys[channel] = key;
363 void NetworkConnection::removeChannelKey(const QString &channel) {
364 _channelKeys.remove(channel);
367 void NetworkConnection::nickChanged(const QString &newNick, const QString &oldNick) {
368 emit nickChanged(_network->networkId(), newNick, oldNick);
371 /* Exception classes for message handling */
372 NetworkConnection::ParseError::ParseError(QString cmd, QString prefix, QStringList params) {
374 _msg = QString("Command Parse Error: ") + cmd + params.join(" ");
377 NetworkConnection::UnknownCmdError::UnknownCmdError(QString cmd, QString prefix, QStringList params) {
379 _msg = QString("Unknown Command: ") + cmd + params.join(" ");