1/*
2
3 qqsocket.cpp - Base class for the sockets used in QQ
4 forked from msnsocket.cpp
5
6 Copyright (c) 2006 Hui Jin <blueangel.jin@gmail.com>
7 Copyright (c) 2002-2003 by Martijn Klingens <klingens@kde.org>
8 Copyright (c) 2002-2005 by Olivier Goffart <ogoffart at kde.org>
9 Copyright (c) 2005 by Gregg Edghill <gregg.edghill@gmail.com>
10
11 Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
12
13 Portions of this code are taken from KMerlin,
14 (c) 2001 by Olaf Lueg <olueg@olsd.de>
15
16 *************************************************************************
17 * *
18 * This program is free software; you can redistribute it and/or modify *
19 * it under the terms of the GNU General Public License as published by *
20 * the Free Software Foundation; either version 2 of the License, or *
21 * (at your option) any later version. *
22 * *
23 *************************************************************************
24*/
25
26#include "qqsocket.h"
27#include "qqprotocol.h"
28
29#include <QTimer>
30#include <QByteArray>
31
32#include <kdebug.h>
33#include <kconfig.h>
34#include <k3bufferedsocket.h>
35#include <k3serversocket.h>
36#include <k3resolver.h>
37#include <klocale.h>
38#include <kmessagebox.h>
39#include <kurl.h>
40
41#include "kopeteuiglobal.h"
42
43using namespace KNetwork;
44
45QQSocket::QQSocket(QObject* parent) : QObject (parent)
46{
47 m_onlineStatus = Disconnected;
48 m_socket = 0L;
49}
50
51QQSocket::~QQSocket()
52{
53 doneDisconnect();
54 if ( m_socket )
55 m_socket->deleteLater();
56}
57
58void QQSocket::connect( const QString &server, uint port )
59{
60 if ( m_onlineStatus == Connected || m_onlineStatus == Connecting )
61 {
62 kWarning( 14140 ) << "Already connected or connecting! Not connecting again.";
63 return;
64 }
65
66 if( m_onlineStatus == Disconnecting )
67 {
68 // Cleanup first.
69 // FIXME: More generic!!!
70 kWarning( 14140 ) << "We're still disconnecting! Deleting socket the hard way first.";
71 delete m_socket;
72 }
73 setOnlineStatus( Connecting );
74 m_id = 5; // FIXME:Don't use the magic #, use random number instead.
75 m_server = server;
76 m_port = port;
77 kDebug( 14140 ) << "connecting to :" << server << ":" << port;
78 m_socket = new KBufferedSocket( server, QString::number(port) );
79 m_socket->enableRead( true );
80
81 // enableWrite eats the CPU, and we only need it when the queue is
82 // non-empty, so disable it until we have actual data in the queue
83 m_socket->enableWrite( false );
84
85 QObject::connect( m_socket, SIGNAL(readyRead()), this, SLOT(slotDataReceived()) );
86 QObject::connect( m_socket, SIGNAL(readyWrite()), this, SLOT(slotReadyWrite()) );
87 QObject::connect( m_socket, SIGNAL(hostFound()), this, SLOT(slotHostFound()) );
88 QObject::connect( m_socket, SIGNAL(connected(KNetwork::KResolverEntry)), this, SLOT(slotConnectionSuccess()) );
89 QObject::connect( m_socket, SIGNAL(gotError(int)), this, SLOT(slotSocketError(int)) );
90 QObject::connect( m_socket, SIGNAL(closed()), this, SLOT(slotSocketClosed()) );
91
92 aboutToConnect();
93
94 // start the asynchronous connection
95 m_socket->connect();
96}
97
98void QQSocket::disconnect()
99{
100 kDebug(14140) ;
101 if ( m_socket )
102 {
103 m_socket->closeNow();
104 setOnlineStatus( Disconnecting );
105 }
106 else
107 slotSocketClosed();
108}
109
110void QQSocket::aboutToConnect()
111{
112 /* Empty default implementation */
113}
114
115void QQSocket::doneConnect()
116{
117 setOnlineStatus( Connected );
118}
119
120void QQSocket::doneDisconnect()
121{
122 kDebug( 14140 ) << "disconnected done";
123 setOnlineStatus( Disconnected );
124}
125
126void QQSocket::setOnlineStatus( QQSocket::OnlineStatus status )
127{
128 if ( m_onlineStatus == status )
129 return;
130
131 m_onlineStatus = status;
132 kDebug( 14140 ) << ": status = " << m_onlineStatus;
133 emit onlineStatusChanged( status );
134}
135
136
137void QQSocket::sendPacket( const QByteArray& data )
138{
139 kDebug(14140) << data;
140 m_sendQueue.append( data );
141 m_socket->enableWrite(true);
142}
143
144void QQSocket::slotSocketError( int error )
145{
146 kWarning( 14140 ) << "Error: " << error << " (" << m_socket->errorString() << ")";
147
148 if(!KSocketBase::isFatalError(error))
149 return;
150 //we only care about fatal error
151
152 QString errormsg = i18n( "There was an error while connecting to the QQ server.\nError message:\n" );
153 if ( error == KSocketBase::LookupFailure )
154 errormsg += i18n( "Unable to lookup %1", m_socket->peerResolver().nodeName() );
155 else
156 errormsg += m_socket->errorString() ;
157
158 //delete m_socket;
159 m_socket->deleteLater();
160 m_socket = 0L;
161
162 setOnlineStatus( Disconnected );
163 emit connectionFailed();
164 //like if the socket is closed
165 emit socketClosed();
166
167 //KMessageBox::queuedMessageBox( Kopete::UI::Global::mainWidget(), KMessageBox::Error, errormsg, i18n( "QQ Plugin" ) );
168 emit errorMessage( ErrorNormal, errormsg );
169}
170
171void QQSocket::slotDataReceived()
172{
173 kDebug(14140) << "DATA RECEIVED! ";
174 int avail = m_socket->bytesAvailable();
175 if ( avail < 0 )
176 {
177 // error!
178 kWarning( 14140 ) << "bytesAvailable() returned " << avail
179 << ". This should not happen!" << endl
180 << "Are we disconnected? Backtrace:" << endl << kBacktrace() << endl;
181 return;
182 }
183
184 char *buffer = new char[ avail + 1 ];
185 int ret = m_socket->read( buffer, avail );
186
187 if ( ret < 0 )
188 {
189 kWarning( 14140 ) << "read() returned " << ret << "!";
190 }
191 else if ( ret == 0 )
192 {
193 kWarning( 14140 ) << "read() returned no data!";
194 }
195 else
196 {
197 if ( avail )
198 {
199 if ( ret != avail)
200 {
201 kWarning( 14140 ) << avail << " bytes were reported available, "
202 << "but read() returned only " << ret << " bytes! Proceeding anyway." << endl;
203 }
204 }
205 else
206 {
207 kDebug( 14140 ) << "Read " << ret << " bytes into 4kb block.";
208 }
209
210 // FIXME: Here we assume that the packet is fetched in one shot,
211 // and no producer/consumer race condition, is this true ?
212 // TODO: memory overhead, use a smart pointer here later.
213 QByteArray buf(buffer, ret );
214
215 // FIXME: do we need a incoming message pool right now ?
216
217 handleIncomingPacket(buf);
218 }
219
220 // Cleanup.
221 delete[] buffer;
222}
223
224
225void QQSocket::handleError( uint code, uint /* id */ )
226{
227 kDebug(14140) ;
228 QString msg;
229
230 switch ( code )
231 {
232 default:
233 // FIXME: if the error causes a disconnect, it will crash, but we can't disconnect every time
234 msg = i18n( "Unhandled QQ error code %1 \n"
235 "Please file a bug report with a detailed description and, if possible, the last console debug output.", code );
236 break;
237 }
238
239 if ( !msg.isEmpty() )
240 //KMessageBox::queuedMessageBox( Kopete::UI::Global::mainWidget(), KMessageBox::Error, msg, i18n( "QQ Plugin" ) );
241 emit errorMessage( ErrorNormal, msg );
242
243 return;
244}
245
246void QQSocket::slotReadyWrite()
247{
248 kDebug(14140) ;
249 if ( !m_sendQueue.isEmpty() )
250 {
251 // If the command queue is not empty, retrieve the first command.
252 QList<QByteArray>::Iterator it = m_sendQueue.begin();
253
254 // Otherwise, send the command normally.
255 m_socket->write( *it, ( *it ).size() );
256 m_sendQueue.erase( it );
257
258 // If the queue is empty agalin stop waiting for readyWrite signals
259 // because of the CPU usage
260 if ( m_sendQueue.isEmpty() )
261 m_socket->enableWrite( false );
262 }
263 else
264 m_socket->enableWrite( false );
265}
266
267
268void QQSocket::slotConnectionSuccess()
269{
270 kDebug ( 14140 ) << "slotConnectionSuccess: calling doneConnect()";
271 doneConnect();
272}
273
274void QQSocket::slotHostFound()
275{
276 // nothing to do
277}
278
279void QQSocket::slotSocketClosed()
280{
281 kDebug( 14140 ) << "Socket closed. ";
282
283 if ( !m_socket || m_onlineStatus == Disconnected )
284 {
285 kDebug( 14140 ) << "Socket already deleted or already disconnected";
286 return;
287 }
288
289 doneDisconnect();
290
291 m_socket->deleteLater();
292 m_socket = 0L;
293
294 emit socketClosed();
295}
296
297QString QQSocket::getLocalIP()
298{
299 if ( !m_socket )
300 return QString();
301
302 const KSocketAddress address = m_socket->localAddress();
303
304 QString ip = address.nodeName();
305
306 kDebug( 14140 ) << "IP: " << ip;
307 return ip;
308}
309
310#include "qqsocket.moc"
311
312// vim: set noet ts=4 sts=4 sw=4:
313
314