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 | |
43 | using namespace KNetwork; |
44 | |
45 | QQSocket::QQSocket(QObject* parent) : QObject (parent) |
46 | { |
47 | m_onlineStatus = Disconnected; |
48 | m_socket = 0L; |
49 | } |
50 | |
51 | QQSocket::~QQSocket() |
52 | { |
53 | doneDisconnect(); |
54 | if ( m_socket ) |
55 | m_socket->deleteLater(); |
56 | } |
57 | |
58 | void 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 | |
98 | void 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 | |
110 | void QQSocket::aboutToConnect() |
111 | { |
112 | /* Empty default implementation */ |
113 | } |
114 | |
115 | void QQSocket::doneConnect() |
116 | { |
117 | setOnlineStatus( Connected ); |
118 | } |
119 | |
120 | void QQSocket::doneDisconnect() |
121 | { |
122 | kDebug( 14140 ) << "disconnected done" ; |
123 | setOnlineStatus( Disconnected ); |
124 | } |
125 | |
126 | void 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 | |
137 | void QQSocket::sendPacket( const QByteArray& data ) |
138 | { |
139 | kDebug(14140) << data; |
140 | m_sendQueue.append( data ); |
141 | m_socket->enableWrite(true); |
142 | } |
143 | |
144 | void 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 | |
171 | void 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 | |
225 | void 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 | |
246 | void 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 | |
268 | void QQSocket::slotConnectionSuccess() |
269 | { |
270 | kDebug ( 14140 ) << "slotConnectionSuccess: calling doneConnect()" ; |
271 | doneConnect(); |
272 | } |
273 | |
274 | void QQSocket::slotHostFound() |
275 | { |
276 | // nothing to do |
277 | } |
278 | |
279 | void 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 | |
297 | QString 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 | |