Completely change the behavior of entering special keys (like Ctrl+Alt+Del). Do not...
[kdenetwork.git] / kopete / protocols / qq / qqnotifysocket.cpp
blobd14d7fdf2d357b3c4361c413d4187ad551fc7ee7
1 /*
2 qqnotifysocket.cpp - Notify Socket for the QQ Protocol
3 forked from msnnotifysocket.cpp
5 Copyright (c) 2006 by Hui Jin <blueangel.jin@gmail.com>
6 Copyright (c) 2002 by Duncan Mac-Vicar Prett <duncan@kde.org>
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 Michaƫl Larouche <larouche@kde.org>
10 Copyright (c) 2005 by Gregg Edghill <gregg.edghill@gmail.com>
12 Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org>
14 Portions taken from
15 KMerlin (c) 2001 by Olaf Lueg <olueg@olsd.de>
17 *************************************************************************
18 * *
19 * This program is free software; you can redistribute it and/or modify *
20 * it under the terms of the GNU General Public License as published by *
21 * the Free Software Foundation; either version 2 of the License, or *
22 * (at your option) any later version. *
23 * *
24 *************************************************************************
27 #include <kdebug.h>
28 #include <QHostAddress>
30 #include "kopetestatusmessage.h"
31 #include "libeva.h"
33 #include "qqnotifysocket.h"
34 #include "qqaccount.h"
36 QQNotifySocket::QQNotifySocket( QQAccount *account, const QString &password )
37 : QQSocket( account )
39 m_account = account;
40 m_newstatus = Kopete::OnlineStatus::Offline;
41 Eva::ByteArray pwd( password.toAscii().data(), password.size() );
42 m_passwordKey = Eva::Packet::QQHash(pwd);
43 pwd.release(); // the data is handled in QT
44 m_loginMode = Eva::NormalLogin;
46 // FIXME: more error-checking.
47 m_qqId = account->accountId().toInt();
49 m_heartbeat = new QTimer(this);
50 QObject::connect( m_heartbeat, SIGNAL(timeout()), SLOT(heartbeat()) );
53 QQNotifySocket::~QQNotifySocket()
55 kDebug(14140) ;
56 if( m_heartbeat->isActive() )
57 m_heartbeat->stop();
59 delete m_heartbeat;
63 void QQNotifySocket::doneConnect()
65 // setup the status first
66 QQSocket::doneConnect();
68 kDebug( 14140 ) << "Negotiating server protocol version";
69 if( m_token.size() )
70 sendPacket( Eva::login( m_qqId, m_id++, m_passwordKey, m_token, m_loginMode ) );
71 else
72 sendPacket( Eva::loginToken(m_qqId, m_id++) );
76 void QQNotifySocket::disconnect()
78 kDebug(14140) << "online status =" <<
79 onlineStatus() << endl;
80 // FIXME: double check the logic, please.
81 if( m_disconnectReason==Kopete::Account::Unknown )
82 m_disconnectReason=Kopete::Account::Manual;
83 // sendGoodbye, shall we setup the status as well ?
84 if( onlineStatus() == Connected )
85 sendGoodbye();
87 // the socket is not connected yet, so I should force the signals
88 if ( onlineStatus() == Disconnected || onlineStatus() == Connecting )
89 emit socketClosed();
90 else
91 QQSocket::disconnect();
94 void QQNotifySocket::handleError( uint code, uint id )
96 kDebug(14140) ;
98 // TODO: Add support for all of these!
99 switch( code )
101 default:
102 QQSocket::handleError( code, id );
103 break;
107 // Core functions
108 void QQNotifySocket::handleIncomingPacket( const QByteArray& rawData )
110 kDebug( 14140 ) << rawData;
111 Eva::Packet packet( rawData.data(), rawData.size() );
112 Eva::ByteArray text;
114 Eva::ByteArray initKey((char*) Eva::Packet::getInitKey(), 16 );
115 initKey.release();
117 kDebug( 14140 ) << "command = " << packet.command();
118 switch( packet.command() )
120 case Eva::Command::RequestLoginToken :
121 text = Eva::Packet::loginToken( packet.body() );
122 break;
124 case Eva::Command::Login :
125 text = Eva::Packet::decrypt( packet.body(), m_passwordKey );
126 if( text.size() == 0 )
127 text = Eva::Packet::decrypt( packet.body(), initKey );
128 break;
130 default:
131 text = Eva::Packet::decrypt( packet.body(), m_sessionKey );
132 if ( text.size() == 0 )
133 text = Eva::Packet::decrypt( packet.body(), m_passwordKey );
136 kDebug( 14140 ) << "text = " << QByteArray( text.c_str(), text.size() );
139 switch( packet.command() )
141 // FIXME: use table-driven pattern ?
142 case Eva::Command::Logout :
143 case Eva::Command::Heartbeat:
144 break;
145 case Eva::Command::UpdateInfo :
146 case Eva::Command::Search :
147 case Eva::Command::UserInfo :
149 // map std::map to QMap
150 std::map<const char*, std::string, Eva::ltstr> dict = Eva::Packet::contactDetail(text);
151 QMap<const char*, QByteArray> qmap;
153 QString id = QString( dict["qqId"].c_str() );
154 std::map<const char*, std::string, Eva::ltstr>::const_iterator it = dict.begin();
156 for( ; it != dict.end(); it++ )
157 qmap.insert( (*it).first, QByteArray((*it).second.c_str() ) );
159 emit contactDetailReceived(id, qmap);
163 break;
164 case Eva::Command::AddBuddy:
165 case Eva::Command::RemoveBuddy:
166 case Eva::Command::AuthInvite :
167 break;
168 case Eva::Command::ChangeStatus :
169 if( Eva::Packet::replyCode(text) == Eva::ChangeStatusOK )
171 kDebug( 14140 ) << "ChangeStatus ok";
172 emit statusChanged( m_newstatus );
174 else // TODO: Debug me.
175 disconnect();
176 break;
178 case Eva::Command::AckSysMsg :
179 case Eva::Command::SendMsg :
180 break;
181 case Eva::Command::ReceiveMsg :
183 Eva::MessageEnvelop envelop(text);
184 kDebug(14140) << "Received message from " << envelop.sender << " to " << envelop.receiver << " type=" << envelop.type;
185 kDebug(14140) << "seq = " << envelop.sequence << " from " << envelop.ip << ":" << envelop.port;
187 sendPacket( Eva::messageReply(m_qqId, packet.sequence(), m_sessionKey, Eva::Packet::replyKey(text) ));
188 Eva::ByteArray body( text.data() + sizeof(envelop), text.size() - sizeof(envelop) );
189 body.release();
191 // TODO: check whether this is a duplicated message
192 switch( envelop.type )
194 case 0x0010:
195 kDebug(14140) << "command 0x0010: " << QByteArray( body.c_str(), body.size() );
196 break;
197 case Eva::RcvFromBuddy:
199 Eva::MessageHeader mh(body);
200 kDebug(14140) << "message header:";
201 kDebug(14140) << "ver:" << mh.version << " sender:" << mh.sender
202 << " receiver:" << mh.receiver
203 << " type:" << mh.type << " seq:" << mh.sequence
204 << " timestamp:" << mh.timestamp << " avatar:" << mh.avatar
205 << endl;
207 if( mh.receiver != m_qqId )
209 kDebug(14140) << "receive other(" << mh.receiver <<")'s message";
210 break;
213 // FIXME: replace the magic number!
214 // FIXME: the code stinks!
215 Eva::uchar* p = body.data()+36;
216 bool hasFontStyle = p[3] != 0;
217 Eva::uchar replyType = p[8];
219 // clear compiler warnings
220 Q_UNUSED(hasFontStyle);
221 Q_UNUSED(replyType);
223 Eva::ByteArray msg(body.size());
224 p += 9;
226 while( *p )
227 msg += *p++;
228 msg += char(0x0);
230 kDebug(14140) << "message received: " << msg.data();
231 // FIXME: use a function to generate guid!
232 emit messageReceived(mh, msg);
234 break;
236 default:
237 break;
239 break;
242 case Eva::Command::RemoveMe :
243 break;
245 case Eva::Command::RequestKey :
247 char type = text.data()[0];
248 char reply = text.data()[1];
250 if( reply == Eva::RequestKeyOK )
252 // NOTE: the type of the key supports TransferKey only.
253 if( type == Eva::TransferKey )
255 m_transferKey = Eva::Packet::transferKey( text );
256 m_transferToken = Eva::Packet::transferToken( text );
257 kDebug( 14140 ) << "transferKey =" << QByteArray( m_transferKey.c_str(), m_transferKey.size());
258 kDebug( 14140 ) << "transferToken =" << QByteArray( m_transferToken.c_str(), m_transferToken.size());
262 break;
265 case Eva::Command::GetCell :
266 break;
268 case Eva::Command::Login :
269 switch( Eva::Packet::replyCode(text) )
271 case Eva::LoginOK:
272 kDebug( 14140 ) << "Bingo! QQ:#" << m_qqId << " logged in!";
273 // show off some meta data :
274 m_sessionKey = Eva::Packet::sessionKey(text);
275 kDebug( 14140 ) << "sessionKey = " <<
276 QByteArray( m_sessionKey.c_str(), m_sessionKey.size() ) << endl;
278 kDebug( 14140 ) << "remote IP: " << QHostAddress( Eva::Packet::remoteIP(text) ).toString();
279 kDebug( 14140 ) << "remote port: " << Eva::Packet::remotePort(text);
280 kDebug( 14140 ) << "local IP: " << QHostAddress( Eva::Packet::localIP(text) ).toString();
281 kDebug( 14140 ) << "local port: " << Eva::Packet::localPort(text);
282 kDebug( 14140 ) << "login time: " << Eva::Packet::loginTime(text);
283 kDebug( 14140 ) << "last login from: " << QHostAddress( Eva::Packet::lastLoginFrom(text) ).toString();
284 kDebug( 14140 ) << "last login time: " << Eva::Packet::lastLoginTime(text);
286 // start the heartbeat
287 if( !m_heartbeat->isActive() )
289 m_heartbeat->setSingleShot(false);
290 m_heartbeat->start(60000);
293 // FIXME: refactor me!
294 emit newContactList();
295 // FIXME: We might login in as invisible as well.
296 m_newstatus = Kopete::OnlineStatus::Online;
297 sendPacket( Eva::statusUpdate( m_qqId, m_id++, m_sessionKey, Eva::Online) );
298 sendPacket( Eva::transferKey( m_qqId, m_id++, m_sessionKey) );
300 // get the meta data for myself
301 contactDetail(m_qqId);
303 // fetch the online contacts
304 sendListOnlineContacts();
308 break;
310 case Eva::LoginRedirect :
311 kDebug( 14140 ) << "Redirect to "
312 << QHostAddress(Eva::Packet::redirectedIP(text)).toString()
313 << " : " << Eva::Packet::redirectedPort(text) << endl;
314 disconnect();
315 connect( QHostAddress( Eva::Packet::redirectedIP(text) ).toString(), Eva::Packet::redirectedPort(text) );
316 break;
318 case Eva::LoginWrongPassword :
319 kDebug( 14140 ) << "password is wrong. ";
320 break;
322 case Eva::LoginMiscError :
323 kDebug( 14140 ) << "unknown error. ";
324 break;
326 default:
327 kDebug( 14140 ) << "Bad, we are not supposed to be here !";
328 break;
331 break;
333 case Eva::Command::AllContacts:
336 len = 2;
337 while( len < text.size() )
338 emit contactList( Eva::contactInfo( text.data(), len ) );
339 short pos = ntohs( Eva::type_cast<short> (text.data()) );
341 if( pos != Eva::ContactListEnd )
342 sendPacket( Eva::allContacts( m_qqId, m_id++, m_sessionKey, pos ) );
345 break;
346 case Eva::Command::ContactsOnline :
348 break;
349 case Eva::Command::GetCell2 :
350 case Eva::Command::SIP :
351 case Eva::Command::Test :
352 break;
353 case Eva::Command::GroupNames :
354 groupNames( text );
355 break;
357 case Eva::Command::UploadGroups :
358 case Eva::Command::Memo :
359 break;
360 case Eva::Command::DownloadGroups :
361 groupInfos( text );
362 break;
364 case Eva::Command::GetLevel :
365 break;
367 case Eva::Command::RequestLoginToken :
368 m_token = text;
369 kDebug( 14140 ) << "command = " << packet.command() << ": token = " <<
370 QByteArray ( m_token.c_str(), m_token.size() ) << endl;
371 sendPacket( Eva::login( m_qqId, m_id++, m_passwordKey, m_token, m_loginMode ) );
372 break;
374 case Eva::Command::ExtraInfo :
375 case Eva::Command::Signature :
376 case Eva::Command::ReceiveSysMsg :
377 break;
378 case Eva::Command::ContactStausChanged :
380 kDebug( 14140 ) << "contact status signal";
381 Eva::ContactStatus cs(text.data());
382 kDebug( 14140 ) << "contact status detail:";
383 kDebug( 14140 ) << "id = " << cs.qqId << " status = " << cs.status;
384 emit contactStatusChanged( cs );
385 break;
387 default:
388 break;
394 void QQNotifySocket::contactDetail(Eva::uint qqId)
396 sendPacket( Eva::contactDetail( m_qqId, m_id++, m_sessionKey, qqId) );
399 void QQNotifySocket::sendTextMessage( const uint toId, const QByteArray& message )
401 // Translate the message to Eva::ByteArray
402 // TODO: color and font
403 kDebug( 14140 ) << "Send the message: " << message << " from " << m_qqId << " to " << toId;
404 // attach the ByteArray to QString:
405 // FIXME: Add an adapter to ByteArray
406 Eva::ByteArray text( (char*)message.data(), message.size() );
407 text.release();
409 Eva::ByteArray packet = Eva::textMessage(m_qqId, m_id++, m_sessionKey, toId, m_transferKey, text );
410 QQSocket::sendPacket( QByteArray( packet.c_str(), packet.size()) );
414 void QQNotifySocket::heartbeat()
416 sendPacket( Eva::heartbeat( m_qqId, m_id++, m_sessionKey ));
419 void QQNotifySocket::sendListOnlineContacts(uint pos)
421 sendPacket( Eva::onlineContacts( m_qqId, m_id++, m_sessionKey, pos) );
424 void QQNotifySocket::groupNames( const Eva::ByteArray& text )
426 QStringList ql;
427 std::list< std::string > l = Eva::Packet::groupNames( text );
428 for( std::list<std::string>::const_iterator it = l.begin(); it != l.end(); it++ )
429 ql.append( QString( (*it).c_str() ) );
431 kDebug(14140) ;
432 emit groupNames( ql );
435 void QQNotifySocket::groupInfos( const Eva::ByteArray& text )
437 kDebug(14140) ;
438 std::list< Eva::GroupInfo > gis = Eva::Packet::groupInfos( text );
439 // TODO: send it one by one.
440 for( std::list< Eva::GroupInfo >::const_iterator it = gis.begin();
441 it != gis.end(); it++ )
443 kDebug(14140) << "buddy: qqId = " << (*it).qqId << " type = " << (*it).type
444 << " groupId = " << (*it).groupId << endl;
445 emit contactInGroup( (*it).qqId, (*it).type, (*it).groupId );
448 int next = Eva::Packet::nextGroupId( text );
449 if( next )
450 sendDownloadGroups( next );
453 void QQNotifySocket::doGetContactStatuses( const Eva::ByteArray& text )
455 kDebug(14140) ;
456 Eva::uchar pos = Eva::ContactListBegin;
457 std::list< Eva::ContactStatus > css = Eva::Packet::onlineContacts( text, pos );
458 for( std::list< Eva::ContactStatus >::const_iterator it = css.begin();
459 it != css.end(); it++ )
461 kDebug(14140) << "buddy: qqId = " << (*it).qqId << " status = " << (*it).status;
462 emit contactStatusChanged(*it);
465 if( pos != 0xff )
466 sendListOnlineContacts(pos);
469 #include "qqnotifysocket.moc"
470 // vim: set noet ts=4 sts=4 sw=4: